// pixel-live.jsx — the LIVE animated pixel "S" for SoftLAN.
// At rest it's the baseline concept-5 S. On hover (and at random idle moments)
// it "comes alive": slithers like the game Snake across the grid then reforms,
// or glitches with an RGB datamosh. Exposes play(trick) via ref.
//
// Grid geometry matches the baseline MarkPixel exactly so the swap is seamless.

const { useState, useEffect, useRef, useImperativeHandle, forwardRef } = React;

// ── shared grid geometry (identical to baseline MarkPixel) ──
const LCELL = 17, LOFF = 7.5, LGAP = 2.4, LR = 3;
const LSIZE = LCELL - LGAP; // 14.6

// the "S" as an ORDERED walk (tail → head). Each step is grid-adjacent.
// head = (0,4) bottom-left, tail = (4,0) top-right.
const SORD = [
  [4,0],[3,0],[2,0],[1,0],[0,0],   // top bar  (R→L)
  [0,1],                            // left drop
  [0,2],[1,2],[2,2],[3,2],[4,2],    // middle bar (L→R)
  [4,3],                            // right drop
  [4,4],[3,4],[2,4],[1,4],[0,4],    // bottom bar (R→L)
];

// boustrophedon sweep of the whole 5×5 board, (0,4) → (4,0) — the Snake "playfield".
const BOUS = (() => {
  const b = [];
  for (let col = 0; col < 5; col++) {
    const rows = col % 2 === 0 ? [4,3,2,1,0] : [0,1,2,3,4];
    rows.forEach(r => b.push([col, r]));
  }
  return b; // 25 cells, b[0]=(0,4), b[24]=(4,0)
})();

// full head path P: lay the S, sweep the board, re-lay the S.
const PATH = (() => {
  const p = SORD.map(c => c.slice());        // 0..16  (full S)
  for (let i = 1; i < BOUS.length; i++) p.push(BOUS[i].slice()); // 17..40
  for (let i = 1; i < SORD.length; i++) p.push(SORD[i].slice()); // 41..56
  return p; // length 57
})();
const I0 = 16, I1 = PATH.length - 1; // animate head index 16 → 56

// snake length schedule: full S → short snake → full S
function lenAt(i) {
  if (i <= 16) return 17;
  if (i <= 26) return Math.round(17 + (7 - 17) * ((i - 16) / 10));
  if (i <= 40) return 7;
  if (i <= 56) return Math.round(7 + (17 - 7) * ((i - 40) / 16));
  return 17;
}

// hex → rgb mixed toward white(t>0)/black(t<0)
function lshade(hex, t) {
  let h = (hex || '#000').replace('#', '');
  if (h.length === 3) h = h.split('').map(x => x + x).join('');
  let r = parseInt(h.slice(0,2),16), g = parseInt(h.slice(2,4),16), b = parseInt(h.slice(4,6),16);
  const tgt = t >= 0 ? 255 : 0, a = Math.min(1, Math.abs(t));
  r = Math.round(r+(tgt-r)*a); g = Math.round(g+(tgt-g)*a); b = Math.round(b+(tgt-b)*a);
  return `rgb(${r},${g},${b})`;
}

function px(col, row) { return { x: LOFF + col * LCELL + LGAP / 2, y: LOFF + row * LCELL + LGAP / 2 }; }
function Cell({ col, row, fill, opacity = 1, dx = 0, dy = 0, w = LSIZE, h = LSIZE, rx = LR }) {
  const { x, y } = px(col, row);
  return <rect x={x + dx} y={y + dy} width={w} height={h} rx={rx} fill={fill} opacity={opacity} />;
}

// ── renderers ──
function renderStatic(accent) {
  return SORD.map(([c, r], i) => <Cell key={i} col={c} row={r} fill={accent} />);
}

function renderSnake(p, accent) {
  const i = Math.min(I1, Math.max(I0, I0 + Math.round(p * (I1 - I0))));
  const L = lenAt(i);
  const start = Math.max(0, i - L + 1);
  const body = PATH.slice(start, i + 1);
  const n = body.length;
  // over the final stretch, ease the per-cell gradient/opacity/head-grow toward
  // the flat, uniform static S so the hand-off to renderStatic is seamless.
  const settle = Math.max(0, Math.min(1, (p - 0.82) / 0.18));
  return body.map((cell, j) => {
    const t = n > 1 ? j / (n - 1) : 1;        // 0 tail → 1 head
    const fill = lshade(accent, (t - 0.45) * 0.5 * (1 - settle)); // head brighter, tail darker
    const opacity = (0.42 + 0.58 * t) + (1 - (0.42 + 0.58 * t)) * settle;
    const isHead = j === n - 1;
    const grow = (isHead ? 1.06 : 1) * (1 - settle) + 1 * settle;
    const s = LSIZE * grow, d = (LSIZE - s) / 2;
    return <Cell key={j} col={cell[0]} row={cell[1]} fill={fill} opacity={opacity} dx={d} dy={d} w={s} h={s} />;
  });
}

