// iso-scene.jsx
// The isometric world that surrounds the live-poll monitor. Two arrangements
// ("city" plaza + "desk" console) built from the iso-engine primitives.
// Painter's-ordered so blocks occlude correctly.

const VB = { w: 1040, h: 760 };
const ORIGIN = { x: 520, y: 300 };
const PED_H = 1.0;                       // pedestal the monitor stands on

// ── tonal muting ────────────────────────────────────────────────────────────
// Corporate directions desaturate the world toward a warm grey so the crowd
// reads as "audience / data points", not candy. We mix every palette colour
// toward a neutral taupe; the result is a low-chroma tonal ramp.
function mixHex(a, b, t) {
  const pa = parseInt(a.slice(1), 16), pb = parseInt(b.slice(1), 16);
  const ar = (pa >> 16) & 255, ag = (pa >> 8) & 255, ab = pa & 255;
  const br = (pb >> 16) & 255, bg = (pb >> 8) & 255, bb = pb & 255;
  const h = (v) => Math.round(v).toString(16).padStart(2, '0');
  return '#' + h(ar + (br - ar) * t) + h(ag + (bg - ag) * t) + h(ab + (bb - ab) * t);
}
const NEUTRAL = '#938A7B';
function mute(hex, t = 0.52) { return mixHex(hex, NEUTRAL, t); }
function mutePalette(pal) {
  const out = {};
  for (const k in pal) out[k] = mute(pal[k]);
  return out;
}

// Where the HTML monitor should plant itself (bottom-centre), as % of stage.
const MONITOR_ANCHOR = (() => {
  const p = iso(0, 0, PED_H);
  return { left: ((ORIGIN.x + p.x) / VB.w) * 100, top: ((ORIGIN.y + p.y) / VB.h) * 100 };
})();

// ── building with windows ──────────────────────────────────────────────────
function winRight(X, y, z, sy, sz) {
  return pts([iso(X, y, z), iso(X, y + sy, z), iso(X, y + sy, z + sz), iso(X, y, z + sz)]);
}
function winLeft(Y, x, z, sx, sz) {
  return pts([iso(x, Y, z), iso(x + sx, Y, z), iso(x + sx, Y, z + sz), iso(x, Y, z + sz)]);
}
function Building({ gx, gy, w, d, h, color }) {
  const roof = shade(color, 0.16);
  const lit = '#FBE7B0', dark = shade(color, -0.46);
  const wins = [];
  // window grid on the right face (x = gx+w)
  let k = 0;
  for (let zz = 0.5; zz < h - 0.4; zz += 0.9) {
    for (let yy = gy + 0.28; yy < gy + d - 0.3; yy += 0.55) {
      const on = ((k * 7 + 3) % 5) > 1;
      wins.push(<polygon key={'r' + k} points={winRight(gx + w, yy, zz, 0.32, 0.5)}
        fill={on ? lit : dark} opacity={on ? 0.92 : 0.55} />);
      k++;
    }
  }
  for (let zz = 0.5; zz < h - 0.4; zz += 0.9) {
    for (let xx = gx + 0.28; xx < gx + w - 0.3; xx += 0.55) {
      const on = ((k * 5 + 1) % 5) > 2;
      wins.push(<polygon key={'l' + k} points={winLeft(gy + d, xx, zz, 0.32, 0.5)}
        fill={on ? lit : dark} opacity={on ? 0.85 : 0.5} />);
      k++;
    }
  }
  return (
    <g>
      <Cuboid gx={gx} gy={gy} w={w} d={d} h={h} color={color} />
      <g>{wins}</g>
      {/* thin roof cap for a chunky toy silhouette */}
      <Cuboid gx={gx + 0.08} gy={gy + 0.08} w={w - 0.16} d={d - 0.16} h={0.18} z={h} color={roof} />
    </g>
  );
}

// ── pedestal under the monitor ──────────────────────────────────────────────
function Pedestal({ pal }) {
  return (
    <g>
      <Cuboid gx={-1.15} gy={-1.15} w={2.3} d={2.3} h={PED_H} color={pal.plaza} />
      <Cuboid gx={-0.95} gy={-0.95} w={1.9} d={1.9} h={0.16} z={PED_H} color={shade(pal.primary, 0.34)} />
    </g>
  );
}

