/* vectorfieldlab.jsx — VectorFieldTutor: the vector-field divergence & curl bench. Built on
   window.BenchKit (harness) + window.BenchFields (shared field math), so this file is just the
   FIGURE. computeFields = window.BenchFields.vectorfield — the SAME function
   server/benches/vectorfield.js calls, so client and server fields are identical by construction.
   The learner dials the source α, the rotation β and a probe point (x,y) for F = (αx−βy, βx+αy);
   a grid of arrows shows the field, the probe point its sampled vector, and the divergence 2α and
   curl 2β are read directly off the readout. */
(function () {
  const { useState, useMemo, useEffect } = React;
  const K = window.BenchKit, C = K.C, SVG_BG = K.SVG_BG, fmt = K.fmt;
  const TaskStrip = K.TaskStrip, StatMeter = K.StatMeter;
  const compute = (s) => window.BenchFields.vectorfield(s);

  function Slider({ label, value, set, min, max, step, unit, color, onUp }) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 9 }}>
        <span style={{ fontSize: 12, color: C.mute, width: 116 }}><K.T>{label}</K.T></span>
        <input type="range" min={min} max={max} step={step} value={value} onChange={(e) => set(parseFloat(e.target.value))} onPointerUp={onUp} style={{ flex: 1 }} />
        <span style={{ color: color || C.amber, width: 64, textAlign: "right" }}>{value}{unit ? ` ${unit}` : ""}</span>
      </div>
    );
  }

  function VectorFieldFig({ task, setTask, tasks, report, event, done }) {
    const [alpha, setAlpha] = useState(1);
    const [beta, setBeta] = useState(1);
    const [x, setX] = useState(1);
    const [y, setY] = useState(1);
    const fld = useMemo(() => compute({ alpha, beta, x, y }), [alpha, beta, x, y]);
    useEffect(() => { report(fld); }, [alpha, beta, x, y]); // eslint-disable-line

    // grid of arrows over [-3,3]² on the left half; world→screen
    const cx0 = 150, cy0 = 150, S = 40;          // origin px + px-per-unit
    const sx = (wx) => cx0 + wx * S, sy = (wy) => cy0 - wy * S;
    // sample F=(αx−βy, βx+αy) on a 7×7 grid; normalize lengths so they don't overflow
    const coords = [-3, -2, -1, 0, 1, 2, 3];
    let maxMag = 0.0001;
    const samples = [];
    coords.forEach((gy) => coords.forEach((gx) => {
      const vx = alpha * gx - beta * gy, vy = beta * gx + alpha * gy;
      const m = Math.hypot(vx, vy);
      if (m > maxMag) maxMag = m;
      samples.push({ gx, gy, vx, vy, m });
    }));
    const ARROW = 17; // max arrow length in px
    const arrows = samples.map((s, i) => {
      if (s.m < 1e-6) return null;
      const k = (ARROW / maxMag);
      const x1 = sx(s.gx), y1 = sy(s.gy);
      const x2 = x1 + s.vx * k, y2 = y1 - s.vy * k;
      return <line key={i} x1={x1} y1={y1} x2={x2} y2={y2} stroke={C.line} strokeWidth="1.5" markerEnd="url(#vf-a)" />;
    });
    // probe point + its actual vector (normalized to the same scale)
    const px = sx(x), py = sy(y);
    const pk = (ARROW * 1.5 / maxMag);
    const pgx = px + fld.fx * pk, pgy = py - fld.fy * pk;

    const t = tasks.find((q) => q.id === task) || tasks[0];
    const srcLabel = fld.divergence > 0.05 ? "source (outflow)" : fld.divergence < -0.05 ? "sink (inflow)" : "divergence-free";
    const rotLabel = fld.curl > 0.05 ? "CCW rotation" : fld.curl < -0.05 ? "CW rotation" : "irrotational";

    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <svg viewBox="0 0 600 300" style={{ width: "100%", background: SVG_BG, border: `1px solid ${C.line}`, borderRadius: 8 }}>
          <defs>
            <marker id="vf-a" markerWidth="8" markerHeight="8" refX="5" refY="2.5" orient="auto"><path d="M0,0 L5,2.5 L0,5 Z" fill={C.line} /></marker>
            <marker id="vf-p" markerWidth="9" markerHeight="9" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" fill={C.gold} /></marker>
          </defs>
          {/* axes */}
          <line x1={cx0 - 3.3 * S} y1={cy0} x2={cx0 + 3.3 * S} y2={cy0} stroke={C.faint} strokeWidth="1" />
          <line x1={cx0} y1={cy0 - 3.3 * S} x2={cx0} y2={cy0 + 3.3 * S} stroke={C.faint} strokeWidth="1" />
          {/* field of arrows */}
          {arrows}
          {/* probe point + its vector */}
          <line x1={px} y1={py} x2={pgx} y2={pgy} stroke={C.gold} strokeWidth="2.5" markerEnd="url(#vf-p)" />
          <circle cx={px} cy={py} r="4.5" fill={C.gold} stroke={C.bg} strokeWidth="1" />
          {/* readout panel */}
          <text x="350" y="40" fill={C.mute} fontSize="12" fontFamily="monospace">F = (αx − βy, βx + αy)</text>
          <text x="350" y="66" fill={C.blue} fontSize="13" fontFamily="monospace">∇·F = 2α = {fmt(fld.divergence)}</text>
          <text x="350" y="84" fill={C.mute} fontSize="11" fontFamily="monospace">{srcLabel}</text>
          <text x="350" y="110" fill={C.teal} fontSize="13" fontFamily="monospace">(∇×F)_z = 2β = {fmt(fld.curl)}</text>
          <text x="350" y="128" fill={C.mute} fontSize="11" fontFamily="monospace">{rotLabel}</text>
          <text x="350" y="154" fill={C.gold} fontSize="13" fontFamily="monospace">F(probe) = ({fmt(fld.fx)}, {fmt(fld.fy)})</text>
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${(fld.isSolenoidal || fld.isIrrotational) ? C.teal : C.line}`, color: (fld.isSolenoidal || fld.isIrrotational) ? C.teal : C.mute, fontSize: 12.5 }}>
          Divergence {fmt(fld.divergence)}, curl {fmt(fld.curl)}. {fld.isSolenoidal && fld.isIrrotational ? <K.T>Both divergence-free and curl-free — a trivial field.</K.T> : fld.isSolenoidal ? <><K.T>Solenoidal: divergence-free (α=0)</K.T>{fld.curl !== 0 ? <K.T> — a pure vortex.</K.T> : null}</> : fld.isIrrotational ? <><K.T>Irrotational: curl-free (β=0)</K.T>{fld.divergence !== 0 ? <K.T> — a pure source.</K.T> : null}</> : <K.T>A blend of source (α) and rotation (β). Zero α for solenoidal, zero β for irrotational.</K.T>}
        </div>
        <Slider label="source α" value={alpha} set={setAlpha} min={-2} max={2} step={0.5} unit="" color={C.blue} onUp={() => event("adjusted", `Set α = ${fmt(alpha)}`, { response: `divergence ${fmt(fld.divergence)}` }, C.blue)} />
        <Slider label="rotation β" value={beta} set={setBeta} min={-2} max={2} step={0.5} unit="" color={C.teal} onUp={() => event("adjusted", `Set β = ${fmt(beta)}`, { response: `curl ${fmt(fld.curl)}` }, C.teal)} />
        <Slider label="probe x" value={x} set={setX} min={-3} max={3} step={0.5} unit="" color={C.gold} onUp={() => event("adjusted", `Set probe x = ${fmt(x)}`, { response: `fx ${fmt(fld.fx)}` }, C.gold)} />
        <Slider label="probe y" value={y} set={setY} min={-3} max={3} step={0.5} unit="" color={C.gold} onUp={() => event("adjusted", `Set probe y = ${fmt(y)}`, { response: `fy ${fmt(fld.fy)}` }, C.gold)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="∇·F" v={fld.divergence} d={2} color={C.blue} />
          <StatMeter label="(∇×F)_z" v={fld.curl} d={2} color={C.teal} />
          <StatMeter label="fx" v={fld.fx} d={2} color={C.gold} />
          <StatMeter label="fy" v={fld.fy} d={2} color={C.gold} />
        </div>
      </>
    );
  }

  window.VectorFieldTutor = K.makeTutor(VectorFieldFig, { moduleLabel: "Vector Fields bench", benchId: "vectorfield" });
})();
