/* AsciiPortrait.jsx — el retrato en ASCII vivo (dot-matrix distorsionado a
   caracteres). Muestrea una versión pequeña en grises del rostro y lo dibuja
   con una rampa de glifos ASCII en cream. Animación en loop para llamar la
   atención: un barrido de escaneo que recorre el rostro (los glifos suben de
   densidad a su paso), micro-parpadeo tipo terminal y una leve onda de
   distorsión — vivo pero legible. */
(function () {
  function AsciiPortrait() {
    const cvRef = React.useRef(null);

    React.useEffect(() => {
      const cv = cvRef.current;
      if (!cv) return;
      const ctx = cv.getContext('2d');
      const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      const CREAM = '245,244,239';
      const RAMP = ' .,:-=+*oa#%@';   // bajo → alto (espacio = vacío)

      // ── carga + muestreo del rostro (una vez) ──
      let grid = null, gCols = 0, gRows = 0, ready = false;
      const img = new Image();
      img.onload = function () {
        // celdas ASCII: el carácter es ~2x más alto que ancho → corregimos
        gCols = 96;
        const cellAspect = 2.05;
        gRows = Math.round(gCols * (img.height / img.width) / cellAspect);
        const sc = document.createElement('canvas');
        sc.width = gCols; sc.height = gRows;
        const sx = sc.getContext('2d');
        sx.drawImage(img, 0, 0, gCols, gRows);
        const d = sx.getImageData(0, 0, gCols, gRows).data;
        grid = new Float32Array(gCols * gRows);
        for (let i = 0; i < gCols * gRows; i++) grid[i] = d[i * 4] / 255;   // ya es gris
        ready = true;
      };
      img.src = 'assets/founder-src.png';

      let W = 0, H = 0, dpr = 1;
      function resize() {
        dpr = Math.min(2, window.devicePixelRatio || 1);
        const r = cv.getBoundingClientRect();
        W = cv.width = Math.max(1, Math.round(r.width * dpr));
        H = cv.height = Math.max(1, Math.round(r.height * dpr));
      }
      resize();

      let raf = 0, t0 = performance.now();
      function frame(now) {
        const t = (now - t0) / 1000;
        ctx.clearRect(0, 0, W, H);
        if (ready && grid) {
          const cw = W / gCols, ch = H / gRows;
          ctx.font = (ch * 1.04).toFixed(1) + 'px ui-monospace, "SF Mono", Menlo, monospace';
          ctx.textBaseline = 'top';
          ctx.textAlign = 'left';

          // barrido de escaneo en loop (banda que recorre el rostro)
          const scan = reduce ? -9 : ((t * 0.42) % 1.35 - 0.18) * gRows;

          for (let r = 0; r < gRows; r++) {
            // leve onda de distorsión horizontal (dot-matrix distorsionado)
            const wave = reduce ? 0 : Math.sin(t * 1.1 + r * 0.5) * cw * 0.22;
            for (let c = 0; c < gCols; c++) {
              let v = grid[r * gCols + c];
              if (v < 0.06) continue;                       // fondo: vacío
              // realce del barrido
              const dist = Math.abs(r - scan);
              const boost = dist < 2.2 ? (1 - dist / 2.2) * 0.5 : 0;
              // micro-parpadeo tipo terminal
              const flick = reduce ? 0 : (Math.sin(t * 9 + r * 7.3 + c * 3.1) * 0.05);
              let vv = Math.min(1, v + boost + flick);
              const ramp = Math.pow(vv, 0.85);
              const idx = Math.max(0, Math.min(RAMP.length - 1, Math.round(ramp * (RAMP.length - 1))));
              const ch2 = RAMP[idx];
              if (ch2 === ' ') continue;
              const a = Math.min(1, 0.32 + vv * 0.78);
              ctx.fillStyle = 'rgba(' + CREAM + ',' + a.toFixed(3) + ')';
              ctx.fillText(ch2, c * cw + wave, r * ch);
              // glow sutil en la línea de escaneo
              if (boost > 0.18) {
                ctx.fillStyle = 'rgba(' + CREAM + ',' + (boost * 0.5).toFixed(3) + ')';
                ctx.fillText(ch2, c * cw + wave, r * ch);
              }
            }
          }
        }
        raf = requestAnimationFrame(frame);
      }
      raf = requestAnimationFrame(frame);

      let rt;
      const onResize = () => { clearTimeout(rt); rt = setTimeout(resize, 150); };
      window.addEventListener('resize', onResize);

      return () => {
        cancelAnimationFrame(raf);
        window.removeEventListener('resize', onResize);
      };
    }, []);

    return React.createElement('canvas', { className: 'person-ascii person-slot', ref: cvRef, 'aria-label': 'Rubén — retrato en ASCII' });
  }

  window.AsciiPortrait = AsciiPortrait;
})();
