// ═══════════════════════════════════════════════════════════════════
//  UsaUsa AI — hero scene engine
//  Renders a 3D wireframe rabbit + moon scene to canvas, then
//  optionally re-samples it into ASCII / point-cloud / pixel modes.
// ═══════════════════════════════════════════════════════════════════

// ───── 3D primitive: rabbit silhouette built from low-poly geometry
// We hand-roll vertices instead of relying on assets, but keep it
// chunky (Stanford-bunny-esque) so it reads as "the famous CG rabbit".

const RABBIT_VERTS = (() => {
  // Body + head + ears + tail, low-poly. Coordinates roughly in unit space.
  // Returns: { v: [[x,y,z],...], e: [[i,j],...] }
  const v = [];
  const e = [];

  const push = (x,y,z) => { v.push([x,y,z]); return v.length - 1; };
  const link = (a,b) => e.push([a,b]);

  // ─ body (egg-ish, 8 sides x 6 rings)
  const bodyRings = [];
  const ringDefs = [
    // [yOffset, radiusX, radiusZ]
    [-0.55, 0.18, 0.14],
    [-0.40, 0.36, 0.28],
    [-0.20, 0.46, 0.36],
    [ 0.00, 0.44, 0.34],
    [ 0.18, 0.36, 0.28],
    [ 0.32, 0.22, 0.18],
  ];
  const SIDES = 10;
  ringDefs.forEach(([y,rx,rz]) => {
    const ring = [];
    for (let i=0;i<SIDES;i++){
      const t = (i / SIDES) * Math.PI*2;
      ring.push(push(Math.cos(t)*rx, y, Math.sin(t)*rz));
    }
    bodyRings.push(ring);
  });
  // connect rings (longitudinals + latitudes)
  for (let r=0;r<bodyRings.length;r++){
    const ring = bodyRings[r];
    for (let i=0;i<SIDES;i++) link(ring[i], ring[(i+1)%SIDES]);
    if (r>0){
      const prev = bodyRings[r-1];
      for (let i=0;i<SIDES;i++) link(ring[i], prev[i]);
    }
  }

  // ─ head (smaller sphere, offset forward+up)
  const headCx = 0.34, headCy = 0.42, headCz = 0;
  const headRings = [];
  const headDefs = [
    [headCy + -0.18, 0.06],
    [headCy + -0.10, 0.16],
    [headCy +  0.00, 0.20],
    [headCy +  0.10, 0.18],
    [headCy +  0.18, 0.10],
  ];
  headDefs.forEach(([y, r]) => {
    const ring = [];
    for (let i=0;i<SIDES;i++){
      const t = (i / SIDES) * Math.PI*2;
      ring.push(push(headCx + Math.cos(t)*r, y, headCz + Math.sin(t)*r));
    }
    headRings.push(ring);
  });
  for (let r=0;r<headRings.length;r++){
    const ring = headRings[r];
    for (let i=0;i<SIDES;i++) link(ring[i], ring[(i+1)%SIDES]);
    if (r>0){
      const prev = headRings[r-1];
      for (let i=0;i<SIDES;i++) link(ring[i], prev[i]);
    }
  }

  // ─ ears: two cylinders going up & slightly back
  const makeEar = (xOff, tiltZ) => {
    const segs = 6;
    const earRings = [];
    for (let s=0;s<=segs;s++){
      const t = s/segs;
      const yy = headCy + 0.18 + t*0.55;
      const xx = headCx + xOff + tiltZ * t * 0.06;
      const r  = 0.045 * (1 - t*0.4);
      const ring = [];
      const SD = 6;
      for (let i=0;i<SD;i++){
        const a = (i / SD) * Math.PI*2;
        ring.push(push(xx + Math.cos(a)*r, yy, Math.sin(a)*r*1.4));
      }
      earRings.push(ring);
    }
    for (let r=0;r<earRings.length;r++){
      const ring = earRings[r];
      const SD = ring.length;
      for (let i=0;i<SD;i++) link(ring[i], ring[(i+1)%SD]);
      if (r>0){
        const prev = earRings[r-1];
        for (let i=0;i<SD;i++) link(ring[i], prev[i]);
      }
    }
  };
  makeEar( 0.06, -0.5);
  makeEar(-0.06, -0.5);

  // ─ tail (small sphere)
  const tailCx = -0.46, tailCy = 0.04;
  const tSegs = 4, tSides = 6;
  const tailRings = [];
  for (let s=0;s<tSegs;s++){
    const tt = s/(tSegs-1);
    const yy = tailCy - 0.05 + tt*0.10;
    const r  = 0.07 * Math.sin(tt*Math.PI);
    const ring = [];
    for (let i=0;i<tSides;i++){
      const a = (i / tSides) * Math.PI*2;
      ring.push(push(tailCx + Math.cos(a)*r, yy, Math.sin(a)*r));
    }
    tailRings.push(ring);
  }
  for (let r=0;r<tailRings.length;r++){
    const ring = tailRings[r];
    for (let i=0;i<tSides;i++) link(ring[i], ring[(i+1)%tSides]);
    if (r>0){
      const prev = tailRings[r-1];
      for (let i=0;i<tSides;i++) link(ring[i], prev[i]);
    }
  }

  return { v, e };
})();

