import type { Catalog, Config, Item, Section } from './types.ts';

/** Hauteur utile interieure d'une colonne. */
export function innerHeightMm(cfg: Config, cat: Catalog): number {
  return cfg.carcass.heightMm - cfg.carcass.plinthMm - 2 * cat.structural.panelThicknessMm;
}

/** Hauteur du corps (hors socle). */
export function bodyHeightMm(cfg: Config): number {
  return cfg.carcass.heightMm - cfg.carcass.plinthMm;
}

/** Hauteur occupee par un element : fixe pour un PLANE, variable pour un BLOCK. */
export function itemHeightMm(item: Item, cat: Catalog): number {
  const t = cat.componentTypes[item.type];
  if (!t) throw new Error(`Type d'element inconnu : ${item.type}`);
  return t.kind === 'PLANE' ? t.defaultHeightMm : (item.heightMm ?? t.defaultHeightMm);
}

/** Elements tries du bas vers le haut. */
export function sortedItems(section: Section, cat: Catalog): Item[] {
  return [...section.items].sort((a, b) => a.yMm - b.yMm);
}

/** Haut de l'element situe immediatement en dessous (0 si aucun). */
export function floorBelowMm(section: Section, item: Item, cat: Catalog): number {
  let floor = 0;
  for (const o of section.items) {
    if (o === item) continue;
    const top = o.yMm + itemHeightMm(o, cat);
    if (top <= item.yMm && top > floor) floor = top;
  }
  return floor;
}

/** Bas de l'element situe immediatement au-dessus (hauteur utile si aucun). */
export function ceilingAboveMm(section: Section, item: Item, cat: Catalog, cfg: Config): number {
  let ceiling = innerHeightMm(cfg, cat);
  const top = item.yMm + itemHeightMm(item, cat);
  for (const o of section.items) {
    if (o === item) continue;
    if (o.yMm >= top && o.yMm < ceiling) ceiling = o.yMm;
  }
  return ceiling;
}

/** Espace libre au-dessus d'un element. */
export function gapAboveMm(section: Section, item: Item, cat: Catalog, cfg: Config): number {
  return Math.max(0, ceilingAboveMm(section, item, cat, cfg) - (item.yMm + itemHeightMm(item, cat)));
}

/** Hauteur de suspension utilisable sous une barre de penderie. */
export function hangingDropMm(section: Section, item: Item, cat: Catalog): number {
  return Math.max(0, item.yMm - floorBelowMm(section, item, cat));
}

/** Hauteur d'un element au-dessus du SOL (et non du plancher interieur). */
export function heightFromFloorMm(cfg: Config, cat: Catalog, yMm: number): number {
  return cfg.carcass.plinthMm + cat.structural.panelThicknessMm + yMm;
}

/** Paires d'elements qui se chevauchent dans une colonne. */
export function overlaps(section: Section, cat: Catalog): Array<[Item, Item]> {
  const items = sortedItems(section, cat);
  const out: Array<[Item, Item]> = [];
  for (let i = 1; i < items.length; i++) {
    const prevTop = items[i - 1].yMm + itemHeightMm(items[i - 1], cat);
    if (items[i].yMm < prevTop) out.push([items[i - 1], items[i]]);
  }
  return out;
}
