/* tripleintegrallab.jsx — TripleIntegralTutor: the triple integrals bench, in TRUE 3-D. Built on
   window.BenchKit (harness + 3-D SVG toolkit) + window.BenchFields (shared field math), so this file is
   just the FIGURE. computeFields = window.BenchFields.tripleintegral — the SAME function
   server/benches/tripleintegral.js calls, so client and server fields are identical by construction.
   The learner sizes the box [0,X]×[0,Y]×[0,Z] and the grid resolution n; a drag-to-rotate solid box,
   whose six outer faces are each tessellated into an n×n grid and shaded by f = x²+y²+z² at the cell
   midpoint, partitions the volume into the n×n×n midpoint Riemann sum and converges onto the exact
   triple integral (X³YZ + XY³Z + XYZ³)/3. 3-D method: docs/samples/triple-integral-svg.html. */
(function () {
  const { useState, useMemo, useEffect } = React;
  const K = window.BenchKit, C = K.C, fmt = K.fmt, V3 = K.V3;
  const TaskStrip = K.TaskStrip, StatMeter = K.StatMeter, Scene3D = K.Scene3D;
  const compute = (s) => window.BenchFields.tripleintegral(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 TripleIntegralFig({ task, setTask, tasks, report, event, done }) {
    const [X, setX] = useState(2);
    const [Y, setY] = useState(2);
    const [Z, setZ] = useState(2);
    const [n, setN] = useState(4);
    const [az, setAz] = useState(40);
    const [el, setEl] = useState(26);
    const fld = useMemo(() => compute({ X, Y, Z, n }), [X, Y, Z, n]);
    useEffect(() => { report(fld); }, [X, Y, Z, n]); // eslint-disable-line

    // TRUE 3-D solid box [0,X]×[0,Y]×[0,Z] partitioned into an n×n×n grid. To stay fast (n up to 24 →
    // up to 13824 voxels) we render only the box's OUTER SHELL: each of the 6 faces is tessellated into
    // an ni×ni grid of quads, each quad coloured by f = x²+y²+z² at that cell's 3-D midpoint. The shell
    // subdivides more finely as n grows; depth-sorting hides the back faces.
    const ni = Math.max(1, Math.round(n));
    const dx = X / ni, dy = Y / ni, dz = Z / ni;
    const fmax = X * X + Y * Y + Z * Z || 1;
    const cf = (fv, nrm) => V3.shade(V3.colormap(fv / fmax), nrm); // cell colour

    // world bounding-box corners → framed by the projector
    const fit = [];
    for (const xx of [0, X]) for (const yy of [0, Y]) for (const zz of [0, Z]) fit.push([xx, yy, zz]);

    const build = (screen) => {
      const faces = [];
      // top z=Z (n[0,0,1]) and bottom z=0 (n[0,0,-1]): grid over X×Y
      for (let i = 0; i < ni; i++) for (let j = 0; j < ni; j++) {
        const x0 = i * dx, x1 = (i + 1) * dx, y0 = j * dy, y1 = (j + 1) * dy;
        const mx = (i + 0.5) * dx, my = (j + 0.5) * dy;
        const ftop = mx * mx + my * my + Z * Z;
        faces.push(K.face3d(screen, [[x0, y0, Z], [x1, y0, Z], [x1, y1, Z], [x0, y1, Z]], cf(ftop, [0, 0, 1])));
        const fbot = mx * mx + my * my; // z=0
        faces.push(K.face3d(screen, [[x0, y0, 0], [x1, y0, 0], [x1, y1, 0], [x0, y1, 0]], cf(fbot, [0, 0, -1])));
      }
      // right x=X (n[1,0,0]) and left x=0 (n[-1,0,0]): grid over Y×Z
      for (let j = 0; j < ni; j++) for (let k = 0; k < ni; k++) {
        const y0 = j * dy, y1 = (j + 1) * dy, z0 = k * dz, z1 = (k + 1) * dz;
        const my = (j + 0.5) * dy, mz = (k + 0.5) * dz;
        const fr = X * X + my * my + mz * mz;
        faces.push(K.face3d(screen, [[X, y0, z0], [X, y1, z0], [X, y1, z1], [X, y0, z1]], cf(fr, [1, 0, 0])));
        const fl = my * my + mz * mz; // x=0
        faces.push(K.face3d(screen, [[0, y0, z0], [0, y1, z0], [0, y1, z1], [0, y0, z1]], cf(fl, [-1, 0, 0])));
      }
      // back y=Y (n[0,1,0]) and front y=0 (n[0,-1,0]): grid over X×Z
      for (let i = 0; i < ni; i++) for (let k = 0; k < ni; k++) {
        const x0 = i * dx, x1 = (i + 1) * dx, z0 = k * dz, z1 = (k + 1) * dz;
        const mx = (i + 0.5) * dx, mz = (k + 0.5) * dz;
        const fb = mx * mx + Y * Y + mz * mz;
        faces.push(K.face3d(screen, [[x0, Y, z0], [x1, Y, z0], [x1, Y, z1], [x0, Y, z1]], cf(fb, [0, 1, 0])));
        const ff = mx * mx + mz * mz; // y=0
        faces.push(K.face3d(screen, [[x0, 0, z0], [x1, 0, z0], [x1, 0, z1], [x0, 0, z1]], cf(ff, [0, -1, 0])));
      }
      let out = K.paint3d(faces);
      // bounding-box edges (12) in a dim colour
      const E = (a, b) => { out += K.seg3d(screen, a, b, C.faint, 1); };
      const cor = [[0, 0, 0], [X, 0, 0], [X, Y, 0], [0, Y, 0], [0, 0, Z], [X, 0, Z], [X, Y, Z], [0, Y, Z]];
      const ed = [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]];
      for (const [a, b] of ed) E(cor[a], cor[b]);
      // axis labels
      out += K.text3d(screen, [X * 1.12, 0, 0], "x", C.faint);
      out += K.text3d(screen, [0, Y * 1.12, 0], "y", C.faint);
      out += K.text3d(screen, [0, 0, Z * 1.12], "z", C.faint);
      return out;
    };

    const t = tasks.find((x) => x.id === task) || tasks[0];
    const okColor = fld.converged ? C.teal : C.amber;

    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <Scene3D az={az} el={el} setAz={setAz} setEl={setEl} fit={fit} width={600} height={320} build={build}
          onUp={() => event("rotated", `View az ${Math.round(az)}° · el ${Math.round(el)}°`, { response: `riemann ${fmt(fld.riemann)}` }, C.blue)} />
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 8, fontSize: 11.5, color: C.faint, fontFamily: "monospace" }}>
          <span><K.T>drag to orbit</K.T> · {ni}³ = {fld.nCells} <K.T>cells</K.T> · f = x²+y²+z²</span>
          <span style={{ color: C.blue }}>Σ = {fmt(fld.riemann)} <span style={{ color: C.faint }}>vs</span> <span style={{ color: C.teal }}>∫ = {fmt(fld.exactVal)}</span></span>
        </div>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${fld.converged ? C.teal : C.line}`, color: fld.converged ? C.teal : C.mute, fontSize: 12.5 }}>
          Midpoint Riemann sum = {fmt(fld.riemann)} vs exact triple integral (X³YZ+XY³Z+XYZ³)/3 = {fmt(fld.exactVal)}. {fld.converged ? <>⚑ <K.T>Converged — the Riemann sum has closed onto the exact triple integral.</K.T></> : <K.T>Not converged yet — add cells (raise n) to shrink the error.</K.T>}
        </div>
        <Slider label="box width X" value={X} set={setX} min={0.5} max={3} step={0.5} unit="" color={C.teal} onUp={() => event("adjusted", `Set X = ${fmt(X)}`, { response: `riemann ${fmt(fld.riemann)}` }, C.teal)} />
        <Slider label="box depth Y" value={Y} set={setY} min={0.5} max={3} step={0.5} unit="" color={C.gold} onUp={() => event("adjusted", `Set Y = ${fmt(Y)}`, { response: `riemann ${fmt(fld.riemann)}` }, C.gold)} />
        <Slider label="box height Z" value={Z} set={setZ} min={0.5} max={3} step={0.5} unit="" color={C.amber} onUp={() => event("adjusted", `Set Z = ${fmt(Z)}`, { response: `riemann ${fmt(fld.riemann)}` }, C.amber)} />
        <Slider label="cells per side n" value={n} set={(v) => setN(Math.round(v))} min={1} max={24} step={1} unit="" color={C.blue} onUp={() => event("adjusted", `Set n = ${fmt(Math.round(n))}`, { response: `error ${fmt(fld.error)}` }, C.blue)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="Riemann sum" v={fld.riemann} d={2} color={C.blue} />
          <StatMeter label="exact value" v={fld.exactVal} d={2} color={C.teal} />
          <StatMeter label="error" v={fld.error} d={3} color={okColor} />
          <StatMeter label="# cells" v={fld.nCells} d={0} color={C.amber} />
        </div>
      </>
    );
  }

  window.TripleIntegralTutor = K.makeTutor(TripleIntegralFig, { moduleLabel: "Triple Integrals bench", benchId: "tripleintegral" });
})();
