/* FreeSize — squarified treemap visual panel */

// ---- squarified treemap layout ----
function squarify(items, x, y, w, h) {
  // items: [{node, value}] sorted desc; returns [{node,x,y,w,h}]
  const out = [];
  const total = items.reduce((s, it) => s + it.value, 0);
  if (total <= 0 || w <= 0 || h <= 0) return out;
  const scale = (w * h) / total;
  const scaled = items.map((it) => ({ node: it.node, area: it.value * scale }));

  let rx = x, ry = y, rw = w, rh = h;
  let i = 0;
  while (i < scaled.length) {
    const vertical = rw < rh; // lay row along shorter side
    const sideLen = vertical ? rw : rh;
    let row = [scaled[i]];
    let rowArea = scaled[i].area;
    let best = worst(row, rowArea, sideLen);
    let j = i + 1;
    while (j < scaled.length) {
      const na = rowArea + scaled[j].area;
      const nw = worst([...row, scaled[j]], na, sideLen);
      if (nw > best) break;
      row.push(scaled[j]);
      rowArea = na;
      best = nw;
      j++;
    }
    // place row
    const rowThickness = rowArea / sideLen;
    let off = 0;
    for (const cell of row) {
      const cellLen = cell.area / rowThickness;
      if (vertical) {
        out.push({ node: cell.node, x: rx + off, y: ry, w: cellLen, h: rowThickness });
      } else {
        out.push({ node: cell.node, x: rx, y: ry + off, w: rowThickness, h: cellLen });
      }
      off += cellLen;
    }
    if (vertical) { ry += rowThickness; rh -= rowThickness; }
    else { rx += rowThickness; rw -= rowThickness; }
    i = j;
  }
  return out;
}
function worst(row, rowArea, sideLen) {
  const thickness = rowArea / sideLen;
  let maxR = 0;
  for (const c of row) {
    const len = c.area / thickness;
    const ar = Math.max(thickness / len, len / thickness);
    if (ar > maxR) maxR = ar;
  }
  return maxR;
}

function useSize() {
  const ref = React.useRef(null);
  const [size, setSize] = React.useState({ w: 380, h: 380 });
  React.useLayoutEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver((entries) => {
      const r = entries[0].contentRect;
      if (r.width > 0 && r.height > 0) setSize({ w: r.width, h: r.height });
    });
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  return [ref, size];
}

function Treemap({ root, globalMax, onDrill, selected, onSelect }) {
  const { heatColor, heatT, fmt } = window.FZ;
  const [ref, size] = useSize();
  const children = (root.children || []).filter((c) => c.size > 0);
  const items = children.map((c) => ({ node: c, value: c.size })).sort((a, b) => b.value - a.value);
  const pad = 3;
  const tiles = squarify(items, pad, pad, Math.max(0, size.w - pad * 2), Math.max(0, size.h - pad * 2));

  return (
    <div className="treemap" ref={ref}>
      {tiles.map((tl) => {
        const showLabel = tl.w > 50 && tl.h > 22;
        const showSub = tl.w > 64 && tl.h > 38;
        const isDir = tl.node.type === "dir";
        const t = heatT(tl.node.size, globalMax);
        const ink = t > 0.66 ? "oklch(0.99 0.01 60)" : "oklch(0.22 0.03 255)";
        const inkSub = t > 0.66 ? "oklch(0.97 0.02 60 / 0.9)" : "oklch(0.28 0.03 255 / 0.9)";
        return (
          <div
            key={tl.node.id}
            className={"tm-tile" + (selected === tl.node.id ? " active" : "")}
            style={{
              left: (tl.x / size.w) * 100 + "%",
              top: (tl.y / size.h) * 100 + "%",
              width: (tl.w / size.w) * 100 + "%",
              height: (tl.h / size.h) * 100 + "%",
              background: heatColor(t, isDir ? 1 : 0.82),
            }}
            title={tl.node.name + " · " + fmt(tl.node.size)}
            onClick={() => onSelect(tl.node.id)}
            onDoubleClick={() => isDir && tl.node.children && tl.node.children.length && onDrill(tl.node)}
          >
            {showLabel && <span className="tm-label" style={{ color: ink, textShadow: "none" }}>{tl.node.name}</span>}
            {showSub && <span className="tm-sub" style={{ color: inkSub }}>{fmt(tl.node.size)}</span>}
          </div>
        );
      })}
    </div>
  );
}
window.Treemap = Treemap;
window.fzSquarify = squarify;