// ─── Moon: icosphere-ish wireframe
const MOON_GEO = (() => {
  const v = [];
  const e = [];
  const RINGS = 12, SIDES = 18;
  const grid = [];
  for (let r=0;r<=RINGS;r++){
    const phi = (r/RINGS) * Math.PI;
    const row = [];
    for (let s=0;s<SIDES;s++){
      const th = (s/SIDES) * Math.PI*2;
      const x = Math.sin(phi)*Math.cos(th);
      const y = Math.cos(phi);
      const z = Math.sin(phi)*Math.sin(th);
      row.push(v.length);
      v.push([x,y,z]);
    }
    grid.push(row);
  }
  for (let r=0;r<=RINGS;r++){
    for (let s=0;s<SIDES;s++){
      e.push([grid[r][s], grid[r][(s+1)%SIDES]]);
      if (r<RINGS) e.push([grid[r][s], grid[r+1][s]]);
    }
  }
  // a few "craters" — small rings on surface
  const craters = [
    [0.3, 0.4, 0.7, 0.18],
    [-0.5, 0.2, 0.6, 0.13],
    [0.1, -0.5, 0.6, 0.15],
    [0.6, -0.1, 0.5, 0.10],
  ];
  craters.forEach(([cx,cy,cz,r]) => {
    const N = 14;
    const start = v.length;
    // build local basis
    const len = Math.hypot(cx,cy,cz);
    const nx=cx/len, ny=cy/len, nz=cz/len;
    let ux=0,uy=1,uz=0;
    if (Math.abs(ny) > 0.9) { ux=1;uy=0;uz=0; }
    // u = normalize(cross(n, up))
    let cxx = ny*uz - nz*uy;
    let cyy = nz*ux - nx*uz;
    let czz = nx*uy - ny*ux;
    const ul = Math.hypot(cxx,cyy,czz);
    cxx/=ul; cyy/=ul; czz/=ul;
    // w = cross(n, u)
    const wx = ny*czz - nz*cyy;
    const wy = nz*cxx - nx*czz;
    const wz = nx*cyy - ny*cxx;
    for (let i=0;i<N;i++){
      const t = (i/N)*Math.PI*2;
      const px = nx + (cxx*Math.cos(t) + wx*Math.sin(t))*r;
      const py = ny + (cyy*Math.cos(t) + wy*Math.sin(t))*r;
      const pz = nz + (czz*Math.cos(t) + wz*Math.sin(t))*r;
      // pull crater inward a touch
      const L = Math.hypot(px,py,pz) * 1.02;
      v.push([px/L, py/L, pz/L]);
    }
    for (let i=0;i<N;i++) e.push([start+i, start+((i+1)%N)]);
  });
  return { v, e };
})();

// ─── Math helpers
function rotY(p, a){ const c=Math.cos(a),s=Math.sin(a); return [p[0]*c+p[2]*s, p[1], -p[0]*s+p[2]*c]; }
function rotX(p, a){ const c=Math.cos(a),s=Math.sin(a); return [p[0], p[1]*c-p[2]*s, p[1]*s+p[2]*c]; }
function rotZ(p, a){ const c=Math.cos(a),s=Math.sin(a); return [p[0]*c-p[1]*s, p[0]*s+p[1]*c, p[2]]; }
function add(a,b){ return [a[0]+b[0], a[1]+b[1], a[2]+b[2]]; }
function scale(a,s){ return [a[0]*s, a[1]*s, a[2]*s]; }

