// ============================================================
// LED Matrix — animated dot display
// ============================================================
// Renders a grid of "LEDs" that can show scrolling text, images,
// or animated patterns. Honors the brand's mono / 3-bit / 12-bit modes.

const { useState, useEffect, useRef, useMemo, useCallback } = React;

// 5x7 pixel font for scrolling text
const FONT_5x7 = {
  ' ': ['00000','00000','00000','00000','00000','00000','00000'],
  '!': ['00100','00100','00100','00100','00100','00000','00100'],
  '?': ['01110','10001','00010','00100','00100','00000','00100'],
  '.': ['00000','00000','00000','00000','00000','00000','00100'],
  ',': ['00000','00000','00000','00000','00100','00100','01000'],
  ':': ['00000','00100','00000','00000','00100','00000','00000'],
  "'": ['00100','00100','00000','00000','00000','00000','00000'],
  '-': ['00000','00000','00000','01110','00000','00000','00000'],
  '+': ['00000','00000','00100','01110','00100','00000','00000'],
  '*': ['00000','10101','01110','11111','01110','10101','00000'],
  '/': ['00001','00010','00010','00100','01000','01000','10000'],
  '%': ['11001','11010','00100','01011','10011','00000','00000'],
  '0': ['01110','10001','10011','10101','11001','10001','01110'],
  '1': ['00100','01100','00100','00100','00100','00100','01110'],
  '2': ['01110','10001','00001','00010','00100','01000','11111'],
  '3': ['11110','00001','00001','01110','00001','00001','11110'],
  '4': ['00010','00110','01010','10010','11111','00010','00010'],
  '5': ['11111','10000','11110','00001','00001','10001','01110'],
  '6': ['00110','01000','10000','11110','10001','10001','01110'],
  '7': ['11111','00001','00010','00100','01000','01000','01000'],
  '8': ['01110','10001','10001','01110','10001','10001','01110'],
  '9': ['01110','10001','10001','01111','00001','00010','01100'],
  'A': ['01110','10001','10001','10001','11111','10001','10001'],
  'B': ['11110','10001','10001','11110','10001','10001','11110'],
  'C': ['01110','10001','10000','10000','10000','10001','01110'],
  'D': ['11110','10001','10001','10001','10001','10001','11110'],
  'E': ['11111','10000','10000','11110','10000','10000','11111'],
  'F': ['11111','10000','10000','11110','10000','10000','10000'],
  'G': ['01110','10001','10000','10111','10001','10001','01110'],
  'H': ['10001','10001','10001','11111','10001','10001','10001'],
  'I': ['01110','00100','00100','00100','00100','00100','01110'],
  'J': ['00111','00010','00010','00010','00010','10010','01100'],
  'K': ['10001','10010','10100','11000','10100','10010','10001'],
  'L': ['10000','10000','10000','10000','10000','10000','11111'],
  'M': ['10001','11011','10101','10101','10001','10001','10001'],
  'N': ['10001','10001','11001','10101','10011','10001','10001'],
  'O': ['01110','10001','10001','10001','10001','10001','01110'],
  'P': ['11110','10001','10001','11110','10000','10000','10000'],
  'Q': ['01110','10001','10001','10001','10101','10010','01101'],
  'R': ['11110','10001','10001','11110','10100','10010','10001'],
  'S': ['01111','10000','10000','01110','00001','00001','11110'],
  'T': ['11111','00100','00100','00100','00100','00100','00100'],
  'U': ['10001','10001','10001','10001','10001','10001','01110'],
  'V': ['10001','10001','10001','10001','10001','01010','00100'],
  'W': ['10001','10001','10001','10101','10101','10101','01010'],
  'X': ['10001','10001','01010','00100','01010','10001','10001'],
  'Y': ['10001','10001','01010','00100','00100','00100','00100'],
  'Z': ['11111','00001','00010','00100','01000','10000','11111'],
};

function textToBitmap(text, spacing = 1) {
  const upper = text.toUpperCase();
  const cols = [];
  for (const ch of upper) {
    const g = FONT_5x7[ch] || FONT_5x7[' '];
    for (let c = 0; c < 5; c++) {
      const col = [];
      for (let r = 0; r < 7; r++) col.push(g[r][c] === '1' ? 1 : 0);
      cols.push(col);
    }
    for (let s = 0; s < spacing; s++) cols.push([0,0,0,0,0,0,0]);
  }
  return cols; // array of columns, each of 7 rows
}

// Quantize a hex color into 3-bit (8 colors) or 12-bit palette
function quantizeColor(hex, mode) {
  if (mode === 'mono') return '#E13F92';
  if (!hex) return '#E13F92';
  const r = parseInt(hex.slice(1,3), 16);
  const g = parseInt(hex.slice(3,5), 16);
  const b = parseInt(hex.slice(5,7), 16);
  if (mode === '3bit') {
    const rq = r > 127 ? 255 : 0;
    const gq = g > 127 ? 255 : 0;
    const bq = b > 127 ? 255 : 0;
    return `rgb(${rq},${gq},${bq})`;
  }
  if (mode === '12bit') {
    const q = (v) => Math.round(v / 17) * 17;
    return `rgb(${q(r)},${q(g)},${q(b)})`;
  }
  return hex;
}

