/* osc555-schem.jsx — a live, Falstad-style schematic of the 555 astable.
   Draws the real circuit (battery, 555 chip, R_A, R_B, timing cap, LED) with
   proper schematic symbols and animates CHARGE PARTICLES along every wire:
     • direction  = sign of the live current in that branch
     • speed/size = magnitude of the current
     • colour     = spectrum (blue full-energy → amber → red spent), like the
                    breadboard "watch the charges" overlay
   Driven by the live MNA sim (window.useMnaSim result passed in as `sim`); all
   branch currents come from the engine, so what you see is what the solver did.
   Names os*. Exports Osc555Schem. */

const OSS_NS = "http://www.w3.org/2000/svg";
const OSS_BLUE = "#2f6db0", OSS_AMBER = "#e0a32e", OSS_RED = "#c0392b";
function ossRgb(h){h=h.replace("#","");return [parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16)];}
function ossMix(a,b,t){const A=ossRgb(a),B=ossRgb(b);return `rgb(${Math.round(A[0]+(B[0]-A[0])*t)},${Math.round(A[1]+(B[1]-A[1])*t)},${Math.round(A[2]+(B[2]-A[2])*t)})`;}
function ossSpectrum(e){e=Math.max(0,Math.min(1,e));return e>=0.5?ossMix(OSS_AMBER,OSS_BLUE,(e-0.5)/0.5):ossMix(OSS_RED,OSS_AMBER,e/0.5);}

/* polyline → {pts,total,at(s)} so particles can ride along it */
function ossPath(pts){
  const cum=[0];
  for(let i=1;i<pts.length;i++) cum.push(cum[i-1]+Math.hypot(pts[i].x-pts[i-1].x,pts[i].y-pts[i-1].y));
  const total=cum[pts.length-1]||1;
  const at=(s)=>{s=((s%total)+total)%total;let i=1;while(i<pts.length&&cum[i]<s)i++;const a=pts[i-1],b=pts[i]||pts[0],seg=(cum[i]-cum[i-1])||1,f=(s-cum[i-1])/seg;return {x:a.x+(b.x-a.x)*f,y:a.y+(b.y-a.y)*f};};
  return {pts,total,at};
}
const ossPL = (pts)=>pts.map(p=>`${p.x},${p.y}`).join(" ");