// Project a 3D point to 2D screen with simple perspective
function project(p, W, H, fov=600, camZ=4){
  const z = p[2] + camZ;
  if (z <= 0.05) return null;
  const f = fov / z;
  return [W/2 + p[0]*f, H/2 - p[1]*f, z];
}

// hex / shorthand-hex → rgba string
function hexToRgba(hex, a=1){
  if (!hex) return `rgba(180,195,200,${a})`;
  if (hex.startsWith('rgba') || hex.startsWith('rgb')) return hex;
  let h = hex.replace('#','').trim();
  if (h.length === 3) h = h.split('').map(c => c+c).join('');
  const r = parseInt(h.slice(0,2),16);
  const g = parseInt(h.slice(2,4),16);
  const b = parseInt(h.slice(4,6),16);
  return `rgba(${r},${g},${b},${a})`;
}

// ═══════════════════════════════════════════════════════════════════
// HeroScene React component
// ═══════════════════════════════════════════════════════════════════

function HeroScene({ mode = 'hybrid', speed = 1, density = 1, accent = '#b9e8e0', theme = 'dark' }) {
  const wireRef = React.useRef(null);
  const asciiRef = React.useRef(null);
  const wrapRef = React.useRef(null);
  const startRef = React.useRef(performance.now());
  const rafRef = React.useRef(0);

  React.useEffect(() => {
    const wrap = wrapRef.current;
    const wireCanvas = wireRef.current;
    const asciiEl = asciiRef.current;
    if (!wrap || !wireCanvas) return;

    let W = 0, H = 0, DPR = Math.min(window.devicePixelRatio || 1, 2);
    const ctx = wireCanvas.getContext('2d');

    const resize = () => {
      const r = wrap.getBoundingClientRect();
      W = Math.max(1, Math.floor(r.width));
      H = Math.max(1, Math.floor(r.height));
      wireCanvas.width  = W * DPR;
      wireCanvas.height = H * DPR;
      wireCanvas.style.width  = W + 'px';
      wireCanvas.style.height = H + 'px';
      ctx.setTransform(DPR,0,0,DPR,0,0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(wrap);

    const draw = (now) => {
      const t = (now - startRef.current) / 1000 * speed;

      // ── world transforms
      // Rabbit: arc trajectory toward moon, hopping
      const arcT = (t * 0.18) % 1;       // 0..1 across arc
      const arcA = arcT * Math.PI;       // angle along arc
      const rabbitX = -2.2 + arcT * 4.4;
      const rabbitY = -0.4 + Math.sin(arcA) * 1.3 + Math.sin(t*4)*0.04; // hop bob
      const rabbitZ = Math.sin(arcA*2) * 0.3;
      const rabbitYaw = -0.3 + arcT * 0.6 + Math.sin(t*1.2)*0.05;
      const rabbitTilt = Math.sin(arcA) * -0.25; // lean during arc
      const rabbitScale = 0.95;

      // Moon
      const moonY = 0.85;
      const moonX = 1.7 + Math.sin(t*0.2)*0.04;
      const moonZ = -0.2;
      const moonRot = t * 0.18;
      const moonScale = 0.95;

      // ── transform vertices
      const rPts = RABBIT_VERTS.v.map(p => {
        let q = scale(p, rabbitScale);
        q = rotZ(q, rabbitTilt);
        q = rotY(q, rabbitYaw);
        q = add(q, [rabbitX, rabbitY, rabbitZ]);
        return q;
      });
      const mPts = MOON_GEO.v.map(p => {
        let q = scale(p, moonScale);
        q = rotY(q, moonRot);
        q = rotX(q, 0.4);
        q = add(q, [moonX, moonY, moonZ]);
        return q;
      });

      // ── project
      const fov = Math.min(W, H) * 0.7;
      const camZ = 4.2;
      const rProj = rPts.map(p => project(p, W, H, fov, camZ));
      const mProj = mPts.map(p => project(p, W, H, fov, camZ));

      // ── render
      ctx.clearRect(0,0,W,H);

      // background gradient (very subtle)
      const g = ctx.createRadialGradient(W*0.7, H*0.35, 10, W*0.7, H*0.35, Math.max(W,H));
      g.addColorStop(0, 'rgba(40,46,56,0.35)');
      g.addColorStop(0.4, 'rgba(20,24,30,0.2)');
      g.addColorStop(1, 'rgba(7,8,10,0)');
      ctx.fillStyle = g;
      ctx.fillRect(0,0,W,H);

      // grid floor (faint)
      ctx.strokeStyle = 'rgba(70, 80, 95, 0.08)';
      ctx.lineWidth = 1;
      const horizon = H * 0.62;
      for (let i=-12;i<=12;i++){
        const x1 = W/2 + i * (W*0.05);
        ctx.beginPath();
        ctx.moveTo(W/2, horizon);
        ctx.lineTo(x1 + (i<0?-W:W), H+50);
        ctx.stroke();
      }
      for (let r=1;r<10;r++){
        const yy = horizon + Math.pow(r, 1.6) * 6;
        if (yy > H) break;
        ctx.beginPath();
        ctx.moveTo(0, yy);
        ctx.lineTo(W, yy);
        ctx.stroke();
      }

      const showWire = mode === 'hybrid' || mode === 'wireframe';
      const showPts  = mode === 'pointcloud';
      const showPix  = mode === 'pixel';

      // ── moon
      if (showWire || mode === 'hybrid') {
        // glowy halo behind the moon for atmosphere
        const moonCenter = project([moonX, moonY, moonZ], W, H, fov, camZ);
        if (moonCenter) {
          const radius = fov * moonScale / (moonZ + camZ);
          const halo = ctx.createRadialGradient(
            moonCenter[0], moonCenter[1], radius * 0.4,
            moonCenter[0], moonCenter[1], radius * 2.2
          );
          if (theme === 'light') {
            halo.addColorStop(0, hexToRgba(accent, 0.22));
            halo.addColorStop(0.5, hexToRgba(accent, 0.08));
            halo.addColorStop(1, hexToRgba(accent, 0));
          } else {
            halo.addColorStop(0, hexToRgba(accent, 0.28));
            halo.addColorStop(0.5, hexToRgba(accent, 0.10));
            halo.addColorStop(1, hexToRgba(accent, 0));
          }
          ctx.fillStyle = halo;
          ctx.beginPath();
          ctx.arc(moonCenter[0], moonCenter[1], radius * 2.2, 0, Math.PI*2);
          ctx.fill();
        }

        // moon wires — accent-tinted, brighter on near hemisphere
        ctx.lineWidth = 1;
        ctx.beginPath();
        const farEdges = [];
        for (const [a,b] of MOON_GEO.e){
          const pa = mProj[a], pb = mProj[b];
          if (!pa || !pb) continue;
          const isFar = pa[2] > camZ + 0.5 && pb[2] > camZ + 0.5;
          if (isFar) { farEdges.push([pa,pb]); continue; }
          ctx.moveTo(pa[0], pa[1]);
          ctx.lineTo(pb[0], pb[1]);
        }
        ctx.strokeStyle = hexToRgba(accent, theme === 'light' ? 0.85 : 0.75);
        ctx.stroke();

        // far hemisphere — fainter
        ctx.beginPath();
        for (const [pa, pb] of farEdges) {
          ctx.moveTo(pa[0], pa[1]);
          ctx.lineTo(pb[0], pb[1]);
        }
        ctx.strokeStyle = hexToRgba(accent, theme === 'light' ? 0.25 : 0.18);
        ctx.stroke();
      }

      // ── rabbit
      if (showWire || mode === 'hybrid') {
        if (theme === 'light') {
          ctx.strokeStyle = 'rgba(21, 22, 26, 0.85)';
        } else {
          ctx.strokeStyle = mode === 'hybrid' ? 'rgba(232, 230, 223, 0.9)' : 'rgba(232, 230, 223, 0.95)';
        }
        ctx.lineWidth = 1.2;
        ctx.beginPath();
        for (const [a,b] of RABBIT_VERTS.e){
          const pa = rProj[a], pb = rProj[b];
          if (!pa || !pb) continue;
          ctx.moveTo(pa[0], pa[1]);
          ctx.lineTo(pb[0], pb[1]);
        }
        ctx.stroke();
      }

      // pointcloud mode: just dots
      if (showPts) {
        ctx.fillStyle = accent;
        for (const p of mProj){
          if (!p) continue;
          ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
        }
        ctx.fillStyle = '#e8e6df';
        for (const p of rProj){
          if (!p) continue;
          ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
        }
      }

      // pixel mode: blocky chunks at vertex positions
      if (showPix) {
        const pix = 6;
        ctx.fillStyle = '#cfd6dc';
        for (const p of mProj){
          if (!p) continue;
          const x = Math.round(p[0]/pix)*pix;
          const y = Math.round(p[1]/pix)*pix;
          ctx.fillRect(x, y, pix, pix);
        }
        ctx.fillStyle = '#e8e6df';
        for (const p of rProj){
          if (!p) continue;
          const x = Math.round(p[0]/pix)*pix;
          const y = Math.round(p[1]/pix)*pix;
          ctx.fillRect(x, y, pix, pix);
        }
      }

      // ── ASCII overlay
      // We sample the wire canvas's pixel intensity at a coarse grid and
      // paint characters into a fixed-pitch <pre>.
      if ((mode === 'hybrid' || mode === 'ascii') && asciiEl){
        renderAsciiOverlay(asciiEl, ctx, W, H, density, mode, accent, t);
      } else if (asciiEl) {
        asciiEl.textContent = '';
      }

      rafRef.current = requestAnimationFrame(draw);
    };

    rafRef.current = requestAnimationFrame(draw);
    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
    };
  }, [mode, speed, density, accent]);

  return (
    <div ref={wrapRef} style={{ position:'absolute', inset:0, overflow:'hidden' }}>
      <canvas ref={wireRef} style={{
        position:'absolute', inset:0,
        opacity: mode === 'ascii' ? 0.08 : 1,
        filter: mode === 'hybrid' ? 'blur(0.3px)' : 'none',
      }} />
      <pre ref={asciiRef} style={{
        position:'absolute', inset:0,
        margin: 0,
        fontFamily: 'JetBrains Mono, ui-monospace, monospace',
        fontSize: '10px',
        lineHeight: '10px',
        letterSpacing: '0px',
        color: mode === 'ascii' ? '#e8e6df' : 'rgba(232,230,223,0.55)',
        pointerEvents: 'none',
        whiteSpace: 'pre',
        mixBlendMode: mode === 'hybrid' ? 'screen' : 'normal',
        textShadow: mode === 'ascii' ? `0 0 6px ${accent}33` : 'none',
        // fade the ASCII out where the hero text sits (left side)
        // visible band extended further left so rabbit ASCII shows
        WebkitMaskImage: 'linear-gradient(to right, transparent 0%, transparent 18%, rgba(0,0,0,0.35) 30%, black 48%)',
        maskImage:       'linear-gradient(to right, transparent 0%, transparent 18%, rgba(0,0,0,0.35) 30%, black 48%)',
      }} />
    </div>
  );
}

// Sample the wire canvas to ASCII glyphs
const ASCII_RAMP = ' .·:⋅∙•◦∘○◯◉◎█'.split('');
const ASCII_RAMP_DENSE = ' .,:;-~+=*#%@'.split('');
const ASCII_KATAKANA = ' ｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾝ'.split('');

function renderAsciiOverlay(el, ctx, W, H, density, mode, accent, t){
  const cell = Math.max(6, Math.round(11 / density));
  const cols = Math.floor(W / cell);
  const rows = Math.floor(H / (cell * 1.6));
  if (!cols || !rows) return;

  let img;
  try {
    img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
  } catch(e) { return; }
  const data = img.data;
  const cw = ctx.canvas.width, ch = ctx.canvas.height;

  // pick ramp
  const ramp = mode === 'ascii' ? ASCII_RAMP_DENSE : ASCII_RAMP;

  let out = '';
  for (let r=0; r<rows; r++){
    for (let c=0; c<cols; c++){
      const sx = Math.floor(((c+0.5)/cols) * cw);
      const sy = Math.floor(((r+0.5)/rows) * ch);
      const idx = (sy * cw + sx) * 4;
      const lum = (data[idx] + data[idx+1] + data[idx+2]) / 3 / 255;

      // also add a subtle ambient noise driven by time so empty space shimmers
      const drift = (Math.sin((c*0.21 + r*0.13) + t*0.6) + 1) * 0.5;
      const ambient = drift > 0.93 ? 0.12 : 0;

      const v = Math.min(0.999, lum + ambient);
      if (v < 0.04) { out += ' '; continue; }
      const ch2 = ramp[Math.min(ramp.length-1, Math.floor(v * ramp.length))];
      out += ch2;
    }
    out += '\n';
  }
  el.textContent = out;
}

window.HeroScene = HeroScene;