const GLX = [11, -7, 13, -5, 9]; // per-row horizontal kick
function renderGlitch(p, accent) {
  const burst = Math.max(0, 1 - p) * (0.45 + 0.55 * Math.abs(Math.sin(p * 26)));
  return SORD.map(([c, r], i) => {
    const dx = GLX[r] * burst;
    const drop = burst > 0.55 && Math.sin(i * 12.9 + p * 50) > 0.82; // occasional dropout
    const flick = 0.7 + 0.3 * Math.abs(Math.sin(p * 70 + i));
    return <Cell key={i} col={c} row={r} fill={accent} opacity={drop ? 0.12 : flick} dx={dx} />;
  });
}

function renderRGB(p, accent) {
  const burst = Math.pow(Math.max(0, 1 - p), 1.25);
  const jit = Math.sin(p * 52) * 0.5 + Math.sin(p * 31) * 0.5;
  const off = (4.5 + 2.5 * jit) * burst;
  const voff = 1.6 * jit * burst;
  const layers = [
    { fill: '#ff2d55', dx: -off, dy: -voff, op: 0.8 },
    { fill: '#21e3a8', dx:  off, dy:  voff, op: 0.8 },
    { fill: accent,    dx: 0,    dy: 0,     op: 1   },
  ];
  return layers.flatMap((ly, li) =>
    SORD.map(([c, r], i) => (
      <g key={li + '-' + i} style={{ mixBlendMode: li < 2 ? 'screen' : 'normal' }}>
        <Cell col={c} row={r} fill={ly.fill} opacity={ly.op} dx={ly.dx} dy={ly.dy} />
      </g>
    ))
  );
}

const RENDER = { snake: renderSnake, glitch: renderGlitch, rgb: renderRGB };
const TRICK_DUR = { snake: 2600, glitch: 1100, rgb: 1350 };
const POOL = ['snake', 'snake', 'snake', 'rgb', 'glitch']; // snake-weighted
function randomTrick() { return POOL[Math.floor(Math.random() * POOL.length)]; }

const LiveLogo = forwardRef(function LiveLogo(
  { accent = '#3b82f6', size = 140, idleAuto = true, idleMin = 5000, idleMax = 11000 }, ref
) {
  const [anim, setAnim] = useState(null); // {trick, p} | null
  const rafRef = useRef(0);
  const playingRef = useRef(false);

  const play = (trick) => {
    if (playingRef.current) return;
    playingRef.current = true;
    const dur = TRICK_DUR[trick] || 2000;
    const t0 = performance.now();
    const step = (now) => {
      const p = (now - t0) / dur;
      if (p >= 1) { setAnim(null); playingRef.current = false; return; }
      setAnim({ trick, p });
      rafRef.current = requestAnimationFrame(step);
    };
    setAnim({ trick, p: 0 });
    rafRef.current = requestAnimationFrame(step);
  };

  useImperativeHandle(ref, () => ({ play, isPlaying: () => playingRef.current }));

  useEffect(() => {
    if (!idleAuto) return;
    let timer;
    const schedule = () => {
      timer = setTimeout(() => {
        if (!document.hidden && !playingRef.current) play(randomTrick());
        schedule();
      }, idleMin + Math.random() * (idleMax - idleMin));
    };
    schedule();
    return () => clearTimeout(timer);
  }, [idleAuto, idleMin, idleMax]);

  useEffect(() => () => cancelAnimationFrame(rafRef.current), []);

  const content = anim ? RENDER[anim.trick](anim.p, accent) : renderStatic(accent);

  return (
    <svg viewBox="0 0 100 100" width={size} height={size} fill="none"
         style={{ cursor: 'pointer', overflow: 'visible' }}
         onMouseEnter={() => play(randomTrick())}
         onClick={() => play('snake')}>
      {content}
    </svg>
  );
});

Object.assign(window, { LiveLogo, renderSnake, renderGlitch, renderRGB, renderStatic });