// ── raised land island (grass top + dirt sides) — the city sits on top ──────
// Top surface is z=0 so every prop placed at z=0 rests on it. Plaza + roads
// are flat colour patches flush on the grass top.
function RaisedIsland({ pal, half = 4.6, plazaHalf = 3.0, roads = true }) {
  const dirtR = shade(pal.road, -0.16);
  const dirtL = shade(pal.road, -0.34);
  const ph = plazaHalf;
  return (
    <g>
      <Cuboid gx={-half} gy={-half} w={half * 2} d={half * 2} h={0.7} z={-0.7}
        color={pal.grass} topColor={pal.grass} rightColor={dirtR} leftColor={dirtL} outline={shade(pal.grass, -0.42)} />
      {/* paved plaza patch */}
      <polygon points={pts([iso(-ph, -ph), iso(ph, -ph), iso(ph, ph), iso(-ph, ph)])}
        fill={pal.plaza} stroke={shade(pal.plaza, -0.22)} strokeWidth="1.4" strokeLinejoin="round" />
      {roads && <g>
        <polygon points={pts([iso(-ph, -0.5), iso(ph, -0.5), iso(ph, 0.5), iso(-ph, 0.5)])} fill={pal.road} opacity="0.85" />
        <polygon points={pts([iso(-0.5, -ph), iso(0.5, -ph), iso(0.5, ph), iso(-0.5, ph)])} fill={pal.road} opacity="0.85" />
      </g>}
    </g>
  );
}

// ── prop builders return {d, node} for depth sorting ────────────────────────
function depthOf(gx, gy, w = 0, d = 0) { return gx + gy + (w + d) / 2; }

function cityProps(pal0, muted) {
  const pal = muted ? mutePalette(pal0) : pal0;
  const P = [];
  const add = (d, node) => P.push({ d, node });
  const blds = [
    [-4.4, -2.8, 1.5, 1.5, 2.8, pal.blue],
    [-2.6, -4.3, 1.5, 1.5, 3.4, pal.amber],
    [-0.2, -4.5, 1.4, 1.4, 2.4, pal.primary],
    [ 2.3, -4.0, 1.5, 1.5, 3.0, pal.green],
    [ 3.7, -1.9, 1.4, 1.4, 2.4, pal.amber],
    [ 3.7,  0.5, 1.4, 1.4, 3.2, pal.primary],
    [ 2.8,  2.7, 1.4, 1.4, 2.0, pal.blue],
    [ 0.7,  3.8, 1.4, 1.4, 1.8, pal.amber],
    [-1.7,  3.7, 1.4, 1.4, 1.6, pal.purple],
    [-4.0,  1.5, 1.4, 1.4, 2.2, pal.green],
    [-4.4, -0.5, 1.4, 1.4, 2.8, pal.amber],
  ];
  blds.forEach((b, i) => add(depthOf(b[0], b[1], b[2], b[3]),
    <Building key={'b' + i} gx={b[0]} gy={b[1]} w={b[2]} d={b[3]} h={b[4]} color={b[5]} />));

  const trees = [[-2.3, -1.2], [1.9, -1.5], [-1.5, 2.2], [2.3, 0.0], [-4.2, 3.4], [4.2, -3.6]];
  trees.forEach((t, i) => add(depthOf(t[0], t[1]), <Tree key={'t' + i} gx={t[0]} gy={t[1]} color={pal.green} />));

  const lamps = [[-2.2, -2.2], [2.2, -2.2], [-2.2, 2.2], [2.2, 2.2]];
  lamps.forEach((l, i) => add(depthOf(l[0], l[1]), <Lamp key={'l' + i} gx={l[0]} gy={l[1]} accent={pal.amber} />));

  // the audience — bright little voters ringing the plaza, phones out
  const ppl = [
    [-2.7, 0.4, pal.blue, true], [-1.8, 1.8, pal.amber, false], [-0.5, 2.6, pal.primary, true],
    [1.1, 2.6, pal.green, false], [2.5, 1.4, pal.blue, true], [2.7, -0.3, pal.amber, false],
    [-2.9, -0.7, pal.primary, false], [0.6, -2.7, pal.purple, true], [-0.8, -2.6, pal.green, false],
    [1.9, -2.2, pal.primary, true], [-2.2, 2.5, pal.amber, true], [2.2, 2.4, pal.purple, false],
    [0.2, 1.4, pal.blue, false], [-1.3, -1.0, pal.amber, true],
  ];
  ppl.forEach((p, i) => add(depthOf(p[0], p[1]) + 0.02, <Person key={'p' + i} gx={p[0]} gy={p[1]} color={p[2]} phone={p[3]} />));
  return P;
}

