// Particle dolphin hero — samples points along a stylized leaping-dolphin
// SVG silhouette and animates them as a flowing wave of points.
// Background-friendly: low contrast, premium glow, subtle drift.

const DolphinHero = ({ accent = '#3FB6FF', density = 1.0, intensity = 1.0 }) => {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    let DPR = Math.min(window.devicePixelRatio || 1, 2);
    let W = 0, H = 0;

    // ---- Dolphin silhouette: composed of body + dorsal fin + pectoral fin + tail flukes ----
    // Designed in an 800x400 frame.
    const DOLPHIN_W = 800, DOLPHIN_H = 400;
    const sampleCanvas = document.createElement('canvas');
    sampleCanvas.width = DOLPHIN_W;
    sampleCanvas.height = DOLPHIN_H;
    const sctx = sampleCanvas.getContext('2d');
    sctx.fillStyle = '#fff';

    // Body — sleek teardrop, beak protruding, melon over head
    const body = new Path2D();
    body.moveTo(760, 220);
    body.bezierCurveTo(740, 200, 715, 184, 685, 175);
    body.bezierCurveTo(660, 168, 640, 162, 615, 158);
    body.bezierCurveTo(560, 152, 490, 152, 410, 156);
    body.bezierCurveTo(330, 162, 240, 174, 160, 188);
    body.bezierCurveTo(125, 194, 100, 198, 88, 202);
    body.lineTo(86, 214);
    body.bezierCurveTo(100, 220, 130, 226, 170, 232);
    body.bezierCurveTo(250, 246, 350, 256, 440, 256);
    body.bezierCurveTo(520, 254, 590, 246, 645, 234);
    body.bezierCurveTo(680, 226, 710, 220, 730, 218);
    body.bezierCurveTo(745, 220, 755, 222, 760, 220);
    body.closePath();
    sctx.fill(body);

    // Dorsal fin — swept back curve
    const dorsal = new Path2D();
    dorsal.moveTo(410, 158);
    dorsal.bezierCurveTo(400, 130, 380, 100, 350, 82);
    dorsal.quadraticCurveTo(335, 72, 326, 86);
    dorsal.bezierCurveTo(315, 110, 312, 138, 320, 158);
    dorsal.closePath();
    sctx.fill(dorsal);

    // Pectoral fin — paddle sweeping down and back
    const pec = new Path2D();
    pec.moveTo(525, 252);
    pec.bezierCurveTo(510, 270, 478, 296, 432, 318);
    pec.quadraticCurveTo(412, 326, 412, 314);
    pec.bezierCurveTo(440, 296, 470, 276, 500, 256);
    pec.closePath();
    sctx.fill(pec);

    // Tail flukes — horizontal Y
    const tail = new Path2D();
    tail.moveTo(95, 202);
    tail.bezierCurveTo(70, 184, 44, 162, 22, 148);
    tail.quadraticCurveTo(10, 152, 18, 168);
    tail.bezierCurveTo(35, 186, 56, 196, 78, 206);
    tail.quadraticCurveTo(86, 210, 78, 216);
    tail.bezierCurveTo(54, 228, 30, 244, 14, 264);
    tail.quadraticCurveTo(8, 276, 22, 274);
    tail.bezierCurveTo(48, 264, 74, 248, 96, 224);
    tail.closePath();
    sctx.fill(tail);

    const imgData = sctx.getImageData(0, 0, DOLPHIN_W, DOLPHIN_H).data;

    // ---- Sample particle targets ----
    // dot spacing (smaller = denser)
    const step = Math.max(3, Math.round(4 / density));
    const targets = [];
    for (let y = 0; y < DOLPHIN_H; y += step) {
      for (let x = 0; x < DOLPHIN_W; x += step) {
        const i = (y * DOLPHIN_W + x) * 4;
        if (imgData[i + 3] > 128) {
          // jitter slightly so the grid doesn't look gridded
          targets.push({
            x: x + (Math.random() - 0.5) * step * 0.9,
            y: y + (Math.random() - 0.5) * step * 0.9,
          });
        }
      }
    }

    // ---- Particles ----
    const particles = targets.map((t, i) => {
      // Each particle has its own scatter vector: where it drifts to when
      // the formation breathes outward. Mix radial-from-center with a random
      // angle so it feels organic, not symmetric.
      const dxC = t.x - DOLPHIN_W * 0.55;
      const dyC = t.y - DOLPHIN_H * 0.5;
      const rad = Math.atan2(dyC, dxC) + (Math.random() - 0.5) * 1.6;
      const scatterDist = 40 + Math.random() * 180;
      return {
        tx: t.x, ty: t.y,
        // start position scattered, biased to the left (incoming wave)
        x: t.x - 200 - Math.random() * 600,
        y: t.y + (Math.random() - 0.5) * 400,
        vx: 0, vy: 0,
        // per-particle phase for breathing motion
        phase: Math.random() * Math.PI * 2,
        speed: 0.5 + Math.random() * 0.9,
        size: 0.7 + Math.random() * 1.3,
        // organic drift offsets (layered noise seeds)
        nx: Math.random() * 1000,
        ny: Math.random() * 1000,
        nFreq: 0.6 + Math.random() * 0.8,
        nAmp: 1.4 + Math.random() * 2.6,
        // scatter direction (when formation breathes out)
        sdx: Math.cos(rad) * scatterDist,
        sdy: Math.sin(rad) * scatterDist * 0.7,
        // individual scatter eagerness — some particles drift far, some barely move
        sBias: Math.pow(Math.random(), 1.5),
        seed: i,
      };
    });

    // Ambient drift particles (background sparkle)
    const ambient = Array.from({ length: 90 }, () => ({
      x: Math.random(), y: Math.random(),
      vx: (Math.random() - 0.5) * 0.0004,
      vy: (Math.random() - 0.5) * 0.0002,
      size: 0.4 + Math.random() * 1.6,
      twinkle: Math.random() * Math.PI * 2,
    }));

    // Wave lines (energy ribbons flowing under dolphin)
    const waves = Array.from({ length: 5 }, (_, i) => ({
      offset: i * 0.15,
      amp: 14 + i * 6,
      freq: 0.004 + i * 0.0005,
      speed: 0.0006 + i * 0.0002,
      thickness: 1 - i * 0.13,
      yBase: 0.62 + i * 0.06,
    }));

    // ---- Resize ----
    const resize = () => {
      DPR = Math.min(window.devicePixelRatio || 1, 2);
      const rect = canvas.parentElement.getBoundingClientRect();
      W = rect.width; H = rect.height;
      canvas.width = W * DPR;
      canvas.height = H * DPR;
      canvas.style.width = W + 'px';
      canvas.style.height = H + 'px';
      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    };
    resize();
    window.addEventListener('resize', resize);

    // Mouse parallax
    const mouse = { x: 0, y: 0, tx: 0, ty: 0 };
    const onMouse = (e) => {
      const rect = canvas.getBoundingClientRect();
      mouse.tx = (e.clientX - rect.left) / rect.width - 0.5;
      mouse.ty = (e.clientY - rect.top) / rect.height - 0.5;
    };
    window.addEventListener('mousemove', onMouse);

    // Scroll tracking — drives the wave flow and a soft dolphin parallax
    const scroll = { y: 0, smoothed: 0, vel: 0, smoothVel: 0 };
    const onScroll = () => {
      const ny = window.scrollY || 0;
      scroll.vel = ny - scroll.y;
      scroll.y = ny;
    };
    window.addEventListener('scroll', onScroll, { passive: true });

    const accentRgb = hexToRgb(accent);
    let t0 = performance.now();

    const tick = (now) => {
      const t = (now - t0) / 1000;
      mouse.x += (mouse.tx - mouse.x) * 0.06;
      mouse.y += (mouse.ty - mouse.y) * 0.06;

      // Smooth scroll values for soft parallax / wave drift
      scroll.smoothed += (scroll.y - scroll.smoothed) * 0.08;
      scroll.smoothVel = scroll.smoothVel * 0.85 + scroll.vel * 0.15;
      scroll.vel *= 0.88; // decay
      const scrollFactor = scroll.smoothed; // px
      const scrollNorm = Math.min(1, scrollFactor / (H * 0.9)); // 0 at top, 1 once hero is out

      // --- Gather / scatter cycle ---
      // Mostly settled, with occasional gentle breath outward.
      // sin^3 shape keeps the value near 0 most of the time, peaking briefly.
      const breathPhase = Math.sin(t * 0.18);                 // slow ~35s cycle
      const scatter = Math.max(0, breathPhase * breathPhase * breathPhase) * 0.55;

      // --- Glow breathing ---
      const glowBreath = 0.78 + 0.22 * Math.sin(t * 0.5);     // ~12s breath

      // Compute dolphin transform: center it, scale to viewport, gentle float
      // Mobile readability fix:
      // - On narrow screens the hero copy sits in the vertical center area.
      // - The dolphin previously used the same center anchor as desktop, so the
      //   glowing body overlapped the intro paragraph ("CLEARVERSE는 기술과...").
      // - Keep the dolphin as the symbolic hero visual, but move it upward and
      //   scale it down slightly on mobile so the text remains readable.
      const compact = W < 640;
      const tablet = W >= 640 && W < 1024;
      const targetW = Math.min(
        W * (compact ? 0.84 : tablet ? 0.88 : 0.92),
        compact ? 640 : tablet ? 900 : 1200
      );
      const scale = targetW / DOLPHIN_W;
      const dolphinH = DOLPHIN_H * scale;
      const cx = W / 2 - (DOLPHIN_W * scale) / 2;
      const verticalLift = compact ? -H * 0.24 : tablet ? -H * 0.10 : -H * 0.04;
      const scrollSink = compact ? scrollFactor * 0.08 : scrollFactor * 0.18;
      // Soft scroll parallax — desktop sinks more, mobile keeps clear of copy
      const cy = H / 2 - dolphinH / 2
        + Math.sin(t * 0.5) * (compact ? 5 : 8)
        + verticalLift
        + scrollSink;

      // ---- Background gradient wash ----
      ctx.clearRect(0, 0, W, H);

      // Soft radial glow behind dolphin (breathing)
      const glowGrad = ctx.createRadialGradient(
        W / 2 + mouse.x * 30, H * 0.55 + mouse.y * 20 + scrollFactor * 0.1, 0,
        W / 2, H * 0.55, Math.max(W, H) * (0.5 + 0.06 * Math.sin(t * 0.4))
      );
      const gAlpha = intensity * glowBreath * (1 - scrollNorm * 0.45);
      glowGrad.addColorStop(0, `rgba(${accentRgb}, ${0.20 * gAlpha})`);
      glowGrad.addColorStop(0.35, `rgba(${accentRgb}, ${0.07 * gAlpha})`);
      glowGrad.addColorStop(1, 'rgba(6, 12, 28, 0)');
      ctx.fillStyle = glowGrad;
      ctx.fillRect(0, 0, W, H);

      // ---- Wave ribbons (behind dolphin) — flow with scroll + time ----
      ctx.save();
      ctx.globalCompositeOperation = 'lighter';
      waves.forEach((w, idx) => {
        ctx.beginPath();
        // Scroll-driven phase: waves visibly stream rightward as user scrolls
        const phase = t * (1 + idx * 0.3) + w.offset * 8 + scrollFactor * 0.008 * (1 + idx * 0.15);
        const yb = H * w.yBase + scrollFactor * 0.05 * (1 + idx * 0.4);
        // Amplitude swells slightly with scroll velocity (feels like the surge of energy)
        const ampMul = 1 + Math.min(0.6, Math.abs(scroll.smoothVel) * 0.012);
        for (let x = -20; x <= W + 20; x += 6) {
          const y =
            yb +
            Math.sin(x * w.freq + phase) * w.amp * ampMul +
            Math.sin(x * w.freq * 2.3 + t * 0.7 + scrollFactor * 0.004) * w.amp * 0.3;
          if (x === -20) ctx.moveTo(x, y);
          else ctx.lineTo(x, y);
        }
        const lineGrad = ctx.createLinearGradient(0, 0, W, 0);
        lineGrad.addColorStop(0, `rgba(${accentRgb}, 0)`);
        lineGrad.addColorStop(0.4, `rgba(${accentRgb}, ${0.22 * w.thickness * intensity * glowBreath})`);
        lineGrad.addColorStop(0.7, `rgba(${accentRgb}, ${0.28 * w.thickness * intensity * glowBreath})`);
        lineGrad.addColorStop(1, `rgba(${accentRgb}, 0)`);
        ctx.strokeStyle = lineGrad;
        ctx.lineWidth = w.thickness * 1.1;
        ctx.stroke();
      });
      ctx.restore();

      // ---- Dolphin particles ----
      ctx.save();
      ctx.globalCompositeOperation = 'lighter';

      // Cheap "noise" — sum of decorrelated sines. Good enough for organic drift.
      const noise = (sx, sy, freq, time) =>
        Math.sin(sx * 0.013 + time * freq) * 0.5 +
        Math.sin(sy * 0.011 + time * freq * 1.3) * 0.5;

      for (let i = 0; i < particles.length; i++) {
        const p = particles[i];

        // Apply gather/scatter to the formation target
        const sEff = scatter * p.sBias;
        const wx = cx + (p.tx + p.sdx * sEff) * scale;
        const wy = cy + (p.ty + p.sdy * sEff) * scale;

        // Spring toward (shifting) target
        const ax = (wx - p.x) * 0.045;
        const ay = (wy - p.y) * 0.045;
        p.vx = (p.vx + ax) * 0.82;
        p.vy = (p.vy + ay) * 0.82;
        p.x += p.vx;
        p.y += p.vy;

        // Organic per-particle drift (curl-like; different freq per axis)
        const nA = noise(p.nx, p.ny, p.nFreq, t);
        const nB = noise(p.nx + 41.7, p.ny + 17.3, p.nFreq * 0.85, t + 0.7);
        const driftX = nA * p.nAmp;
        const driftY = nB * p.nAmp * 0.7;

        // Subtle wave bias (the formation rides the swell)
        const localX = (p.x - cx) / scale;
        const biasX = Math.sin(localX * 0.012 + t * 1.2) * 4 + mouse.x * 14;
        const biasY = Math.cos(localX * 0.01 + t * 0.9) * 3 + mouse.y * 10;

        const drawX = p.x + driftX + biasX * 0.3;
        const drawY = p.y + driftY + biasY * 0.3;

        // Color: mostly accent, with hot highlights toward dorsal/leading edge
        const leading = 1 - p.tx / DOLPHIN_W;
        const hot = 0.5 + leading * 0.5;
        // Per-particle alpha — twinkle + global breath
        const twinkle = 0.55 + 0.45 * Math.sin(t * 1.4 + p.phase);
        const alpha = twinkle * intensity * glowBreath;

        ctx.fillStyle = `rgba(${accentRgb}, ${(0.45 + 0.4 * hot) * alpha})`;
        ctx.beginPath();
        ctx.arc(drawX, drawY, p.size * (1 + hot * 0.6), 0, Math.PI * 2);
        ctx.fill();

        // Occasional white-hot spark — slightly brighter when scattering
        if (p.seed % 47 === 0) {
          ctx.fillStyle = `rgba(220, 240, 255, ${(0.45 + 0.4 * sEff) * intensity})`;
          ctx.beginPath();
          ctx.arc(drawX, drawY, p.size * 0.7, 0, Math.PI * 2);
          ctx.fill();
        }
      }
      ctx.restore();

      // ---- Ambient drift dots ----
      ctx.save();
      ctx.globalCompositeOperation = 'lighter';
      for (const a of ambient) {
        a.x += a.vx; a.y += a.vy;
        if (a.x < 0) a.x += 1; if (a.x > 1) a.x -= 1;
        if (a.y < 0) a.y += 1; if (a.y > 1) a.y -= 1;
        const tw = 0.4 + 0.6 * Math.sin(t * 1.2 + a.twinkle);
        // ambient parallax with scroll — sparks drift slightly downward
        const drawY = ((a.y + scrollFactor * 0.0004) % 1) * H;
        ctx.fillStyle = `rgba(${accentRgb}, ${0.18 * tw * intensity * glowBreath})`;
        ctx.beginPath();
        ctx.arc(a.x * W, drawY, a.size, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.restore();

      // ---- Vignette ----
      const vg = ctx.createRadialGradient(W/2, H/2, Math.min(W,H)*0.3, W/2, H/2, Math.max(W,H)*0.75);
      vg.addColorStop(0, 'rgba(0,0,0,0)');
      vg.addColorStop(1, 'rgba(0,0,0,0.55)');
      ctx.fillStyle = vg;
      ctx.fillRect(0, 0, W, H);

      rafRef.current = requestAnimationFrame(tickSafe);
    };
    const tickSafe = (now) => {
      try { tick(now); }
      catch (e) { console.error('[DolphinHero] tick error', e); }
    };
    rafRef.current = requestAnimationFrame(tickSafe);

    return () => {
      cancelAnimationFrame(rafRef.current);
      window.removeEventListener('resize', resize);
      window.removeEventListener('mousemove', onMouse);
      window.removeEventListener('scroll', onScroll);
    };
  }, [accent, density, intensity]);

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: 'absolute',
        inset: 0,
        width: '100%',
        height: '100%',
        display: 'block',
      }}
    />
  );
};

function hexToRgb(hex) {
  const h = hex.replace('#', '');
  const n = parseInt(h.length === 3 ? h.split('').map(c=>c+c).join('') : h, 16);
  return `${(n>>16)&255}, ${(n>>8)&255}, ${n&255}`;
}

window.DolphinHero = DolphinHero;
window.hexToRgb = hexToRgb;
