/*

Download this file and save to your Obsidian Vault including the first line, or open it in “Raw” and copy the entire contents to Obsidian.

This script arranges selected elements and groups with a fixed inner distance.

Tips: You can use the Box Selected Elements and Dimensions scripts to create rectangles of the desired size, then use the Change shape of selected elements script to convert the rectangles to ellipses, and then use the Fixed inner distance script regains a desired inner distance.

Inspiration: #394

See documentation for more details: https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html

*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
  new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
  return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Default distance"]) {
	settings = {
	  "Prompt for distance?": true,
	  "Default distance" : {
		value: 10,
		description: "Fixed horizontal distance between centers"
	  },
	  "Remember last distance?": false
	};
	ea.setScriptSettings(settings);
}
 
let distanceStr = settings["Default distance"].value.toString();
const rememberLastDistance = settings["Remember last distance?"];
 
if(settings["Prompt for distance?"]) {
    distanceStr = await utils.inputPrompt("distance?","number",distanceStr);
}
 
const borders = ["top", "bottom", "left", "right"];
const fromBorder = await utils.suggester(borders, borders, "from border?");
 
if(!fromBorder) {
  return;
}
 
const distance = parseInt(distanceStr);
if(isNaN(distance)) {
  return;
}
if(rememberLastDistance) {
	settings["Default distance"].value = distance;
	ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
    .filter(els => !(els.length === 1 && els[0].type ==="arrow")) // ignore individual arrows
    .filter(els => !(els.length === 1 && (els[0].containerId))); // ignore text in stickynote
 
if(topGroups.length <= 1) {
  new Notice("At least 2 or more elements or groups should be selected.");
  return;
}
 
if(fromBorder === 'top') {
  const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.y)) - Math.min(...rha.map(t => t.y)));
  const firstGroupTop = Math.min(...groups[0].map(el => el.y));
  
  for(var i=0; i<groups.length; i++) {
    if(i > 0) {
        const curGroup = groups[i];
        const moveDistance = distance * i;
        for(const curEl of curGroup) {
          curEl.y = firstGroupTop + moveDistance;
        }
    }
  }   
}
else if(fromBorder === 'bottom') {
  const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.y + t.height)) - Math.min(...rha.map(t => t.y + t.height))).reverse();
  const firstGroupBottom = Math.max(...groups[0].map(el => el.y + el.height));
  
  for(var i=0; i<groups.length; i++) {
    if(i > 0) {
        const curGroup = groups[i];
        const moveDistance = distance * i;
        for(const curEl of curGroup) {
           curEl.y = firstGroupBottom - moveDistance - curEl.height;
        }
    }
  }   
}
else if(fromBorder === 'left') {
  const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.x)) - Math.min(...rha.map(t => t.x)));
  const firstGroupLeft = Math.min(...groups[0].map(el => el.x));
  
  for(var i=0; i<groups.length; i++) {
    if(i > 0) {
        const curGroup = groups[i];
        const moveDistance = distance * i;
        for(const curEl of curGroup) {
          curEl.x = firstGroupLeft + moveDistance;
        }
    }
  }   
}
else if(fromBorder === 'right') {
  const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.x + t.width)) - Math.min(...rha.map(t => t.x + t.width))).reverse();
  const firstGroupRight = Math.max(...groups[0].map(el => el.x + el.width));
  
  for(var i=0; i<groups.length; i++) {
    if(i > 0) {
        const curGroup = groups[i];
        const moveDistance = distance * i;
        for(const curEl of curGroup) {
           curEl.x = firstGroupRight - moveDistance - curEl.width;
        }
    }
  }   
}
 
ea.copyViewElementsToEAforEditing(elements);
await ea.addElementsToView(false, false);