/**
 * Quadtree that spatially sorts a particle array in-place at build time.
 * Stores only leaf index ranges [start, end) into the sorted array.
 * NO per-frame methods — this is purely a build-time spatial index.
 */

export interface Leaf {
  start: number;
  end: number;
  minX: number;
  minY: number;
  maxX: number;
  maxY: number;
}

/**
 * Spatially sort particles using a quadtree and return
 * the sorted array + leaf ranges.
 *
 * Takes raw Float32Arrays (no object allocation).
 * Uses in-place partitioning (zero temporary arrays).
 */
export function buildSpatialIndex(
  xs: Float32Array,
  ys: Float32Array
): { sorted: Float32Array; leaves: Leaf[]; order: Uint32Array } {
  const n = xs.length;
  const order = new Uint32Array(n);
  for (let i = 0; i < n; i++) order[i] = i;

  // Find bounds
  let minX = Infinity,
    minY = Infinity,
    maxX = -Infinity,
    maxY = -Infinity;
  for (let i = 0; i < n; i++) {
    const x = xs[i], y = ys[i];
    if (x < minX) minX = x;
    if (y < minY) minY = y;
    if (x > maxX) maxX = x;
    if (y > maxY) maxY = y;
  }

  const leaves: Leaf[] = [];

  // In-place quicksort-style partitioning
  function partition(
    vals: Float32Array,
    start: number,
    end: number,
    mid: number
  ): number {
    let lo = start,
      hi = end - 1;
    while (lo <= hi) {
      while (lo <= hi && vals[order[lo]] < mid) lo++;
      while (lo <= hi && vals[order[hi]] >= mid) hi--;
      if (lo < hi) {
        const t = order[lo];
        order[lo] = order[hi];
        order[hi] = t;
        lo++;
        hi--;
      }
    }
    return lo;
  }

  function build(
    start: number,
    end: number,
    bMinX: number,
    bMinY: number,
    bMaxX: number,
    bMaxY: number,
    depth: number
  ): void {
    const count = end - start;
    if (count <= 0) return;

    // Leaf: stop subdividing
    if (count <= 4096 || depth >= 12) {
      leaves.push({ start, end, minX: bMinX, minY: bMinY, maxX: bMaxX, maxY: bMaxY });
      return;
    }

    const midX = (bMinX + bMaxX) / 2;
    const midY = (bMinY + bMaxY) / 2;

    // Partition by X, then each half by Y
    const splitX = partition(xs, start, end, midX);
    const splitLeftY = partition(ys, start, splitX, midY);
    const splitRightY = partition(ys, splitX, end, midY);

    // BL, TL, BR, TR
    build(start, splitLeftY, bMinX, bMinY, midX, midY, depth + 1);
    build(splitLeftY, splitX, bMinX, midY, midX, bMaxY, depth + 1);
    build(splitX, splitRightY, midX, bMinY, bMaxX, midY, depth + 1);
    build(splitRightY, end, midX, midY, bMaxX, bMaxY, depth + 1);
  }

  build(0, n, minX, minY, maxX, maxY, 0);

  // Reorder particles to match tree layout
  const sorted = new Float32Array(n * 2);
  for (let i = 0; i < n; i++) {
    const src = order[i];
    sorted[i * 2] = xs[src];
    sorted[i * 2 + 1] = ys[src];
  }

  return { sorted, leaves, order };
}