function deskProps(pal0, muted) {
  const pal = muted ? mutePalette(pal0) : pal0;
  const P = [];
  const add = (d, node) => P.push({ d, node });
  // a couple of skyline towers far back
  const towers = [[-3.0, -3.0, 1.3, 1.3, 3.0, pal.blue], [2.4, -2.9, 1.2, 1.2, 2.5, pal.amber], [-2.9, 2.4, 1.2, 1.2, 2.2, pal.green]];
  towers.forEach((b, i) => add(depthOf(b[0], b[1], b[2], b[3]),
    <Building key={'tw' + i} gx={b[0]} gy={b[1]} w={b[2]} d={b[3]} h={b[4]} color={b[5]} />));

  // the desk: a broad low slab the monitor sits on
  add(depthOf(-2.4, -2.4, 4.8, 4.8), <Cuboid key="desk" gx={-2.4} gy={-2.4} w={4.8} d={4.8} h={0.7} color={shade(pal.primary, 0.32)} />);
  add(depthOf(-2.3, -2.3, 4.6, 4.6) + 0.01, <Cuboid key="desktop" gx={-2.3} gy={-2.3} w={4.6} d={4.6} h={0.12} z={0.7} color={shade(pal.plaza, 0.06)} />);

  // keyboard slab in front of the monitor
  add(depthOf(-1.3, 1.1, 2.6, 1.0) + 0.7, <Cuboid key="kbd" gx={-1.3} gy={1.1} w={2.6} d={1.0} h={0.16} z={0.82} color={shade(pal.plaza, -0.04)} />);

  // floppies + a mug
  add(depthOf(1.5, 0.8) + 0.7, <Floppy key="f1" gx={1.5} gy={0.8} color={pal.blue} z={0.82} />);
  add(depthOf(1.7, 1.5) + 0.7, <Floppy key="f2" gx={1.7} gy={1.5} color={pal.primary} z={0.82} />);
  add(depthOf(-1.9, -0.6) + 0.7, <Cuboid key="mug" gx={-1.9} gy={-0.6} w={0.5} d={0.5} h={0.6} z={0.82} color={pal.amber} />);

  // a few voters standing at the desk
  const ppl = [[-2.9, 1.8, pal.blue, true], [-1.9, 2.8, pal.primary, true], [2.7, 2.0, pal.green, false], [3.0, 0.7, pal.amber, true]];
  ppl.forEach((p, i) => add(depthOf(p[0], p[1]) + 0.02, <Person key={'dp' + i} gx={p[0]} gy={p[1]} color={p[2]} phone={p[3]} />));
  return P;
}

// ── auditorium: a blocky crowd facing the live-poll screen ──────────────────
// Floor is a diamond TRUNCATED at the front, so there's no long empty point of
// dead space below the crowd (it ends just past the front row).
function AuditoriumFloor({ pal }) {
  const top = pal.plaza;
  const dR = shade(pal.road, -0.16), dL = shade(pal.road, -0.34);
  const h = 0.7;
  const T = (x, y) => iso(x, y, 0), Bm = (x, y) => iso(x, y, -h);
  // half-size 6.2 square, front (down-screen) corner cut at x+y = 5
  const A = [-6.2, -6.2], B = [6.2, -6.2], C = [6.2, -1.2], D = [-1.2, 6.2], E = [-6.2, 6.2];
  const topFace = [T(...A), T(...B), T(...C), T(...D), T(...E)];
  const skirt = (p, q, col) => <polygon points={pts([T(...p), T(...q), Bm(...q), Bm(...p)])} fill={col} />;
  return (
    <g stroke={shade(top, -0.4)} strokeWidth="1" strokeLinejoin="round">
      {skirt(B, C, dR)}
      {skirt(C, D, dL)}
      {skirt(D, E, dL)}
      <polygon points={pts(topFace)} fill={top} />
      {/* darker stage apron up-screen, under the screen */}
      <polygon points={pts([iso(-3.2, -3.2), iso(0.2, -3.2), iso(0.2, 0.2), iso(-3.2, 0.2)])}
        fill={shade(top, -0.12)} opacity="0.6" stroke="none" />
      {/* centre aisle */}
      <polygon points={pts([iso(-0.45, -3.0), iso(0.45, -3.0), iso(0.45, 4.2), iso(-0.45, 4.2)])}
        fill={shade(top, -0.16)} opacity="0.5" stroke="none" />
    </g>
  );
}

