// Scene 4: "The Wall"
// Inherits the GPU package layout from scene 3, but tells a different story.
// Data pulses fire from HBM toward the die — but the bus is narrow.
// Pulses queue at the die edge in amber. Cores light briefly then go dark.
// ~3% stay lit. Annotation: "3% of peak FP8 · 1,979 TFLOPS available"

const WALL_INK = 'rgba(244, 241, 236, 0.85)';
const WALL_DIM = 'rgba(244, 241, 236, 0.35)';
const WALL_FAINT = 'rgba(244, 241, 236, 0.15)';
const WALL_CYAN = '#6DD0FF';
const WALL_DEEP_CYAN = '#4A8FC9';
const WALL_AMBER = '#FFAE00';

// Layout — matches scene 3 GPUPackage geometry so the handoff is clean.
const PKG_W = 1100;
const PKG_H = 780;
const DIE_W = 440;
const DIE_H = 360;

// HBM towers, mirroring scene 3.
const HBM = [
  { side: 'left',  x: -PKG_W / 2 + 60,        y: -DIE_H / 2 - 30, w: 200, h: DIE_H + 60 },
  { side: 'right', x:  PKG_W / 2 - 260,        y: -DIE_H / 2 - 30, w: 200, h: DIE_H + 60 },
];

// Bus traces — the narrow channels carrying pulses from each HBM to die edge.
// 8 lanes per side, evenly spaced vertically across the die height.
const LANES_PER_SIDE = 8;
function getLanes() {
  const lanes = [];
  for (let i = 0; i < LANES_PER_SIDE; i++) {
    const y = -DIE_H / 2 + 30 + i * (DIE_H - 60) / (LANES_PER_SIDE - 1);
    lanes.push({ side: 'left',  y, idx: i });
    lanes.push({ side: 'right', y, idx: i });
  }
  return lanes;
}
const LANES = getLanes();

// Cores grid inside the die.
const CORE_COLS = 18;
const CORE_ROWS = 12;
const CORE_PAD_X = 20;
const CORE_PAD_Y = 20;
const CORE_W = (DIE_W - CORE_PAD_X * 2) / CORE_COLS;
const CORE_H = (DIE_H - CORE_PAD_Y * 2) / CORE_ROWS;

// Pre-pick which cores stay lit at the end (~3%).
// 18*12 = 216 cores → 3% ≈ 6-7 cores.
const TOTAL_CORES = CORE_COLS * CORE_ROWS;
const LIT_TARGET = 7;
function pickLitCores() {
  // Deterministic: scatter them visually across the die.
  const seeds = [
    [3, 2], [9, 4], [14, 3], [5, 7], [12, 8], [16, 10], [2, 10],
  ];
  const set = new Set();
  for (const [c, r] of seeds) set.add(`${r}-${c}`);
  return set;
}
const LIT_CORES = pickLitCores();

