/**
 * LANE-UI — figuring out which unit the player clicked.
 *
 * `bus.pickTile` (contracts v2) turns a client point into a tile; tile→entity resolution
 * below is ours. Together they are click-to-inspect.
 */
import type { Dir, MachineDef, SimSnapshot, Vec2 } from '../contracts';

/**
 * Flattened copy of the entities in a snapshot: exactly the fields the UI needs between
 * frames (hit-testing a click, naming a jammed unit in a toast).
 *
 * CONTRACTS: "Sim may reuse internal buffers between snapshots; consumers must not hold
 * references across frames." Pointer and key handlers fire *between* frames, so the UI
 * keeps this copy instead of the snapshot. The backing array is reused — a 500-entity
 * factory must not allocate 500 objects every frame just to be polite.
 */
export interface RosterEntry {
  id: number;
  def: string;
  x: number;
  y: number;
  dir: Dir;
}

export interface Roster {
  entries: RosterEntry[];
  length: number;
}

export function createRoster(): Roster {
  return { entries: [], length: 0 };
}

export function syncRoster(roster: Roster, snap: SimSnapshot): void {
  const n = snap.entities.length;
  for (let i = 0; i < n; i++) {
    const e = snap.entities[i];
    let r = roster.entries[i];
    if (!r) {
      r = { id: 0, def: '', x: 0, y: 0, dir: 0 };
      roster.entries[i] = r;
    }
    r.id = e.id;
    r.def = e.def;
    r.x = e.pos.x;
    r.y = e.pos.y;
    r.dir = e.dir;
  }
  roster.length = n;
}

/**
 * Footprint in world tiles after rotation. CONTRACTS: `Dir` 0..3 = N,E,S,W clockwise,
 * `pos` is the origin corner (min x, min y), so E/W rotations swap width and height.
 */
export function footprintOf(def: MachineDef, dir: Dir): Vec2 {
  return dir === 1 || dir === 3
    ? { x: def.footprint.y, y: def.footprint.x }
    : { x: def.footprint.x, y: def.footprint.y };
}

/** Entity id under `tile`, or null. */
export function entityAt(
  roster: Roster,
  machines: Map<string, MachineDef>,
  tile: Vec2,
): number | null {
  // Reverse order: later-placed units sit on top, which is what the eye expects.
  for (let i = roster.length - 1; i >= 0; i--) {
    const r = roster.entries[i];
    const def = machines.get(r.def);
    if (!def) continue;
    const fp = footprintOf(def, r.dir);
    if (tile.x >= r.x && tile.x < r.x + fp.x && tile.y >= r.y && tile.y < r.y + fp.y) {
      return r.id;
    }
  }
  return null;
}