// Auditorium BACKGROUND — floor furniture that sits BEHIND the screen.
function audProps(pal0, muted) {
  const pal = muted ? mutePalette(pal0) : pal0;
  const P = [];
  const add = (d, node) => P.push({ d, node });

  // stage furniture, directly behind/under the monitor (x=y → sits at sx=0)
  add(depthOf(-3.4, -3.4, 1.7, 1.7), <Cuboid key="wall" gx={-3.4} gy={-3.4} w={1.7} d={1.7} h={2.6} color={shade(pal.plaza, -0.16)} />);
  add(depthOf(-2.3, -2.3, 1.7, 1.7), <Cuboid key="dais" gx={-2.3} gy={-2.3} w={1.7} d={1.7} h={0.55} color={shade(pal.primary, 0.2)} />);
  add(depthOf(-2.2, -2.2, 1.5, 1.5) + 0.01, <Cuboid key="daistop" gx={-2.2} gy={-2.2} w={1.5} d={1.5} h={0.1} z={0.55} color={shade(pal.plaza, 0.05)} />);
  return P;
}

// Auditorium FOREGROUND — the crowd, drawn ON TOP of the screen so the room
// sits in front of the window (we look over the audience toward the slide).
function audCrowd(pal0, muted) {
  const pal = muted ? mutePalette(pal0) : pal0;
  const P = [];
  const add = (d, node) => P.push({ d, node });
  const shirts = [pal.primary, pal.blue, pal.amber, pal.green, pal.purple,
    shade(pal.blue, 0.16), shade(pal.primary, 0.14), shade(pal.green, 0.12), shade(pal.amber, 0.1)];
  const hairs = ['#2B2620', '#4A3528', '#6B4A2E', '#C9A24B', '#3A2A22', '#8A5A3A'];
  const rows = 10, seats = 13;
  for (let d = 0; d < rows; d++) {
    const k = -3.33 + d * 0.74;
    for (let l = 0; l < seats; l++) {
      const off = (d % 2) ? 0.62 : 0;
      const m = -7.5 + l * 1.25 + off;
      if (Math.abs(m) < 0.7) continue;     // centre aisle gap
      const x = (k + m) / 2, y = (k - m) / 2;
      const idx = (d * 5 + l * 3);
      add(depthOf(x, y) + 0.05,
        <BlockPerson key={`a${d}_${l}`} gx={x} gy={y}
          shirt={shirts[(d * 2 + l) % shirts.length]} hair={hairs[idx % hairs.length]} />);
    }
  }
  return P;
}

function IsoWorld({ scene = 'city', pal, muted = false }) {
  const gpal = muted ? mutePalette(pal) : pal;
  const props = (scene === 'desk' ? deskProps(pal, muted) : scene === 'auditorium' ? audProps(pal, muted) : cityProps(pal, muted))
    .slice().sort((a, b) => a.d - b.d);
  const groundFn = scene === 'desk'
    ? <RaisedIsland pal={gpal} half={4.2} plazaHalf={3.4} roads={false} />
    : scene === 'auditorium'
    ? <AuditoriumFloor pal={gpal} />
    : <RaisedIsland pal={gpal} half={5.2} plazaHalf={3.2} roads={true} />;
  const oyOff = scene === 'auditorium' ? 210 : scene === 'city' ? 96 : scene === 'desk' ? 110 : 0;
  const sc = scene === 'city' ? 1.34 : scene === 'desk' ? 1.3 : 1;
  const oy = ORIGIN.y + oyOff;
  return (
    <svg className="iso-world" viewBox={`0 0 ${VB.w} ${VB.h}`} preserveAspectRatio="xMidYMid meet" aria-hidden="true">
      {/* big soft ground shadow */}
      <ellipse cx={ORIGIN.x} cy={oy + 130} rx={430} ry={150} fill="rgba(43,38,32,0.10)" />
      <g transform={`translate(${ORIGIN.x},${oy}) scale(${sc})`}>
        {groundFn}
        {scene !== 'auditorium' && <Pedestal pal={gpal} />}
        {props.map((p, i) => <g key={i}>{p.node}</g>)}
      </g>
    </svg>
  );
}

