// printer.jsx- input block → simple black box → token output
//
// Slide variants (driven by props):
//   1.5- bare model. Input is one undifferentiated text blob. Model spits a
//         comical 220-token chaotic pile, no wrapper, no halt.
//   1.6- divided input (system / user / assistant / user). The model produces
//         a coherent ~14-word sentence, then ⟨eos⟩ as the 15th token. Until
//         that EOS arrives the wrapper border is dark/quiet- only AFTER ⟨eos⟩
//         does it light up bright orange and freeze the printer.
//   1.7- same end-state as 1.6, no regeneration; two big 👁 overlays appear,
//         one over the input block, one over the output sequence.
//
// All three slides use a flex layout with `alignItems: center`, so the input
// block, the LLM box, and the output sequence all share a vertical centerline.
//
// Props:
//   showWrapper   - draw the API · WRAPPER frame
//   showStop      - emit ⟨eos⟩ at the very end of the sequence
//   noDividers    - render input as one undivided text blob (slide 1.5)
//   bigEyes       - overlay two big 👁 (slide 1.7)
//   staticMode    - pre-populate the full sequence + halt; no animation
//   maxOutputs    - total tokens (default 220 chaotic; ≤50 → flow mode)

const INPUT_TURNS = [
  { role: 'system',    text: "You are a helpful assistant. Be concise." },
  { role: 'user',      text: "what's the weather in berlin today?" },
  { role: 'assistant', text: "Mostly cloudy, high near 15°C, light showers later." },
  { role: 'user',      text: "what about in france?" },
];

// One continuous text blob (slide 1.5)- joins every turn with no role markers.
const BLOB_TEXT = INPUT_TURNS.map(t => t.text).join(' ');

// Long, varied pool for the 1.5 chaotic stream.
const OUTPUT_POOL = [
  "the","weather","in","berlin","today","is","mostly","cloudy","with","a",
  "high","near","fifteen","degrees","celsius","light","showers","later",
  "tonight","temperatures","drop","to","seven","wind","calm","skies",
  "clearing","by","sunrise","tomorrow","forecast","scattered","clouds",
  "mild","ranging","from","ten","eighteen","you","may","want","an",
  "umbrella","late","day","just","in","case","humidity","sixty","percent",
  "pressure","steady","one","thousand","fifteen","millibars","sunset","at",
  "seventeen","forty","two","translate","please","summary","follows",
  "first","then","second","note","key","ideas","appear","early",
  "ingredients","include","pasta","eggs","pancetta","cheese","pepper",
  "mix","stir","serve","warm","done","next","step","check","this","also",
  "remember","always","never","sometimes","probably","maybe","yes","no",
];

// Short coherent answer to "what about in france?"- 14 words, ⟨eos⟩ is #15.
const SHORT_ANSWER = [
  "in","paris","today,","partly","cloudy",
  "with","a","high","near","eighteen",
  "and","light","winds","later",
];

