/**
 * Pure alignment functions.
 *
 * Every function accepts a set of bounding rectangles and returns new (x, y)
 * positions.  The caller is responsible for dispatching updateObject calls.
 */

export interface BoundingRect {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
}

export interface PositionUpdate {
  id: string;
  x: number;
  y: number;
}

// -- Align to group edge / center --------------------------------------------

export function alignLeft(items: BoundingRect[]): PositionUpdate[] {
  const minX = Math.min(...items.map((i) => i.x));
  return items.map((i) => ({ id: i.id, x: minX, y: i.y }));
}

export function alignCenter(items: BoundingRect[]): PositionUpdate[] {
  const centers = items.map((i) => i.x + i.width / 2);
  const avgCenter = centers.reduce((a, b) => a + b, 0) / centers.length;
  return items.map((i) => ({ id: i.id, x: avgCenter - i.width / 2, y: i.y }));
}

export function alignRight(items: BoundingRect[]): PositionUpdate[] {
  const maxRight = Math.max(...items.map((i) => i.x + i.width));
  return items.map((i) => ({ id: i.id, x: maxRight - i.width, y: i.y }));
}

export function alignTop(items: BoundingRect[]): PositionUpdate[] {
  const minY = Math.min(...items.map((i) => i.y));
  return items.map((i) => ({ id: i.id, x: i.x, y: minY }));
}

export function alignMiddle(items: BoundingRect[]): PositionUpdate[] {
  const middles = items.map((i) => i.y + i.height / 2);
  const avgMiddle = middles.reduce((a, b) => a + b, 0) / middles.length;
  return items.map((i) => ({ id: i.id, x: i.x, y: avgMiddle - i.height / 2 }));
}

export function alignBottom(items: BoundingRect[]): PositionUpdate[] {
  const maxBottom = Math.max(...items.map((i) => i.y + i.height));
  return items.map((i) => ({ id: i.id, x: i.x, y: maxBottom - i.height }));
}

// -- Distribute evenly -------------------------------------------------------

export function distributeHorizontal(items: BoundingRect[]): PositionUpdate[] {
  if (items.length < 3) return items.map((i) => ({ id: i.id, x: i.x, y: i.y }));
  const sorted = [...items].sort((a, b) => a.x - b.x);
  const first = sorted[0];
  const last = sorted[sorted.length - 1];
  const totalWidth = sorted.reduce((s, i) => s + i.width, 0);
  const gap =
    (last.x + last.width - first.x - totalWidth) / (sorted.length - 1);
  let cursor = first.x;
  return sorted.map((item) => {
    const pos = { id: item.id, x: cursor, y: item.y };
    cursor += item.width + gap;
    return pos;
  });
}

export function distributeVertical(items: BoundingRect[]): PositionUpdate[] {
  if (items.length < 3) return items.map((i) => ({ id: i.id, x: i.x, y: i.y }));
  const sorted = [...items].sort((a, b) => a.y - b.y);
  const first = sorted[0];
  const last = sorted[sorted.length - 1];
  const totalHeight = sorted.reduce((s, i) => s + i.height, 0);
  const gap =
    (last.y + last.height - first.y - totalHeight) / (sorted.length - 1);
  let cursor = first.y;
  return sorted.map((item) => {
    const pos = { id: item.id, x: item.x, y: cursor };
    cursor += item.height + gap;
    return pos;
  });
}

// -- Center on artboard ------------------------------------------------------

export function centerOnArtboard(
  items: BoundingRect[],
  artboardWidth: number,
  artboardHeight: number,
): PositionUpdate[] {
  if (items.length === 0) return [];

  // Find bounding box of all items
  const minX = Math.min(...items.map((i) => i.x));
  const minY = Math.min(...items.map((i) => i.y));
  const maxX = Math.max(...items.map((i) => i.x + i.width));
  const maxY = Math.max(...items.map((i) => i.y + i.height));

  const groupW = maxX - minX;
  const groupH = maxY - minY;

  const offsetX = (artboardWidth - groupW) / 2 - minX;
  const offsetY = (artboardHeight - groupH) / 2 - minY;

  return items.map((i) => ({
    id: i.id,
    x: i.x + offsetX,
    y: i.y + offsetY,
  }));
}