// Hue cycle palette for rainbow effects
function hueColor(t, mode) {
  const hue = (t * 360) % 360;
  const sat = 100, light = 55;
  // hsl -> hex approx via canvas? Cheaper to use hsl directly and let quantize handle it.
  const c = (1 - Math.abs(2 * light/100 - 1)) * sat/100;
  const x = c * (1 - Math.abs(((hue/60) % 2) - 1));
  const m = light/100 - c/2;
  let r=0,g=0,b=0;
  if (hue < 60) { r=c; g=x; }
  else if (hue < 120) { r=x; g=c; }
  else if (hue < 180) { g=c; b=x; }
  else if (hue < 240) { g=x; b=c; }
  else if (hue < 300) { r=x; b=c; }
  else { r=c; b=x; }
  const hex = '#' + [r+m, g+m, b+m].map(v => Math.round(v*255).toString(16).padStart(2,'0')).join('');
  return quantizeColor(hex, mode);
}

// =============================================================
// LEDMatrix component
// Props:
//   rows, cols      grid dimensions
//   pitch           pixel pitch in CSS px (dot size + gap)
//   mode            'mono' | '3bit' | '12bit'
//   content         { kind: 'text', text, color, scroll?: bool, speed? }
//                  | { kind: 'rainbow' }
//                  | { kind: 'pulse', color }
//                  | { kind: 'plasma' }
//   className       optional
// =============================================================
function LEDMatrix({ rows = 16, cols = 96, pitch = 8, mode = '12bit', content, className = '', style }) {
  const [frame, setFrame] = useState(0);
  const raf = useRef(null);
  const last = useRef(0);

  useEffect(() => {
    let mounted = true;
    const tick = (t) => {
      if (!mounted) return;
      if (t - last.current > 60) {
        last.current = t;
        setFrame(f => (f + 1) % 100000);
      }
      raf.current = requestAnimationFrame(tick);
    };
    raf.current = requestAnimationFrame(tick);
    return () => { mounted = false; cancelAnimationFrame(raf.current); };
  }, []);

  // Compute on/off + color per cell
  const cells = useMemo(() => {
    const out = [];
    for (let r = 0; r < rows; r++) {
      for (let c = 0; c < cols; c++) {
        let on = false, color = '#000';
        if (!content) { on = false; }
        else if (content.kind === 'text') {
          const text = content.text || 'VIVIDPIX';
          const bitmap = textToBitmap(text, 2);
          const speed = content.speed || 1;
          const totalWidth = bitmap.length + cols;
          const offset = content.scroll === false ? 0 : Math.floor((frame * speed) % totalWidth) - cols;
          const charRowOffset = Math.floor((rows - 7) / 2);
          const localR = r - charRowOffset;
          const localC = c + offset;
          if (localR >= 0 && localR < 7 && localC >= 0 && localC < bitmap.length) {
            if (bitmap[localC][localR]) {
              on = true;
              color = quantizeColor(content.color || '#E13F92', mode);
            }
          }
        }
        else if (content.kind === 'rainbow') {
          on = true;
          const t = (frame * 0.01) + (c / cols) + (r / rows * 0.3);
          color = hueColor(t, mode);
        }
        else if (content.kind === 'pulse') {
          const cx = cols / 2, cy = rows / 2;
          const d = Math.sqrt((c - cx) ** 2 + (r - cy) ** 2);
          const wave = Math.sin(d * 0.5 - frame * 0.15);
          if (wave > 0.2) {
            on = true;
            color = quantizeColor(content.color || '#C813AC', mode);
          }
        }
        else if (content.kind === 'plasma') {
          const t = frame * 0.05;
          const v = Math.sin(c * 0.2 + t) + Math.sin(r * 0.3 + t * 1.3) + Math.sin((c + r) * 0.15 + t * 0.7);
          if (v > -0.5) {
            on = true;
            const hue = (v + 3) / 6;
            color = hueColor(hue, mode);
          }
        }
        else if (content.kind === 'gif-stripes') {
          // colorful holi-like stripes that drift
          on = true;
          const hue = ((c + frame * 0.3) / cols + Math.sin(r * 0.3) * 0.1) % 1;
          color = hueColor(hue < 0 ? hue + 1 : hue, mode);
        }
        out.push({ on, color });
      }
    }
    return out;
  }, [rows, cols, frame, content, mode]);

  const dotSize = pitch * 0.62;
  return (
    <div
      className={`led-matrix ${className}`}
      style={{
        gridTemplateColumns: `repeat(${cols}, ${pitch}px)`,
        gridTemplateRows: `repeat(${rows}, ${pitch}px)`,
        gap: 0,
        ...style,
      }}
    >
      {cells.map((cell, i) => (
        <div
          key={i}
          style={{
            width: pitch, height: pitch,
            display: 'grid', placeItems: 'center',
          }}
        >
          <div
            style={{
              width: dotSize, height: dotSize,
              borderRadius: '50%',
              background: cell.on ? cell.color : '#0d0d0d',
              color: cell.on ? cell.color : 'transparent',
              boxShadow: cell.on ? `0 0 ${dotSize * 0.5}px ${cell.color}` : 'none',
              transition: 'background 60ms linear',
            }}
          />
        </div>
      ))}
    </div>
  );
}

// =============================================================
// LEDDisplay — lightweight bezel wrapping a static/animated image.
// Use this in most places instead of LEDMatrix to keep the page light.
// Props:
//   src        image path (typically a .gif)
//   mode       'mono' | '3bit' | '12bit' (cosmetic CSS filter)
//   className, style
// =============================================================
function LEDDisplay({ src, mode = '12bit', alt = '', className = '', style }) {
  const modeClass = mode === 'mono' ? 'mono' : mode === '3bit' ? 'bit3' : '';
  return (
    <div className={`led-bezel ${modeClass} ${className}`} style={style}>
      <img src={src} alt={alt}/>
    </div>
  );
}

Object.assign(window, { LEDMatrix, LEDDisplay, textToBitmap, quantizeColor, hueColor });