function PaperPrinter({
  showWrapper = false,
  showStop = false,
  noDividers = false,
  bigEyes = false,
  staticMode = false,
  maxOutputs = 220,
}) {
  const speed = useAnimSpeed();
  const resetKey = useSlideResetKey();

  const [outputs, setOutputs] = useState([]);
  const [eos, setEos] = useState(false);
  const [done, setDone] = useState(false);
  const counterRef = useRef(0);

  const flowMode = maxOutputs <= 50; // ≤50 → readable sentence-flow; otherwise chaotic pile

  // Reset on slide change
  useEffect(() => {
    setOutputs([]);
    setEos(false);
    setDone(false);
    counterRef.current = 0;
  }, [resetKey]);

  // Static mode (slide 1.7): pre-populate the full sequence and halt
  useEffect(() => {
    if (!staticMode) return;
    const items = [];
    for (let i = 1; i <= maxOutputs; i++) {
      const isStop = showStop && i === maxOutputs;
      const word = isStop
        ? '⟨eos⟩'
        : (flowMode
            ? SHORT_ANSWER[(i - 1) % SHORT_ANSWER.length]
            : OUTPUT_POOL[i % OUTPUT_POOL.length]);
      const cols = flowMode ? 5 : 12;
      const col = (i - 1) % cols;
      const row = Math.floor((i - 1) / cols);
      const cellW = flowMode ? 130 : 60;
      const cellH = flowMode ? 56 : 34;
      const x = col * cellW + (flowMode ? 0 : Math.sin(i * 11.3) * 10);
      const y = row * cellH + (flowMode ? 0 : Math.cos(i * 7.7) * 6);
      const rot = flowMode ? 0 : Math.sin(i * 13.1) * 9;
      const palette = ['#f5ebe6', '#fff8ef', '#ede2dc', '#f9f0e8', '#e8dcd1'];
      items.push({ id: i, w: word, isStop, x, y, rot, color: palette[i % palette.length] });
    }
    setOutputs(items);
    setDone(true);
    if (showStop) setEos(true);
  }, [staticMode, resetKey, showStop, maxOutputs, flowMode]);

  // Live tick (non-static modes)
  useEffect(() => {
    if (staticMode || done || eos) return;
    const intervalMs = flowMode ? 240 : 130;
    const id = setInterval(() => {
      if (speed < 0.05) return;
      counterRef.current += 1;
      const i = counterRef.current;
      if (i > maxOutputs) {
        setDone(true);
        return;
      }
      const isStop = showStop && i === maxOutputs;
      const word = isStop
        ? '⟨eos⟩'
        : (flowMode
            ? SHORT_ANSWER[(i - 1) % SHORT_ANSWER.length]
            : OUTPUT_POOL[i % OUTPUT_POOL.length]);
      const cols = flowMode ? 5 : 12;
      const col = (i - 1) % cols;
      const row = Math.floor((i - 1) / cols);
      const cellW = flowMode ? 130 : 60;
      const cellH = flowMode ? 56 : 34;
      const x = col * cellW + (flowMode ? 0 : Math.sin(i * 11.3) * 10);
      const y = row * cellH + (flowMode ? 0 : Math.cos(i * 7.7) * 6);
      const rot = flowMode ? 0 : Math.sin(i * 13.1) * 9;
      const palette = ['#f5ebe6', '#fff8ef', '#ede2dc', '#f9f0e8', '#e8dcd1'];
      setOutputs(prev => [...prev, {
        id: i, w: word, isStop, x, y, rot, color: palette[i % palette.length],
      }]);
      if (isStop) setTimeout(() => setEos(true), 280);
    }, intervalMs / Math.max(0.05, speed));
    return () => clearInterval(id);
  }, [resetKey, speed, eos, done, showStop, maxOutputs, flowMode, staticMode]);

  // Wrapper border is dark/quiet pre-EOS, then bright orange glow post-EOS
  const wrapperBorder = !showWrapper
    ? '1px solid transparent'
    : eos
      ? '6px solid #ffaa6a'
      : '2px solid #2a1f1c';
  const wrapperShadow = !showWrapper
    ? 'none'
    : eos
      ? '0 0 80px rgba(255,170,106,0.85), 0 0 32px rgba(255,170,106,0.6), inset 0 0 60px rgba(255,170,106,0.16)'
      : 'none';
  const wrapperBg = showWrapper ? 'rgba(15,10,9,0.4)' : 'transparent';

  // Role accents for divided input
  const ROLE_COLORS = {
    system:    { bg: '#3a2c2a', label: '#a89888' },
    user:      { bg: '#5a3a2c', label: '#f5d6c0' },
    assistant: { bg: '#3a4a3a', label: '#bfd6c0' },
  };

  // Output area dimensions- match input box height in flow mode so the
  // big-eye overlays land at the same vertical centerline on slide 1.7.
  const outBoxH = flowMode ? 360 : 600;

  return (
    <div style={{
      position: 'relative',
      width: '100%',
      height: '100%',
      minHeight: 600,
      border: wrapperBorder,
      borderRadius: showWrapper ? 14 : 0,
      boxShadow: wrapperShadow,
      transition: 'box-shadow 0.5s, border-color 0.5s, border-width 0.5s',
      background: wrapperBg,
      overflow: 'hidden',
      display: 'flex',
      alignItems: 'center',
      padding: '40px 32px',
      boxSizing: 'border-box',
    }}>
      {/* Wrapper label- sits fully inside the wrapper, top-left, never clipped */}
      {showWrapper && (
        <div style={{
          position: 'absolute', top: 12, left: 16,
          background: '#0a0706',
          padding: '6px 14px',
          fontFamily: 'JetBrains Mono',
          fontSize: 16,
          letterSpacing: '0.22em',
          color: eos ? '#ffffff' : '#d4c4bc',
          textShadow: eos ? '0 0 14px rgba(255,200,160,0.9)' : 'none',
          fontWeight: 700,
          border: eos ? '2px solid #ffaa6a' : '2px solid #4a3835',
          borderRadius: 3,
          transition: 'color 0.4s, text-shadow 0.4s, border-color 0.4s',
          zIndex: 200,
          whiteSpace: 'nowrap',
        }}>
          THE MODEL WRAPPER {eos ? '· ⟨EOS⟩ DETECTED · HALT' : ''}
        </div>
      )}

      {/* === INPUT COLUMN === */}
      <div style={{
        width: 380, flexShrink: 0,
        display: 'flex', flexDirection: 'column', gap: 10, position: 'relative',
      }}>
        <div style={{
          fontFamily: 'JetBrains Mono', fontSize: 11, letterSpacing: '0.2em',
          color: 'var(--text-3)',
        }}>↓ INPUT</div>
        <div style={{
          position: 'relative',
          width: 380, height: 360,
          border: '2px solid #2a1f1c',
          background: '#0d0a09',
          overflow: 'hidden',
        }}>
          {noDividers ? (
            // Slide 1.5: undifferentiated text blob- no role distinction
            <div style={{
              padding: '18px 22px',
              fontFamily: 'Inter Tight',
              fontSize: 17,
              lineHeight: 1.5,
              color: '#f5ebe6',
              opacity: 0.92,
            }}>{BLOB_TEXT}</div>
          ) : (
            // Slide 1.6+: divided sections with role labels
            <div style={{display: 'flex', flexDirection: 'column', height: '100%'}}>
              {INPUT_TURNS.map((t, i) => {
                const c = ROLE_COLORS[t.role] || ROLE_COLORS.user;
                return (
                  <div key={i} style={{
                    flex: 1,
                    padding: '12px 16px',
                    background: c.bg,
                    borderBottom: i < INPUT_TURNS.length - 1 ? '1px solid #1a1413' : 'none',
                    display: 'flex', flexDirection: 'column', gap: 4,
                    color: '#f5ebe6',
                    overflow: 'hidden',
                  }}>
                    <div style={{
                      fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.2em',
                      color: c.label, fontWeight: 600,
                    }}>{t.role.toUpperCase()}</div>
                    <div style={{
                      fontFamily: 'Inter Tight', fontSize: 14, lineHeight: 1.35,
                      color: '#f5ebe6', opacity: 0.92,
                    }}>{t.text}</div>
                  </div>
                );
              })}
            </div>
          )}

          {/* Big eye overlay (slide 1.7)- fades in and out of existence */}
          {bigEyes && (
            <div style={{
              position: 'absolute', inset: 0,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 220,
              filter: 'drop-shadow(0 0 24px rgba(0,0,0,0.7))',
              pointerEvents: 'none',
              zIndex: 20,
              animation: 'eyeBlink 4s ease-in-out infinite',
            }}>👁</div>
          )}
        </div>
      </div>

      {/* ARROW: input → LLM */}
      <div style={{
        flexShrink: 0,
        display: 'flex', alignItems: 'center',
        padding: '0 20px',
        fontFamily: 'JetBrains Mono', fontSize: 32,
        color: '#6b5d57',
      }}>→</div>

      {/* === LLM BOX === */}
      <div style={{flexShrink: 0, display: 'flex', alignItems: 'center'}}>
        <div style={{
          width: 280, height: 200,
          background: '#000',
          border: '2px solid #1a1413',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}>
          <div style={{
            fontFamily: 'JetBrains Mono',
            fontSize: 36,
            color: '#4a3835',
            letterSpacing: '0.4em',
            fontWeight: 700,
          }}>LLM</div>
        </div>
      </div>

      {/* ARROW: LLM → output */}
      <div style={{
        flexShrink: 0,
        display: 'flex', alignItems: 'center',
        padding: '0 20px',
        fontFamily: 'JetBrains Mono', fontSize: 32,
        color: '#6b5d57',
      }}>→</div>

      {/* === OUTPUT COLUMN === */}
      <div style={{
        flex: 1, minWidth: 0,
        display: 'flex', flexDirection: 'column', gap: 10, position: 'relative',
      }}>
        <div style={{
          fontFamily: 'JetBrains Mono', fontSize: 11, letterSpacing: '0.2em',
          color: 'var(--text-3)',
        }}>↓ OUTPUT</div>
        <div style={{
          position: 'relative',
          width: '100%', height: outBoxH,
          overflow: 'hidden',
        }}>
          {outputs.map((o, idx) => (
            <div key={o.id} style={{
              position: 'absolute',
              top: o.y + (flowMode ? 6 : 0),
              left: o.x + (flowMode ? 4 : 0),
              transform: `rotate(${o.rot}deg)`,
              padding: flowMode ? '8px 14px' : '4px 9px',
              background: o.isStop ? '#e85050' : o.color,
              color: o.isStop ? '#fff' : '#3a2a28',
              fontFamily: o.isStop ? 'JetBrains Mono' : 'Inter Tight',
              fontSize: flowMode ? 17 : 12,
              fontWeight: o.isStop ? 700 : 500,
              boxShadow: o.isStop
                ? '0 0 28px rgba(255,170,106,0.95), 0 0 12px rgba(232,80,80,0.7), 0 2px 4px rgba(0,0,0,0.5)'
                : (flowMode
                    ? '0 2px 4px rgba(0,0,0,0.35)'
                    : '0 1px 3px rgba(0,0,0,0.45)'),
              border: '1px solid rgba(0,0,0,0.15)',
              animation: staticMode ? 'none' : `slipIn ${0.3 / Math.max(0.05, speed)}s ease-out`,
              zIndex: idx + 10,
              whiteSpace: 'nowrap',
              letterSpacing: o.isStop ? '0.15em' : 0,
            }}>
              {o.w}
            </div>
          ))}

          {/* Big eye overlay over the output sequence (slide 1.7)- fades in/out, synced to the input eye */}
          {bigEyes && outputs.length > 0 && (
            <div style={{
              position: 'absolute', inset: 0,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 220,
              filter: 'drop-shadow(0 0 24px rgba(0,0,0,0.7))',
              pointerEvents: 'none',
              zIndex: 100,
              animation: 'eyeBlink 4s ease-in-out infinite',
            }}>👁</div>
          )}
        </div>
      </div>

      {/* Counter pill (absolute, bottom-right) */}
      <div style={{
        position: 'absolute', bottom: 14, right: 22,
        fontFamily: 'JetBrains Mono', fontSize: 12, letterSpacing: '0.18em',
        color: eos ? '#ffc28a' : (done ? '#9a8a83' : 'var(--text-3)'),
        background: 'rgba(15,10,9,0.78)', padding: '5px 12px', borderRadius: 3,
        border: '1px solid #3a2c2a',
        zIndex: 50,
      }}>
        {outputs.length} TOKENS{eos ? ' · HALTED ⟨EOS⟩' : (done ? ' · NATURAL STOP' : '')}
      </div>
    </div>
  );
}

// Inject keyframes once
if (!document.getElementById('printer-keyframes')) {
  const s = document.createElement('style');
  s.id = 'printer-keyframes';
  s.textContent = `
    @keyframes slipIn {
      0%   { opacity: 0; transform: translateX(-44px) rotate(0deg) scale(0.6); }
      55%  { opacity: 1; }
      100% { opacity: 1; }
    }
    @keyframes eyeBlink {
      0%, 100% { opacity: 0;    transform: scale(0.65); }
      18%, 82% { opacity: 0.96; transform: scale(1); }
    }
  `;
  document.head.appendChild(s);
}

const Printer = PaperPrinter;
Object.assign(window, { PaperPrinter, Printer });