function Osc555Schem({ sim, kids }){
  const gRef = React.useRef(null);      // particle layer
  const curRef = React.useRef({});      // latest currents, read by the rAF
  // node coordinates ───────────────────────────────────────────────
  const VCCY=46, GNDY=412, BATX=64, RAX=168, CHIPL=300, CHIPR=452, CHIPT=176, CHIPB=300;
  const DISY=176, THRY=300, OUTY=238, OUTX=540;
  const J_DIS={x:RAX,y:DISY}, J_THR={x:RAX,y:THRY};
  const P_DIS={x:CHIPL,y:206}, P_THR={x:CHIPL,y:262}, P_OUT={x:CHIPR,y:OUTY};
  const P_VCC={x:362,y:CHIPT}, P_GND={x:362,y:CHIPB};

  // wires: each {pts, cur} where cur() returns SIGNED amps (+ = along pts order)
  const wires = React.useMemo(()=>{
    const C = curRef;
    const IRA = ()=> (C.current.ra||0);                 // + = VCC → DIS (down R_A)
    const IRB = ()=> (C.current.rb||0);                 // + = DIS → THR (down R_B)
    const ICAP= ()=> (C.current.rb||0);                 // node THR: in from R_B → into cap
    const IDIS= ()=> ((C.current.ra||0)-(C.current.rb||0)); // KCL at DIS node → into pin 7
    const ILED= ()=> (C.current.led||0);                // + = OUT → ground
    const ISUP= ()=> ((C.current.ra||0)+(C.current.led||0));
    return [
      // battery + rail feeding R_A
      { id:"vcc_ra", pts:[{x:BATX,y:VCCY},{x:RAX,y:VCCY},{x:RAX,y:78}], cur:ISUP },
      // R_A body is drawn as a symbol; animate its two short leads as one branch
      { id:"ra_dis", pts:[{x:RAX,y:150},{x:RAX,y:DISY}], cur:IRA },
      // DIS → R_B
      { id:"dis_rb", pts:[{x:RAX,y:DISY},{x:RAX,y:200}], cur:IRB },
      { id:"rb_thr", pts:[{x:RAX,y:272},{x:RAX,y:THRY}], cur:IRB },
      // THR → cap → ground
      { id:"thr_cap", pts:[{x:RAX,y:THRY},{x:RAX,y:340}], cur:ICAP },
      { id:"cap_gnd", pts:[{x:RAX,y:366},{x:RAX,y:GNDY}], cur:ICAP },
      // DIS node → chip pin 7 (discharge path)
      { id:"dis_chip", pts:[{x:RAX,y:DISY},{x:250,y:DISY},{x:250,y:P_DIS.y},{x:CHIPL,y:P_DIS.y}], cur:IDIS },
      // THR node → chip pin 6/2 (high-Z sense — barely moves)
      { id:"thr_chip", pts:[{x:RAX,y:THRY},{x:268,y:THRY},{x:268,y:P_THR.y},{x:CHIPL,y:P_THR.y}], cur:()=>0.0008 },
      // chip VCC + RST → rail ; chip GND → ground
      { id:"chip_vcc", pts:[{x:P_VCC.x,y:VCCY},{x:P_VCC.x,y:CHIPT}], cur:()=> (C.current.led||0)+0.0015 },
      { id:"chip_gnd", pts:[{x:P_GND.x,y:CHIPB},{x:P_GND.x,y:GNDY}], cur:()=> Math.abs(IDIS())+ (C.current.led? 0:0) },
      // rail segment feeding chip VCC
      { id:"rail_chip", pts:[{x:RAX,y:VCCY},{x:P_VCC.x,y:VCCY}], cur:()=> (C.current.led||0)+0.0015 },
      // output → R_LED → LED → ground
      { id:"out_rled", pts:[{x:CHIPR,y:OUTY},{x:OUTX,y:OUTY},{x:OUTX,y:300}], cur:ILED },
      { id:"led_gnd", pts:[{x:OUTX,y:372},{x:OUTX,y:GNDY},{x:P_GND.x,y:GNDY}], cur:ILED },
    ].map(w=>({ ...w, path: ossPath(w.pts) }));
  },[]);

  // build particle pools when wires change
  React.useEffect(()=>{
    const g=gRef.current; if(!g) return;
    while(g.firstChild) g.removeChild(g.firstChild);
    const pools=wires.map(w=>{
      const n=Math.max(3,Math.round(w.path.total/26));
      const arr=[];
      for(let k=0;k<n;k++){const c=document.createElementNS(OSS_NS,"circle");c.setAttribute("r","4.2");c.setAttribute("stroke","rgba(0,0,0,0.25)");c.setAttribute("stroke-width","0.6");g.appendChild(c);arr.push(c);}
      return arr;
    });
    g.__pools=pools; g.__phase=wires.map(()=>0);
  },[wires]);

  // animation loop — reads curRef each frame
  React.useEffect(()=>{
    let raf;
    function frame(){
      const g=gRef.current;
      if(g&&g.__pools){
        for(let wi=0;wi<wires.length;wi++){
          const w=wires[wi], pool=g.__pools[wi]; if(!pool) continue;
          const I=w.cur();
          const mag=Math.min(2.4, Math.abs(I)*180);           // amps → px/frame
          const moving=mag>0.05;
          const dir=I>=0?1:-1;
          g.__phase[wi]=(g.__phase[wi]||0)+dir*(moving?(0.5+mag):0);
          // energy gradient: charges are "fuller" near the source end of a drop
          for(let k=0;k<pool.length;k++){
            const len=w.path.total;
            const s=(g.__phase[wi]+(k/pool.length)*len)%len;
            const p=w.path.at(s), c=pool[k];
            const e=1-(s/len);                                  // crude spend along the wire
            c.setAttribute("cx",p.x.toFixed(1)); c.setAttribute("cy",p.y.toFixed(1));
            c.setAttribute("fill", moving?ossSpectrum(e):"#b7afa0");
            c.setAttribute("r",(moving?(3.4+Math.min(2.2,mag)):2.6).toFixed(1));
            c.setAttribute("opacity", moving?"1":"0.5");
          }
        }
      }
      raf=requestAnimationFrame(frame);
    }
    raf=requestAnimationFrame(frame);
    return ()=>cancelAnimationFrame(raf);
  },[wires]);

  // push latest currents + node voltages from the sim every render
  curRef.current = {
    ra:  sim.current ? (sim.current[2]||0) : 0,
    rb:  sim.current ? (sim.current[3]||0) : 0,
    led: sim.current ? (sim.current[6]||0) : 0,
  };
  const vDis = sim.probe? sim.probe("DIS"):0;
  const vThr = sim.probe? sim.probe("THR"):0;
  const vOut = sim.probe? sim.probe("OUT"):0;
  const outHigh = vOut>4.5;

  // ── static schematic symbols ──
  const wireCol="var(--ink-soft)";
  const Lead=(pts,key)=> <polyline key={key} points={ossPL(pts)} fill="none" stroke={wireCol} strokeWidth="2.4" strokeLinejoin="round" strokeLinecap="round" />;
  // resistor zig (vertical), from y0 to y1 at x
  const resZig=(x,y0,y1,key)=>{const n=6,h=(y1-y0),seg=h/n,w=9;let d=`M ${x} ${y0}`;for(let i=0;i<n;i++){const yy=y0+seg*(i+0.5);d+=` L ${x+(i%2?-w:w)} ${yy}`;}d+=` L ${x} ${y1}`;return <path key={key} d={d} fill="none" stroke="var(--ink)" strokeWidth="2.4" strokeLinejoin="round"/>;};

  return (
    <div style={{ background:"var(--bg-card)", border:"1px solid var(--rule)", borderRadius:12, padding:"6px 6px 2px" }}>
      <svg viewBox="0 0 620 440" width="100%" style={{ display:"block" }}>
        {/* rails */}
        {Lead([{x:BATX,y:VCCY},{x:OUTX,y:VCCY}],"railT")}
        {Lead([{x:BATX,y:GNDY},{x:OUTX,y:GNDY}],"railB")}
        {/* battery (left) */}
        {Lead([{x:BATX,y:VCCY},{x:BATX,y:200}],"bat1")}
        {Lead([{x:BATX,y:236},{x:BATX,y:GNDY}],"bat2")}
        <line x1={BATX-16} y1="200" x2={BATX+16} y2="200" stroke="var(--ink)" strokeWidth="3"/>
        <line x1={BATX-9} y1="212" x2={BATX+9} y2="212" stroke="var(--ink)" strokeWidth="2"/>
        <line x1={BATX-16} y1="224" x2={BATX+16} y2="224" stroke="var(--ink)" strokeWidth="3"/>
        <line x1={BATX-9} y1="236" x2={BATX+9} y2="236" stroke="var(--ink)" strokeWidth="2"/>
        <text x={BATX-24} y="222" textAnchor="end" fontFamily="IBM Plex Mono, monospace" fontSize="13" fill="var(--ink-soft)">9V</text>
        <text x={BATX+22} y={VCCY-8} fontFamily="IBM Plex Mono, monospace" fontSize="13" fill="var(--current)">+9V</text>

        {/* R_A */}
        {resZig(RAX,78,150,"ra")}
        <text x={RAX+16} y="118" fontFamily="IBM Plex Mono, monospace" fontSize="13" fill="var(--ink)">R_A</text>
        {/* DIS node dot */}
        <circle cx={RAX} cy={DISY} r="4" fill="var(--ink)"/>
        {/* R_B */}
        {resZig(RAX,200,272,"rb")}
        <text x={RAX+16} y="240" fontFamily="IBM Plex Mono, monospace" fontSize="13" fill="var(--ink)">R_B</text>
        {/* THR node dot */}
        <circle cx={RAX} cy={THRY} r="4" fill="var(--ink)"/>
        {/* capacitor */}
        <line x1={RAX-18} y1="340" x2={RAX+18} y2="340" stroke="var(--ink)" strokeWidth="3"/>
        <line x1={RAX-18} y1="366" x2={RAX+18} y2="366" stroke="var(--ink)" strokeWidth="3"/>
        <text x={RAX+24} y="358" fontFamily="IBM Plex Mono, monospace" fontSize="13" fill="var(--ink)">C</text>

        {/* the 555 chip */}
        <rect x={CHIPL} y={CHIPT} width={CHIPR-CHIPL} height={CHIPB-CHIPT} rx="8" fill="var(--bg-deeper)" stroke="var(--ink)" strokeWidth="2.2"/>
        <text x={(CHIPL+CHIPR)/2} y={(CHIPT+CHIPB)/2-6} textAnchor="middle" fontFamily="Newsreader, serif" fontSize="22" fill="var(--ink)">555</text>
        <text x={(CHIPL+CHIPR)/2} y={(CHIPT+CHIPB)/2+16} textAnchor="middle" fontFamily="IBM Plex Mono, monospace" fontSize="11" fill={outHigh?"var(--current)":"var(--ink-faint)"}>{outHigh?"OUT ▲ HIGH":"OUT ▼ low"}</text>
        {/* chip pin leads */}
        {Lead([{x:RAX,y:DISY},{x:250,y:DISY},{x:250,y:P_DIS.y},{x:CHIPL,y:P_DIS.y}],"pdis")}
        {Lead([{x:RAX,y:THRY},{x:268,y:THRY},{x:268,y:P_THR.y},{x:CHIPL,y:P_THR.y}],"pthr")}
        {Lead([{x:P_VCC.x,y:VCCY},{x:P_VCC.x,y:CHIPT}],"pvcc")}
        {Lead([{x:P_GND.x,y:CHIPB},{x:P_GND.x,y:GNDY}],"pgnd")}
        {Lead([{x:CHIPR,y:OUTY},{x:OUTX,y:OUTY},{x:OUTX,y:300}],"pout")}
        <text x={CHIPL-6} y={P_DIS.y-6} textAnchor="end" fontFamily="IBM Plex Mono, monospace" fontSize="11" fill="var(--ink-faint)">DIS</text>
        <text x={CHIPL-6} y={P_THR.y-6} textAnchor="end" fontFamily="IBM Plex Mono, monospace" fontSize="11" fill="var(--ink-faint)">THR/TRG</text>
        <text x={CHIPR+6} y={OUTY-6} fontFamily="IBM Plex Mono, monospace" fontSize="11" fill="var(--ink-faint)">OUT</text>

        {/* output R_LED */}
        {resZig(OUTX,300,372,"rled")}
        {/* LED triangle + bar at bottom of output column */}
        <g transform={`translate(${OUTX},388)`}>
          <polygon points="-9,-8 9,-8 0,8" fill={outHigh?"#ff9b2e":"var(--bg-deeper)"} stroke="var(--ink)" strokeWidth="1.6"/>
          <line x1="-9" y1="8" x2="9" y2="8" stroke="var(--ink)" strokeWidth="2.4"/>
        </g>
        <text x={OUTX+16} y="344" fontFamily="IBM Plex Mono, monospace" fontSize="12" fill="var(--ink)">LED</text>

        {/* live node-voltage tags */}
        <text x={RAX-26} y={DISY+4} textAnchor="end" fontFamily="IBM Plex Mono, monospace" fontSize="12" fill="var(--water)">{vDis.toFixed(1)}V</text>
        <text x={RAX-26} y={THRY+4} textAnchor="end" fontFamily="IBM Plex Mono, monospace" fontSize="12" fill="var(--water)">{vThr.toFixed(1)}V</text>

        {/* animated charge particles on top */}
        <g ref={gRef}></g>
      </svg>
      <div style={{ fontSize:12.5, color:"var(--ink-faint)", padding:"2px 10px 8px", lineHeight:1.5 }}>
        {kids
          ? "Watch the dots! They race up through R_A and R_B to fill the bucket (C). When it's full the chip dumps it out the side — and the light flips. Round and round."
          : "Charges flow VCC → R_A → R_B into the cap (it charges). At ⅔ supply the 555 opens pin 7 and the cap dumps back out through R_B — you'll see those dots reverse — then it refills. The LED follows the output."}
      </div>
    </div>
  );
}

Object.assign(window, { Osc555Schem });
