/* visuals9.jsx — Inductor: the flywheel.
   A heavy paddlewheel in the pipe. It resists CHANGES in flow: slow to start
   spinning, but once spinning it keeps the water going (momentum). Mirror of
   the capacitor. `drive` = source on/off; the wheel speed lags the drive.   */

/* Hook: wheel "current" chases the target with inductive lag (L sets how slow). */
function useInductorSim({ driveOn, L = 2, running = true }) {
  const [cur, setCur] = React.useState(0);
  const ref = React.useRef(0);
  React.useEffect(() => {
    if (!running) return;
    let raf, last = performance.now();
    const tick = (now) => {
      const dt = Math.min(0.05, (now - last) / 1000); last = now;
      const target = driveOn ? 1 : 0;
      const tau = Math.max(0.15, L / 2.2);          // bigger L = slower change
      ref.current += (target - ref.current) * (1 - Math.exp(-dt / tau));
      setCur(ref.current);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [driveOn, L, running]);
  return cur; // 0..1
}

/* ─── InductorWaterScene ─────────────────────────────────────────────────
   Barrel → pipe with a paddlewheel → outlet. Wheel spin speed = `cur`.
   Spin angle accumulates so it visibly keeps turning.
   ───────────────────────────────────────────────────────────────────────── */
function InductorWaterScene({ voltage = 6, cur = 0, driveOn = true, height = 360, showLabels = true }) {
  const W = 820, H = 460;
  const I = cur * (voltage / 3);
  const period = flowPeriod(Math.max(0.05, I));
  const fill = fillRatio(voltage);

  const barrelCx = 110, barrelTop = 70, barrelBot = 360;
  const rimRx = 62, midRx = 80;
  const pipeY = 270, pipeStart = barrelCx + 60, pipeEnd = W - 80, baseH = 34;
  const wheelCx = (pipeStart + pipeEnd) / 2, wheelR = 46;

  // accumulate spin angle from cur
  const angleRef = React.useRef(0);
  const curRef = React.useRef(cur);
  curRef.current = cur;
  const [angle, setAngle] = React.useState(0);
  React.useEffect(() => {
    let raf, last = performance.now();
    const tick = (now) => {
      const dt = (now - last) / 1000; last = now;
      angleRef.current = (angleRef.current + dt * curRef.current * 320) % 360;
      setAngle(angleRef.current);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={height}
         preserveAspectRatio="xMidYMid meet" style={{ display: "block", maxHeight: height }}>
      {renderBarrel({ cx: barrelCx, top: barrelTop, bot: barrelBot, rimRx, midRx, fill, idSuffix: "iw" })}
      {renderPipe({ pipeStart, pipeEnd: wheelCx - wheelR + 8, pipeY, baseH, constrictions: [],
                    current: I, period, idSuffix: "iw-pre", showOutletCap: false })}
      {renderPipe({ pipeStart: wheelCx + wheelR - 8, pipeEnd, pipeY, baseH, constrictions: [],
                    current: I, period, idSuffix: "iw-post" })}

      {/* paddlewheel */}
      <g transform={`translate(${wheelCx} ${pipeY})`}>
        <circle r={wheelR + 5} fill="none" stroke="var(--rule-strong)" strokeWidth="1.5" strokeDasharray="3 4" />
        <g transform={`rotate(${angle})`} style={{ transition: "none" }}>
          {Array.from({ length: 8 }, (_, i) => (
            <g key={i} transform={`rotate(${i * 45})`}>
              <rect x={-5} y={-wheelR} width={10} height={wheelR * 0.62} rx={2}
                    fill="var(--water-soft)" stroke="var(--ink)" strokeWidth="1.5" />
            </g>
          ))}
          <circle r={12} fill="var(--bg-card)" stroke="var(--ink)" strokeWidth="2.5" />
          <circle r={3.5} fill="var(--ink)" />
        </g>
      </g>

      {I > 0.1 && renderOutletDroplets({ pipeEnd, pipeY, current: I, period })}

      {showLabels && (
        <g fontFamily="IBM Plex Mono, monospace" letterSpacing="0.1em">
          <text x={barrelCx - midRx + 6} y={barrelTop - 22} fontSize="27" fill="var(--water)">
            PRESSURE · {voltage.toFixed(1)}V
          </text>
          <text x={wheelCx} y={pipeY + wheelR + 40} textAnchor="middle" fontSize="23" fill="var(--ink-soft)">
            FLYWHEEL · {cur < 0.12 ? "barely turning" : cur > 0.85 ? "full speed" : "spinning up"}
          </text>
          <text x={wheelCx} y={pipeY - wheelR - 24} textAnchor="middle" fontSize="20.5"
                fill={driveOn ? "var(--water)" : "var(--current)"}>
            {driveOn ? "driving →" : "source off — coasting"}
          </text>
        </g>
      )}
    </svg>
  );
}

/* ─── InductorChart — current vs time, showing the lag ──────────────────── */
function InductorChart({ cur = 0, driveOn = true, height = 360, showLabels = true }) {
  const W = 820, H = 460, x0 = 70, x1 = W - 50, y0 = 60, y1 = H - 70;
  const histRef = React.useRef([]);
  const curRef = React.useRef(cur);
  curRef.current = cur;
  const driveRef = React.useRef(driveOn);
  driveRef.current = driveOn;
  const [, force] = React.useState(0);
  React.useEffect(() => {
    let raf, last = performance.now();
    const tick = (now) => {
      if (now - last > 40) {
        last = now;
        histRef.current.push(curRef.current);
        if (histRef.current.length > 120) histRef.current.shift();
        force(n => n + 1);
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  const h = histRef.current;
  const yFor = (v) => y1 - v * (y1 - y0);
  const pts = h.map((v, i) => `${(x0 + (i / 119) * (x1 - x0)).toFixed(1)},${yFor(v).toFixed(1)}`);
  const d = pts.length ? "M " + pts.join(" L ") : "";

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={height}
         preserveAspectRatio="xMidYMid meet" style={{ display: "block", maxHeight: height }}>
      <rect x={40} y={40} width={W - 80} height={H - 80} rx="10"
            fill="var(--bg-deeper)" stroke="var(--rule-strong)" strokeWidth="1.5" />
      <line x1={x0} y1={y0} x2={x0} y2={y1} stroke="var(--rule-strong)" strokeWidth="1" />
      <line x1={x0} y1={y1} x2={x1} y2={y1} stroke="var(--rule-strong)" strokeWidth="1" />
      {/* target level (dashed) */}
      <line x1={x0} y1={yFor(driveOn ? 1 : 0)} x2={x1} y2={yFor(driveOn ? 1 : 0)}
            stroke="var(--ink-faint)" strokeWidth="1" strokeDasharray="4 4" />
      <path d={d} fill="none" stroke="var(--current)" strokeWidth="2.5" strokeLinejoin="round" />
      {h.length > 0 && <circle cx={x1} cy={yFor(h[h.length - 1])} r="5" fill="var(--current)" />}
      {showLabels && (
        <g fontFamily="IBM Plex Mono, monospace" fontSize="17.5" fill="var(--ink-faint)" letterSpacing="0.1em">
          <text x={x0 - 8} y={yFor(1) + 4} textAnchor="end">max</text>
          <text x={x0 - 8} y={y1 + 4} textAnchor="end">0</text>
          <text x={(x0 + x1) / 2} y={H - 30} textAnchor="middle" fill="var(--current)">
            current lags the source — it can't change instantly
          </text>
          <text x={x1} y={y0 + 2} textAnchor="end">time →</text>
        </g>
      )}
    </svg>
  );
}

Object.assign(window, { useInductorSim, InductorWaterScene, InductorChart });
