// shadows.jsx — 「藤本の影」 4 種類のレンダラ
// 各影は seed(訪問ごとにわずかに変わる) と progress(0..1 スクロール進捗) を受け取る。

const { useEffect, useRef, useMemo } = React;

// ─────────────────────────────────────────────────────────────
// 影 1: 墨 blob ─ SVG turbulence で歪んだ墨滴
// ─────────────────────────────────────────────────────────────
function SumiBlob({ seed, progress }) {
  const id = `sumi-${seed}`;
  // 上から下へ、画面の右上 1/3 付近にふっと落ちる、小さな墨滴
  const cx = 64 + Math.sin(seed) * 5;
  const cy = 28 + Math.cos(seed * 1.3) * 6 + progress * 30;
  const rx = 11 + (seed % 5);
  const ry = 9 + ((seed * 3) % 4);
  const rot = (seed % 60) - 30 + progress * 6;
  const freq = (0.04 + (seed % 17) * 0.0018).toFixed(4);
  return (
    <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice"
         style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <filter id={id} x="-50%" y="-50%" width="200%" height="200%">
          <feTurbulence type="fractalNoise" baseFrequency={freq} numOctaves="3" seed={seed % 1000}/>
          <feDisplacementMap in="SourceGraphic" scale={16 - progress * 3}/>
          <feGaussianBlur stdDeviation="0.25"/>
        </filter>
      </defs>
      <g transform={`rotate(${rot} ${cx} ${cy})`}>
        <ellipse cx={cx} cy={cy} rx={rx} ry={ry}
                 fill="#0A0A0A" filter={`url(#${id})`}
                 opacity={0.85 - progress * 0.2}/>
      </g>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// 影 2: 点群人影 ─ canvas に密度で輪郭が浮かぶ
// ─────────────────────────────────────────────────────────────
function TenGunSilhouette({ seed, progress }) {
  const ref = useRef(null);
  useEffect(() => {
    const c = ref.current;
    if (!c) return;
    const dpr = window.devicePixelRatio || 1;
    const w = c.width = Math.floor(c.clientWidth * dpr);
    const h = c.height = Math.floor(c.clientHeight * dpr);
    const ctx = c.getContext('2d');
    ctx.clearRect(0, 0, w, h);

    let s = seed;
    const rnd = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };

    // ぼんやり人影のかたち ― 画面右寄り、小ぶりに
    const offX = (0.18 + Math.sin(seed) * 0.04) * w;
    const cx = w / 2 + offX;
    const cyHead = h * (0.32 + progress * 0.04);
    const headR = w * 0.075;
    const bodyTop = cyHead + headR * 0.9;
    const bodyW = w * 0.28;
    const bodyH = h * 0.40;

    const inside = (x, y) => {
      const dx = x - cx, dy = y - cyHead;
      if (dx * dx + dy * dy < headR * headR) return 0.95;
      const ty = (y - bodyTop) / bodyH;
      if (ty < 0 || ty > 1) {
        // ぼやけた近傍
        if (ty >= -0.1 && ty < 0) {
          return Math.max(0, 0.4 - Math.abs(x - cx) * 0.005);
        }
        return 0;
      }
      const half = (bodyW / 2) * (0.6 + ty * 0.5 - Math.pow(ty - 0.15, 2) * 1.1);
      const xd = Math.abs(x - cx);
      if (xd < half) return 0.85 - ty * 0.35;
      const edge = Math.exp(-(xd - half) * 0.012);
      return edge * 0.35;
    };

    const N = 1800;
    let placed = 0, tries = 0;
    while (placed < N && tries < N * 4) {
      tries++;
      const x = rnd() * w;
      const y = rnd() * h;
      const d = inside(x, y);
      if (rnd() < d * 0.55) {
        const a = 0.15 + rnd() * 0.4;
        ctx.fillStyle = `rgba(10,10,10,${a})`;
        ctx.fillRect(x, y, 1.6 * dpr, 1.6 * dpr);
        placed++;
      }
    }
  }, [seed, progress]);

  return <canvas ref={ref} style={{ width: '100%', height: '100%', display: 'block' }}/>;
}

// ─────────────────────────────────────────────────────────────
// 影 3: 幾何家紋 ─ 格子の上に円環で家紋を描く
// ─────────────────────────────────────────────────────────────
function KamonGeom({ seed, progress }) {
  const data = useMemo(() => {
    let s = seed;
    const rnd = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
    const rings = [];
    const ringCount = 2 + Math.floor(rnd() * 2);
    for (let k = 0; k < ringCount; k++) {
      rings.push({
        r: 0.05 + k * (0.035 + rnd() * 0.02),
        seg: 5 + Math.floor(rnd() * 5),
        rot: rnd() * Math.PI * 2,
        w: 0.0008 + rnd() * 0.0012,
      });
    }
    const dots = [];
    const grid = 5;
    for (let i = 0; i < grid; i++)
      for (let j = 0; j < grid; j++)
        dots.push([(j + 0.5) / grid, (i + 0.5) / grid]);
    const rays = [];
    const rayCount = 4 + Math.floor(rnd() * 4);
    for (let k = 0; k < rayCount; k++) {
      const a = (k / rayCount) * Math.PI * 2 + rnd() * 0.1;
      rays.push({ a, len: 0.04 + rnd() * 0.06 });
    }
    return { rings, dots, rays };
  }, [seed]);

  const drift = progress * 0.06;
  const fade = 0.55 - progress * 0.1;
  // 画面右上寄りの家紋
  const cx = 0.7, cy = 0.22 + drift;

  return (
    <svg viewBox="0 0 1 1" preserveAspectRatio="xMidYMid meet"
         style={{ width: '100%', height: '100%', display: 'block' }}>
      {data.dots.map((d, i) => {
        const dx = d[0] - cx, dy = d[1] - cy;
        const dist = Math.sqrt(dx*dx + dy*dy);
        if (dist > 0.25) return null;
        return (
          <circle key={i} cx={d[0]} cy={d[1]} r={0.002}
                  fill="#0A0A0A" opacity={0.12}/>
        );
      })}
      {data.rings.map((ring, k) => {
        const pts = [];
        for (let i = 0; i <= ring.seg; i++) {
          const a = ring.rot + (i / ring.seg) * Math.PI * 2;
          pts.push(`${cx + Math.cos(a) * ring.r},${cy + Math.sin(a) * ring.r}`);
        }
        return (
          <polygon key={k} points={pts.join(' ')} fill="none"
                   stroke="#0A0A0A" strokeWidth={ring.w}
                   opacity={fade - k * 0.08}/>
        );
      })}
      {data.rays.map((r, i) => (
        <line key={i} x1={cx} y1={cy}
              x2={cx + Math.cos(r.a) * r.len} y2={cy + Math.sin(r.a) * r.len}
              stroke="#0A0A0A" strokeWidth={0.0016} opacity={fade}/>
      ))}
      <circle cx={cx} cy={cy} r={0.006} fill="#0A0A0A" opacity={fade + 0.1}/>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────
// 影 4: 粒子集合 ─ 10 種の幾何パターンを巡回する自走粒子
// 自動巡回 + スクロールで送り加速 + 形ごとの自転 + 遷移時に散らかる
// ─────────────────────────────────────────────────────────────
function ParticleSwarm({ seed, progress }) {
  const ref = useRef(null);
  const stateRef = useRef(null);

  useEffect(() => {
    const c = ref.current;
    if (!c) return;
    const dpr = window.devicePixelRatio || 1;
    const w = c.width = Math.floor(c.clientWidth * dpr);
    const h = c.height = Math.floor(c.clientHeight * dpr);
    const ctx = c.getContext('2d');

    let s = seed;
    const rnd = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };

    const N = 320;
    const particles = [];
    for (let i = 0; i < N; i++) {
      particles.push({
        x: rnd() * w,
        y: rnd() * h,
        vx: (rnd() - 0.5) * 2,
        vy: (rnd() - 0.5) * 2,
        // 散らばり用の固有方向
        sx: Math.cos(i * 1.7 + seed) * (0.8 + rnd() * 0.6),
        sy: Math.sin(i * 1.7 + seed) * (0.8 + rnd() * 0.6),
        // 永続ワンダー(決して止まらないための個体波動)
        wPhx: rnd() * Math.PI * 2,
        wPhy: rnd() * Math.PI * 2,
        wFx:  0.0006 + rnd() * 0.0012,
        wFy:  0.0006 + rnd() * 0.0012,
        wAmp: 1.8 + rnd() * 2.5,
      });
    }

    // ─── 10 幾何パターン ───
    // 各関数 (i, N, R, t) → [dx, dy] (中心からの相対座標)
    const segLerp = (i, N, sides, t, speed = 0) => {
      const u = ((i / N) + t * speed) % 1;
      const seg = Math.floor(u * sides);
      return { seg, f: u * sides - seg };
    };
    const polygonAt = (k, sides, R, rotOffset = 0) => {
      const a = (k / sides) * Math.PI * 2 - Math.PI / 2 + rotOffset;
      return [R * Math.cos(a), R * Math.sin(a)];
    };
    const lerp = (a, b, f) => a + (b - a) * f;

    const patterns = [
      // 0: 円
      (i, N, R, t) => {
        const a = (i / N) * Math.PI * 2 + t * 0.0004;
        return [Math.cos(a) * R, Math.sin(a) * R];
      },
      // 1: 四角(辺上を均等に)
      (i, N, R, t) => {
        const { seg, f } = segLerp(i, N, 4, t, 0.00003);
        const [x1, y1] = polygonAt(seg, 4, R * 1.05, Math.PI / 4);
        const [x2, y2] = polygonAt(seg + 1, 4, R * 1.05, Math.PI / 4);
        return [lerp(x1, x2, f), lerp(y1, y2, f)];
      },
      // 2: 三角
      (i, N, R, t) => {
        const { seg, f } = segLerp(i, N, 3, t, 0.00003);
        const [x1, y1] = polygonAt(seg, 3, R * 1.12);
        const [x2, y2] = polygonAt(seg + 1, 3, R * 1.12);
        return [lerp(x1, x2, f), lerp(y1, y2, f)];
      },
      // 3: 六角
      (i, N, R, t) => {
        const { seg, f } = segLerp(i, N, 6, t, 0.00003);
        const [x1, y1] = polygonAt(seg, 6, R);
        const [x2, y2] = polygonAt(seg + 1, 6, R);
        return [lerp(x1, x2, f), lerp(y1, y2, f)];
      },
      // 4: 菱形
      (i, N, R, t) => {
        const { seg, f } = segLerp(i, N, 4, t, 0.00003);
        const corners = [[0, -R * 1.1], [R, 0], [0, R * 1.1], [-R, 0]];
        const [x1, y1] = corners[seg];
        const [x2, y2] = corners[(seg + 1) % 4];
        return [lerp(x1, x2, f), lerp(y1, y2, f)];
      },
      // 5: 十字
      (i, N, R, t) => {
        const arm = Math.floor(i / (N / 4));
        const u = ((i % (N / 4)) / (N / 4 - 1)) * 2 - 1;
        const dirs = [[0, -1], [1, 0], [0, 1], [-1, 0]];
        const [dx, dy] = dirs[arm];
        return [dx * R * u + Math.sin(t * 0.0003 + arm) * 2,
                dy * R * u + Math.cos(t * 0.0003 + arm) * 2];
      },
      // 6: 同心二重円
      (i, N, R, t) => {
        const outer = (i % 2) === 0;
        const r = outer ? R : R * 0.5;
        const a = (Math.floor(i / 2) / (N / 2)) * Math.PI * 2
                  + t * (outer ? 0.0004 : -0.0006);
        return [Math.cos(a) * r, Math.sin(a) * r];
      },
      // 7: 螺旋
      (i, N, R, t) => {
        const u = i / N;
        const a = u * Math.PI * 7 + t * 0.0005;
        const r = R * u;
        return [Math.cos(a) * r, Math.sin(a) * r];
      },
      // 8: 五芒星
      (i, N, R, t) => {
        const { seg, f } = segLerp(i, N, 10, t, 0.00002);
        const star = (k) => {
          const isOut = k % 2 === 0;
          const rr = isOut ? R * 1.1 : R * 0.45;
          return polygonAt(k, 10, rr);
        };
        const [x1, y1] = star(seg);
        const [x2, y2] = star((seg + 1) % 10);
        return [lerp(x1, x2, f), lerp(y1, y2, f)];
      },
      // 9: 格子点
      (i, N, R, t) => {
        const cols = 7;
        const rows = Math.ceil(N / cols);
        const col = i % cols;
        const row = Math.floor(i / cols);
        const cell = R * 2 / cols;
        const x = (col - (cols - 1) / 2) * cell;
        const y = (row - (rows - 1) / 2) * cell * 0.9;
        const sw = Math.sin(t * 0.0008 + i * 0.4) * 1.5;
        return [x + sw, y + Math.cos(t * 0.0008 + i * 0.4) * 1.5];
      },
    ];

    const PATTERN_NUM = patterns.length;
    const PERIOD = 7200; // 1 形の保持時間 (ms) ― ゆったり

    // パターンごとの中心・大きさ ― 平面を広く使う
    // 詳しく見ても 0.10–0.80 の範囲になるよう hash で散らす
    const hash = (n) => {
      let x = (n * 374761393 + seed * 668265263) | 0;
      x = ((x ^ (x >>> 13)) * 1274126177) | 0;
      return ((x ^ (x >>> 16)) >>> 0) / 0xffffffff;
    };
    const centerForIndex = (idx) => {
      const fx = 0.20 + hash(idx * 11 + 1) * 0.60; // 0.20–0.80
      const fy = 0.18 + hash(idx * 17 + 3) * 0.60; // 0.18–0.78
      return [w * fx, h * fy];
    };
    const sizeForIndex = (idx) => {
      // 小さめから画面の半分近くまで、スケールもシャッフル
      return Math.min(w, h) * (0.16 + hash(idx * 23 + 7) * 0.26);
    };

    let raf;
    const start = performance.now();

    const loop = (now) => {
      const t = now - start;
      const st = stateRef.current;
      const scrollP = st ? st.progress : 0;
      const scrollV = st ? st.velocity : 0;

      // パターンインデックス: 時間 + スクロール累積
      const cycle = (t / PERIOD) + scrollP * PATTERN_NUM * 0.8;
      const idxA = ((Math.floor(cycle) % PATTERN_NUM) + PATTERN_NUM) % PATTERN_NUM;
      const idxB = (idxA + 1) % PATTERN_NUM;
      const phase = cycle - Math.floor(cycle); // 0..1
      // 前半 45% 保持、後半 55% で遷移
      const k = phase < 0.45 ? 0 : (phase - 0.45) / 0.55;
      const ek = k * k * (3 - 2 * k); // smoothstep
      // 遷移中は大きく散らかる
      const chaos = Math.sin(ek * Math.PI) * 9;

      // パターン A・B の中心とスケールをブレンド
      const [cAx, cAy] = centerForIndex(idxA);
      const [cBx, cBy] = centerForIndex(idxB);
      const Ra = sizeForIndex(idxA);
      const Rb = sizeForIndex(idxB);
      // ホールド中もゆっくりと中心が漂う
      const drift = (a, b) => Math.sin(t * a) * b;
      const cx = lerp(cAx, cBx, ek) + drift(0.00022, w * 0.04);
      const cy = lerp(cAy, cBy, ek) + drift(0.00027, h * 0.03);
      const R  = lerp(Ra, Rb, ek) * (1 + Math.sin(t * 0.0007) * 0.06);

      // スクロールスピードで全体回転
      const spin = scrollV * 0.6 + Math.sin(t * 0.00013) * 0.03;

      // 退色 (前フレームを薄く残す)
      ctx.fillStyle = 'rgba(248,250,251,0.22)';
      ctx.fillRect(0, 0, w, h);

      const cosS = Math.cos(spin), sinS = Math.sin(spin);

      for (let i = 0; i < N; i++) {
        const p = particles[i];
        const [ax, ay] = patterns[idxA](i, N, R, t);
        const [bx, by] = patterns[idxB](i, N, R, t);
        let dx = lerp(ax, bx, ek);
        let dy = lerp(ay, by, ek);
        // 遷移時の散らかり
        dx += p.sx * chaos;
        dy += p.sy * chaos;
        // 全体回転
        const rx = dx * cosS - dy * sinS;
        const ry = dx * sinS + dy * cosS;
        // 個体波動(止まらないための微動、控えめ)
        const wx = Math.sin(t * p.wFx + p.wPhx) * (p.wAmp * 0.55);
        const wy = Math.cos(t * p.wFy + p.wPhy) * (p.wAmp * 0.55);
        const tx = cx + rx + wx;
        const ty = cy + ry + wy;

        const accel = 0.035;
        p.vx += (tx - p.x) * accel;
        p.vy += (ty - p.y) * accel;
        p.vx *= 0.90;
        p.vy *= 0.90;
        // 底速 ― 完全に止まらせない(但し控えめに)
        const sp = Math.hypot(p.vx, p.vy);
        if (sp < 0.2) {
          p.vx += Math.sin(t * 0.001 + i * 0.51) * 0.08;
          p.vy += Math.cos(t * 0.0012 + i * 0.51) * 0.08;
        }
        p.x += p.vx;
        p.y += p.vy;

        ctx.fillStyle = 'rgba(10,10,10,0.58)';
        ctx.fillRect(p.x, p.y, 1.4 * dpr, 1.4 * dpr);
      }

      raf = requestAnimationFrame(loop);
      stateRef.current.raf = raf;
    };

    stateRef.current = { progress, velocity: 0, lastP: progress, lastT: start, raf: 0 };
    raf = requestAnimationFrame(loop);
    stateRef.current.raf = raf;

    return () => cancelAnimationFrame(raf);
  }, [seed]);

  // progress 変化で速度を算出して ref に渡す
  useEffect(() => {
    const st = stateRef.current;
    if (!st) return;
    const now = performance.now();
    const dt = Math.max(1, now - st.lastT);
    const dp = progress - st.lastP;
    // 急減衰する瞬間速度
    st.velocity = dp / (dt / 1000);
    st.progress = progress;
    st.lastP = progress;
    st.lastT = now;
    // 0 に向けて減衰
    setTimeout(() => { if (stateRef.current) stateRef.current.velocity *= 0.5; }, 120);
  }, [progress]);

  return <canvas ref={ref} style={{ width: '100%', height: '100%', display: 'block' }}/>;
}

// ─────────────────────────────────────────────────────────────
// scroll hook
// ─────────────────────────────────────────────────────────────
function useScrollProgress(elRef) {
  const [p, setP] = React.useState(0);
  React.useEffect(() => {
    const el = elRef && elRef.current;
    if (!el) return;
    const onScroll = () => {
      const max = el.scrollHeight - el.clientHeight;
      setP(max > 0 ? Math.min(1, Math.max(0, el.scrollTop / max)) : 0);
    };
    onScroll();
    el.addEventListener('scroll', onScroll, { passive: true });
    return () => el.removeEventListener('scroll', onScroll);
  }, [elRef]);
  return p;
}

window.Shadows = {
  sumi:   { label: '墨 blob',   Component: SumiBlob },
  tengun: { label: '点群人影',  Component: TenGunSilhouette },
  kamon:  { label: '幾何家紋',  Component: KamonGeom },
  swarm:  { label: '粒子集合',  Component: ParticleSwarm },
};
window.useScrollProgress = useScrollProgress;