// Foreground crowd layer — only for the auditorium. Rendered in the DOM AFTER
// the monitor so the audience sits IN FRONT of the screen (we look over the
// room toward the slide). Uses the same transform so seats line up.
function IsoCrowd({ scene = 'auditorium', pal, muted = false }) {
  if (scene !== 'auditorium') return null;
  const crowd = audCrowd(pal, muted).slice().sort((a, b) => a.d - b.d);
  const oy = ORIGIN.y + 210;
  return (
    <svg className="iso-crowd" viewBox={`0 0 ${VB.w} ${VB.h}`} preserveAspectRatio="xMidYMid meet" aria-hidden="true">
      <g transform={`translate(${ORIGIN.x},${oy})`}>
        {crowd.map((p, i) => <g key={i}>{p.node}</g>)}
      </g>
    </svg>
  );
}

// ── IsoAccent ────────────────────────────────────────────────────────────────
// A compact, self-contained toy diorama used as a SIGNATURE FLOURISH in the
// product-first direction (C): a small plaza with a little muted crowd and a
// couple of buildings, no monitor. Sized to fill its container width.
function accentProps(pal) {
  const P = [];
  const add = (d, node) => P.push({ d, node });
  const blds = [
    [-2.6, -2.6, 1.4, 1.4, 2.6, pal.primary],
    [-0.4, -2.9, 1.3, 1.3, 1.8, pal.blue],
    [ 1.9, -2.3, 1.3, 1.3, 2.2, pal.amber],
    [ 2.2,  0.4, 1.3, 1.3, 1.5, pal.green],
  ];
  blds.forEach((b, i) => add(depthOf(b[0], b[1], b[2], b[3]),
    <Building key={'ab' + i} gx={b[0]} gy={b[1]} w={b[2]} d={b[3]} h={b[4]} color={b[5]} />));
  const trees = [[-1.4, 0.8], [0.9, 1.6], [-2.2, 1.9]];
  trees.forEach((t, i) => add(depthOf(t[0], t[1]), <Tree key={'at' + i} gx={t[0]} gy={t[1]} color={pal.green} />));
  const ppl = [
    [-0.7, 0.2, pal.blue, true], [0.6, 0.0, pal.primary, false], [-0.2, 1.3, pal.amber, true],
    [1.4, 0.7, pal.green, false], [-1.5, -0.4, pal.primary, true], [0.3, -1.1, pal.purple, false],
  ];
  ppl.forEach((p, i) => add(depthOf(p[0], p[1]) + 0.02, <Person key={'ap' + i} gx={p[0]} gy={p[1]} color={p[2]} phone={p[3]} />));
  return P;
}

function IsoAccent({ pal, muted = true }) {
  const gpal = muted ? mutePalette(pal) : pal;
  const props = accentProps(muted ? gpal : pal).slice().sort((a, b) => a.d - b.d);
  const oy = ORIGIN.y + 150;
  return (
    <svg className="iso-accent-svg" viewBox={`0 0 ${VB.w} ${VB.h}`} preserveAspectRatio="xMidYMid meet" aria-hidden="true">
      <ellipse cx={ORIGIN.x} cy={oy + 96} rx={300} ry={108} fill="rgba(43,38,32,0.08)" />
      <g transform={`translate(${ORIGIN.x},${oy}) scale(1.42)`}>
        <RaisedIsland pal={gpal} half={3.6} plazaHalf={2.3} roads={true} />
        {props.map((p, i) => <g key={i}>{p.node}</g>)}
      </g>
    </svg>
  );
}

Object.assign(window, { IsoWorld, IsoCrowd, IsoAccent, MONITOR_ANCHOR });
