// Scene 01 — DISPATCH
// A mono dispatch packet types out on the left.
// A halftone field materializes on the right — dots pulsing into place,
// representing context being shipped to a sub-agent.

const DISPATCH_LINES = [
  '$ o8 dispatch --packet 04',
  '  title "halftone density curve"',
  '  scope app/components/swipe-motifs.tsx',
  '  reviewer orchestrator',
  '  quota sub-agent:haiku-4',
  '',
  '  [OK] worktree forked @ feat/halftone-04',
  '  [OK] context scoped — 1 file, 0 deps',
  '  [..] sub-agent dispatched',
];

function SceneDispatch() {
  const { localTime, progress, duration } = useSprite();

  // Type lines in over 1.6s, then hold.
  const typeDur = 1.8;
  const charsPerSec = DISPATCH_LINES.join('\n').length / typeDur;
  const visibleChars = Math.floor(localTime * charsPerSec);

  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);

  // Build the typed-so-far text.
  let remaining = visibleChars;
  const rendered = [];
  for (const line of DISPATCH_LINES) {
    if (remaining <= 0) { rendered.push(''); continue; }
    if (line.length <= remaining) {
      rendered.push(line);
      remaining -= line.length + 1; // +1 for \n
    } else {
      rendered.push(line.slice(0, remaining));
      remaining = 0;
    }
  }

  // Halftone dots — fade in as typing progresses, one grid column at a time
  const rows = 18, cols = 28;
  const dots = [];
  const gridT = clamp((localTime - 0.4) / 1.8, 0, 1);
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      // Reveal columns left-to-right
      const colT = clamp((gridT * (cols + 4) - c) / 4, 0, 1);
      if (colT <= 0) continue;
      const cxN = (c - cols/2) / (cols/2);
      const ryN = (r - rows/2) / (rows/2);
      const dist = Math.sqrt(cxN*cxN + ryN*ryN);
      const edge = Math.max(0, 1 - dist);
      const fade = 1 - c / cols;
      const baseOpacity = Math.min(1, edge * fade * 1.6);
      if (baseOpacity < 0.03) continue;
      dots.push(
        <circle
          key={`${r}-${c}`}
          cx={14 + c * 20}
          cy={14 + r * 20}
          r={2.2}
          fill={O8.ink}
          opacity={baseOpacity * colT}
        />
      );
    }
  }

  return (
    <div style={{ position: 'absolute', inset: 0, opacity: 1 - exit }}>
      {/* Left — headline */}
      <div style={{
        position: 'absolute', left: 80, top: 240,
        width: 780,
        fontFamily: O8.fontSans,
        fontSize: 60, lineHeight: 1.04,
        letterSpacing: '-0.03em', fontWeight: 500,
        color: O8.ink,
      }}>
        Small packet.
        <br/>
        Scoped context.
      </div>

      {/* Terminal card */}
      <div style={{
        position: 'absolute',
        left: 80, top: 460,
        width: 860,
        background: O8.paperDeep,
        border: `1px solid ${O8.hairlineSoft}`,
        borderRadius: 14,
        paddingTop: 24, paddingBottom: 28,
        paddingLeft: 28, paddingRight: 28,
      }}>
        <div style={{
          display: 'flex', justifyContent: 'space-between',
          alignItems: 'baseline',
          paddingBottom: 16,
          borderBottom: `1px solid ${O8.hairlineSoft}`,
          marginBottom: 16,
        }}>
          <BracketLabel>(ORCHESTRATOR TTY)</BracketLabel>
          <Stamp>04 / 10 &middot; 0.21s</Stamp>
        </div>
        <pre style={{
          fontFamily: O8.fontMono,
          fontSize: 18, lineHeight: 1.55,
          color: O8.ink,
          margin: 0,
          whiteSpace: 'pre-wrap',
          minHeight: 330,
        }}>
{rendered.map((l, i) => {
  const isOK = l.includes('[OK]');
  const isActive = l.includes('[..]');
  const color = isOK ? O8.ink : isActive ? O8.accent : O8.ink;
  return (
    <div key={i} style={{color}}>
      {l || '\u00A0'}
      {i === rendered.length - 1 && remaining <= 0 && isActive ? <Cursor time={localTime}/> : null}
      {i === rendered.length - 1 && visibleChars >= DISPATCH_LINES.join('\n').length - 1 && !isActive ? <Cursor time={localTime}/> : null}
    </div>
  );
})}
        </pre>
      </div>

      {/* Right — halftone figure (context ballooning in miniature) */}
      <div style={{
        position: 'absolute',
        right: 80, top: 280,
        width: 640, height: 400,
      }}>
        <div style={{
          position: 'absolute', right: 0, top: -36,
          display: 'flex', alignItems: 'baseline', gap: 16,
        }}>
          <BracketLabel>(CONTEXT FIELD)</BracketLabel>
          <Stamp>— 1 FILE, 0 DEPS</Stamp>
        </div>
        <svg viewBox="0 0 580 380" width="640" height="420" aria-hidden>
          {dots}
          {/* Hairline axes */}
          <line x1="180" y1="20" x2="180" y2="360" stroke={O8.hairline} strokeWidth="0.5"/>
          <line x1="20" y1="180" x2="560" y2="180" stroke={O8.hairline} strokeWidth="0.5"/>
          {/* One orange focal pixel — appears last */}
          <circle
            cx="180" cy="180"
            r={3.5 * clamp((localTime - 1.8) / 0.4, 0, 1)}
            fill={O8.accent}
          />
        </svg>
      </div>
    </div>
  );
}

window.SceneDispatch = SceneDispatch;
