/* osmosislab.jsx — OsmosisTutor: the osmosis & tonicity cell bench. Built on window.BenchKit
   (harness) + window.BenchFields (shared field math), so this file is just the FIGURE.
   compute = window.BenchFields.osmosis — the SAME function server/benches/osmosis.js calls,
   so client and server fields are identical by construction. Mirrors gaslawslab.jsx. */
(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.osmosis(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 }}>{label}</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>
    );
  }

  // deterministic dot scatter within a rect, count ∝ concentration.
  function scatter(conc, x0, y0, w, h, seed) {
    const n = Math.round(conc * 9);
    const out = [];
    for (let i = 0; i < n; i++) {
      const a = (i * 73 + seed * 29) % 1000 / 1000;
      const b = (i * 131 + seed * 17) % 1000 / 1000;
      out.push({ x: x0 + 8 + a * (w - 16), y: y0 + 8 + b * (h - 16) });
    }
    return out;
  }

  function OsmosisFig({ task, setTask, tasks, report, event, done }) {
    const [insideConc, setInside] = useState(0.9);
    const [outsideConc, setOutside] = useState(0.9);
    const fld = useMemo(() => compute({ insideConc, outsideConc }), [insideConc, outsideConc]);
    useEffect(() => { report(fld); }, [insideConc, outsideConc]); // eslint-disable-line

    const state = fld.cellState; // 'swelling' | 'shrinking' | 'stable'
    const cx = 300, cy = 155;
    const baseR = 52;
    const cellR = state === "swelling" ? baseR + 18 : state === "shrinking" ? baseR - 18 : baseR;
    // crenated (shrunken) cells get a wavy outline; swollen ones a smooth taut circle.
    const cellColor = state === "swelling" ? C.blue : state === "shrinking" ? C.crimson : C.teal;

    // beaker (the solution) and the cell interior
    const BX0 = 60, BY0 = 50, BX1 = 540, BY1 = 260;
    const solDots = scatter(outsideConc, BX0, BY0, BX1 - BX0, BY1 - BY0, 1)
      .filter((d) => Math.hypot(d.x - cx, d.y - cy) > cellR + 6); // outside the cell only
    const inDots = scatter(insideConc, cx - cellR, cy - cellR, cellR * 2, cellR * 2, 7)
      .filter((d) => Math.hypot(d.x - cx, d.y - cy) < cellR - 8);

    const t = tasks.find((x) => x.id === task) || tasks[0];
    const tonicity = fld.isHypotonic ? "hypotonic (water in)" : fld.isHypertonic ? "hypertonic (water out)" : "isotonic (balanced)";
    const flux = fld.netFlux;

    // water flux arrows: into the cell when hypotonic, out when hypertonic.
    const arrows = [];
    if (!fld.isIsotonic) {
      const into = fld.isHypotonic;
      for (let k = 0; k < 4; k++) {
        const ang = (Math.PI / 2) * k + Math.PI / 4;
        const r1 = cellR + 30, r2 = cellR + 8;
        const ax = cx + Math.cos(ang) * (into ? r1 : r2), ay = cy + Math.sin(ang) * (into ? r1 : r2);
        const bx = cx + Math.cos(ang) * (into ? r2 : r1), by = cy + Math.sin(ang) * (into ? r2 : r1);
        arrows.push({ ax, ay, bx, by });
      }
    }

    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 }}>
          {/* beaker / solution */}
          <rect x={BX0} y={BY0} width={BX1 - BX0} height={BY1 - BY0} fill={C.teal} opacity={0.05 + Math.min(0.18, outsideConc * 0.09)} stroke={C.faint} strokeWidth="1.5" rx="6" />
          {solDots.map((d, i) => <circle key={"s" + i} cx={d.x} cy={d.y} r={2.5} fill={C.amber} opacity="0.7" />)}
          {/* water flux arrows */}
          <defs>
            <marker id="osmArrow" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
              <path d="M0,0 L6,3 L0,6 Z" fill={cellColor} />
            </marker>
          </defs>
          {arrows.map((a, i) => (
            <line key={"a" + i} x1={a.ax} y1={a.ay} x2={a.bx} y2={a.by} stroke={cellColor} strokeWidth="2.5" opacity="0.85" markerEnd="url(#osmArrow)" />
          ))}
          {/* the cell (membrane) */}
          <circle cx={cx} cy={cy} r={cellR} fill={cellColor} opacity={0.12} stroke={cellColor} strokeWidth={state === "shrinking" ? "2" : "2.5"} strokeDasharray={state === "shrinking" ? "4 3" : "0"} />
          {inDots.map((d, i) => <circle key={"i" + i} cx={d.x} cy={d.y} r={2.5} fill={C.crimson} opacity="0.8" />)}
          <text x={cx} y={cy + 4} textAnchor="middle" fill={C.ink} fontSize="12" fontFamily="monospace">
            {state === "swelling" ? "SWOLLEN" : state === "shrinking" ? "CRENATED" : "NORMAL"}
          </text>
          <text x={BX0 + 6} y={BY0 - 8} fill={C.mute} fontSize="11" fontFamily="monospace">inside {fmt(insideConc)} M · solution {fmt(outsideConc)} M · {tonicity}</text>
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${C.line}`, color: C.mute, fontSize: 12.5 }}>
          Water moves toward higher solute. Net flux = {fmt(flux)} ({flux > 0 ? "into the cell" : flux < 0 ? "out of the cell" : "no net flux"}). The solution is {tonicity}.
        </div>
        <Slider label="solute inside" value={insideConc} set={setInside} min={0} max={2} step={0.1} unit="M" color={C.crimson} onUp={() => event("adjusted", `Set inside = ${fmt(insideConc)} M`, { response: `flux ${fmt(fld.netFlux)} · ${fld.cellState}` }, C.crimson)} />
        <Slider label="solution" value={outsideConc} set={setOutside} min={0} max={2} step={0.1} unit="M" color={C.amber} onUp={() => event("adjusted", `Set solution = ${fmt(outsideConc)} M`, { response: `flux ${fmt(fld.netFlux)} · ${fld.cellState}` }, C.amber)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="net flux" v={fld.netFlux} d={1} color={flux > 0 ? C.blue : flux < 0 ? C.crimson : C.teal} />
          <StatMeter label="gradient (in − out)" v={fld.gradient} d={2} unit="M" color={C.gold} />
          <StatMeter label="cell state" v={state === "swelling" ? "swelling" : state === "shrinking" ? "shrinking" : "stable"} color={cellColor} />
        </div>
      </>
    );
  }

  window.OsmosisTutor = K.makeTutor(OsmosisFig, { moduleLabel: "Osmosis & tonicity bench", benchId: "osmosis" });
})();