function Scene4_Wall() {
  const { localTime, duration } = useSprite();
  const t = localTime;

  // Sub-beat times within scene4 (relative to scene start).
  // Total 3.5s scene: 0–1.0 pulses queue, 1.0–2.0 cores light/darken,
  // 2.0–3.0 annotations appear, 3.0–3.5 hold + fade for loop.
  const beat = {
    queue:    { start: 0.0,  end: 1.0 },
    cores:    { start: 1.0,  end: 2.0 },
    annot:    { start: 2.0,  end: 3.0 },
    fade:     { start: 3.1,  end: 3.5 },
  };

  // Camera scale — slight pull-back from scene 3 hand-off.
  const camScale = interpolate(
    [0, 0.6, duration],
    [1.1, 1.0, 0.98],
    Easing.easeOutCubic
  )(t);

  // Loop-seam dim: at the very end, dim everything so the wafer can fade up underneath.
  const fadeOut = interpolate(
    [beat.fade.start, beat.fade.end],
    [0, 1],
    Easing.easeInOutCubic
  )(t);
  const sceneOp = 1 - fadeOut * 0.85;

  // Pulse logic: pulses spawn on each lane at staggered times, travel toward
  // the die edge, then PILE UP. They don't enter the die — they queue.
  // Each pulse lives along its lane from origin (HBM-edge) to a queue position
  // near the die edge. As more pulses spawn, the queue stretches back.
  const pulseElems = [];
  const queueAmberByLane = {}; // lane key → 0..1 intensity at die edge

  // Pulse spawn rate per lane: one every ~0.18s, repeating.
  // We collect "queue depth" per lane across time.
  const PULSE_INTERVAL = 0.22;
  const PULSE_TRAVEL = 0.45; // sec from HBM to die edge

  LANES.forEach((lane, laneIdx) => {
    const laneKey = `${lane.side}-${lane.idx}`;
    const startX = lane.side === 'left'  ? HBM[0].x + HBM[0].w   : HBM[1].x;
    const endX   = lane.side === 'left'  ? -DIE_W / 2             :  DIE_W / 2;
    const dist = endX - startX; // positive for left, negative for right

    // Pulses spawn while we're inside the queue beat (and a touch before).
    const pulseStartT = -0.3;
    const pulseEndT = beat.cores.end + 0.2;

    if (t < pulseStartT || t > beat.fade.start) return;

    // For each pulse i that has spawned by now:
    let queueDepth = 0;
    for (let i = 0; i < 60; i++) {
      const spawn = pulseStartT + i * PULSE_INTERVAL + (laneIdx % 5) * 0.04;
      if (spawn > t) break;
      const age = t - spawn;
      // Pulse travels for PULSE_TRAVEL seconds, then queues.
      let progress;
      if (age < PULSE_TRAVEL) {
        progress = age / PULSE_TRAVEL; // 0 → 1
      } else {
        // Queued: sit at die edge, with stack-back offset based on depth.
        progress = 1 + queueDepth * 0.04; // each queued pulse pushes ~4% backward
        queueDepth += 1;
      }
      // If pulse has been around too long, drop it (limits visual clutter)
      if (age > PULSE_TRAVEL + 1.5) continue;

      const px = startX + dist * Math.min(progress, 1);
      // Queued pulses get pushed back along the lane
      const pxQueued = age >= PULSE_TRAVEL
        ? endX - (dist > 0 ? 1 : -1) * (queueDepth - 1) * 14
        : px;
      const finalX = age >= PULSE_TRAVEL ? pxQueued : px;

      // Color shifts to amber as pulses near or enter the queue
      const amberMix = age >= PULSE_TRAVEL
        ? 1
        : clamp((progress - 0.7) / 0.3, 0, 1);
      const fill = amberMix > 0.5 ? WALL_AMBER : WALL_CYAN;
      const opacity = age >= PULSE_TRAVEL
        ? Math.max(0, 1 - (queueDepth - 1) * 0.08) * 0.95
        : 0.9;
      const r = age >= PULSE_TRAVEL ? 3.5 : 2.8;

      pulseElems.push(
        <circle
          key={`${laneKey}-${i}`}
          cx={finalX} cy={lane.y}
          r={r}
          fill={fill}
          opacity={opacity}
        />
      );
    }
    // Track amber buildup at die edge per lane
    queueAmberByLane[laneKey] = clamp(queueDepth / 5, 0, 1);
  });

  // Cores: brief flash then dim. Compute per-core intensity.
  // During beat.cores.start..start+0.4, all cores ramp up.
  // Then they fade out, except the LIT_CORES which stay glowing.
  const coreElems = [];
  for (let r = 0; r < CORE_ROWS; r++) {
    for (let c = 0; c < CORE_COLS; c++) {
      const key = `${r}-${c}`;
      const isLit = LIT_CORES.has(key);
      // Random per-core stagger for organic flash
      const stagger = ((r * 31 + c * 17) % 13) / 13 * 0.15;
      const flashStart = beat.cores.start + stagger;
      const flashPeak  = flashStart + 0.25;
      const flashEnd   = flashStart + 0.55;
      let intensity;
      if (t < flashStart) {
        intensity = 0;
      } else if (t < flashPeak) {
        intensity = (t - flashStart) / (flashPeak - flashStart);
      } else if (t < flashEnd) {
        const fade = (t - flashPeak) / (flashEnd - flashPeak);
        intensity = 1 - fade * (isLit ? 0.15 : 1);
      } else {
        intensity = isLit ? 0.9 : 0;
      }
      // Apply final fade for loop
      intensity *= sceneOp;

      const cx = -DIE_W / 2 + CORE_PAD_X + c * CORE_W + CORE_W / 2;
      const cy = -DIE_H / 2 + CORE_PAD_Y + r * CORE_H + CORE_H / 2;
      const cellW = CORE_W - 2;
      const cellH = CORE_H - 2;

      // Lit cores get amber, others get cyan during flash
      const fill = isLit ? WALL_AMBER : WALL_CYAN;
      // Always show a faint outline so the lattice reads
      coreElems.push(
        <g key={key}>
          <rect
            x={cx - cellW / 2} y={cy - cellH / 2}
            width={cellW} height={cellH}
            fill="none"
            stroke={WALL_FAINT}
            strokeWidth={0.6}
          />
          {intensity > 0.02 && (
            <rect
              x={cx - cellW / 2} y={cy - cellH / 2}
              width={cellW} height={cellH}
              fill={fill}
              opacity={intensity * (isLit ? 0.7 : 0.5)}
            />
          )}
          {isLit && intensity > 0.3 && (
            <rect
              x={cx - cellW / 2 - 1} y={cy - cellH / 2 - 1}
              width={cellW + 2} height={cellH + 2}
              fill="none"
              stroke={WALL_AMBER}
              strokeWidth={1.2}
              opacity={intensity}
            />
          )}
        </g>
      );
    }
  }

  // Annotation: pick one lit core to anchor the leader line (top-right area).
  const annotOp = interpolate(
    [beat.annot.start, beat.annot.start + 0.4, beat.fade.start, beat.fade.end],
    [0, 1, 1, 0],
    Easing.easeInOutCubic
  )(t);

  // Anchor on the rightmost lit core
  let anchor = { c: 14, r: 3 };
  for (const key of LIT_CORES) {
    const [rr, cc] = key.split('-').map(Number);
    if (cc > anchor.c) anchor = { c: cc, r: rr };
  }
  const anchorX = -DIE_W / 2 + CORE_PAD_X + anchor.c * CORE_W + CORE_W / 2;
  const anchorY = -DIE_H / 2 + CORE_PAD_Y + anchor.r * CORE_H + CORE_H / 2;

  // Leader line goes from anchor up-right, then horizontal to a label
  const leaderEndX = DIE_W / 2 + 80;
  const leaderEndY = anchorY - 60;

  return (
    <div style={{
      position: 'absolute',
      left: 960, top: 540,
      transform: `translate(-50%, -50%) scale(${camScale})`,
      transformOrigin: 'center',
      opacity: sceneOp,
      willChange: 'transform, opacity',
    }}>
      <svg
        width={PKG_W + 200} height={PKG_H + 200}
        viewBox={`${-(PKG_W + 200) / 2} ${-(PKG_H + 200) / 2} ${PKG_W + 200} ${PKG_H + 200}`}
        style={{ overflow: 'visible' }}
      >
        {/* Outer package outline */}
        <rect
          x={-PKG_W / 2} y={-PKG_H / 2}
          width={PKG_W} height={PKG_H}
          fill="#0B0D11"
          stroke={WALL_INK}
          strokeWidth={1.2}
        />
        <rect
          x={-PKG_W / 2 + 30} y={-PKG_H / 2 + 30}
          width={PKG_W - 60} height={PKG_H - 60}
          fill="none"
          stroke={WALL_FAINT}
          strokeWidth={1}
        />

        {/* HBM towers */}
        {HBM.map((m, i) => (
          <g key={i}>
            <rect x={m.x} y={m.y} width={m.w} height={m.h} fill="none" stroke={WALL_INK} strokeWidth={1.2} />
            {[0.2, 0.4, 0.6, 0.8].map((p, j) => (
              <line
                key={j}
                x1={m.x} y1={m.y + m.h * p}
                x2={m.x + m.w} y2={m.y + m.h * p}
                stroke={WALL_FAINT}
                strokeWidth={0.7}
              />
            ))}
            <text
              x={m.x + m.w / 2} y={m.y - 10}
              textAnchor="middle"
              fill="rgba(244,241,236,0.5)"
              fontFamily="JetBrains Mono, ui-monospace, monospace"
              fontSize={11}
              letterSpacing="0.18em"
            >
              HBM
            </text>
          </g>
        ))}

        {/* Bus lanes — drawn as thin horizontal lines, dim by default,
            warming to amber where pulses are queueing */}
        {LANES.map((lane, i) => {
          const laneKey = `${lane.side}-${lane.idx}`;
          const startX = lane.side === 'left' ? HBM[0].x + HBM[0].w : HBM[1].x;
          const endX = lane.side === 'left' ? -DIE_W / 2 : DIE_W / 2;
          const amber = queueAmberByLane[laneKey] ?? 0;
          const stroke = amber > 0.3 ? WALL_AMBER : WALL_DEEP_CYAN;
          const op = 0.35 + amber * 0.4;
          return (
            <line
              key={i}
              x1={startX} y1={lane.y}
              x2={endX}   y2={lane.y}
              stroke={stroke}
              strokeWidth={0.9}
              opacity={op}
            />
          );
        })}

        {/* Die frame */}
        <rect
          x={-DIE_W / 2} y={-DIE_H / 2}
          width={DIE_W} height={DIE_H}
          fill="#0B0D11"
          stroke={WALL_CYAN}
          strokeWidth={1.4}
        />
        {/* Corner brackets */}
        {[
          [-DIE_W / 2, -DIE_H / 2,  1,  1],
          [ DIE_W / 2, -DIE_H / 2, -1,  1],
          [-DIE_W / 2,  DIE_H / 2,  1, -1],
          [ DIE_W / 2,  DIE_H / 2, -1, -1],
        ].map(([x, y, sx, sy], i) => (
          <g key={i} stroke={WALL_CYAN} strokeWidth={1.4} fill="none">
            <line x1={x} y1={y} x2={x + sx * 18} y2={y} />
            <line x1={x} y1={y} x2={x} y2={y + sy * 18} />
          </g>
        ))}

        {/* Cores grid */}
        {coreElems}

        {/* Pulses — drawn over cores so queueing reads */}
        {pulseElems}

        {/* Bottleneck markers at die edge — short amber tick where lanes meet die */}
        {t > beat.queue.start + 0.4 && t < beat.fade.start && LANES.map((lane, i) => {
          const laneKey = `${lane.side}-${lane.idx}`;
          const amber = queueAmberByLane[laneKey] ?? 0;
          if (amber < 0.2) return null;
          const x = lane.side === 'left' ? -DIE_W / 2 : DIE_W / 2;
          return (
            <line
              key={`tick-${i}`}
              x1={x} y1={lane.y - 4}
              x2={x} y2={lane.y + 4}
              stroke={WALL_AMBER}
              strokeWidth={1.6}
              opacity={amber * 0.9}
            />
          );
        })}

        {/* Annotation leader + label */}
        {annotOp > 0.02 && (
          <g opacity={annotOp}>
            <line
              x1={anchorX} y1={anchorY}
              x2={anchorX + 30} y2={leaderEndY}
              stroke={WALL_AMBER}
              strokeWidth={1}
            />
            <line
              x1={anchorX + 30} y1={leaderEndY}
              x2={leaderEndX} y2={leaderEndY}
              stroke={WALL_AMBER}
              strokeWidth={1}
            />
            <text
              x={leaderEndX + 8} y={leaderEndY - 6}
              fill={WALL_AMBER}
              fontFamily="JetBrains Mono, ui-monospace, monospace"
              fontSize={14}
              fontWeight={500}
              letterSpacing="0.14em"
            >
              3% OF PEAK FP8
            </text>
            <text
              x={leaderEndX + 8} y={leaderEndY + 14}
              fill="rgba(244,241,236,0.6)"
              fontFamily="JetBrains Mono, ui-monospace, monospace"
              fontSize={11}
              letterSpacing="0.18em"
            >
              1,979 TFLOPS AVAILABLE
            </text>
          </g>
        )}
      </svg>
    </div>
  );
}

window.Scene4_Wall = Scene4_Wall;
