'use strict';
const fs = require('fs');
const path = require('path');

const targetFile = path.join(__dirname, '../../frontend/js/labs/opticsbench.js');

const ifSimCode = `/* ─────────────────────────────────────────────────────────────
   4d. INTERFERENCE SIM — Newton's rings / Thin film / Polarization
   Agent C — additive only, class InterferenceSim
─────────────────────────────────────────────────────────────*/
class InterferenceSim {
  constructor(canvas) {
    this.canvas  = canvas;
    this.ctx     = canvas.getContext('2d');
    this.W = 0; this.H = 0;
    this.subMode = 'newton';
    // Newton rings
    this.nR    = 200;
    this.nNmax = 12;
    // Thin film
    this.tfT      = 400;
    this.tfN      = 1.33;
    this.tfTheta  = 0;
    this.tfPreset = 'soap';
    // Polarization
    this.polTheta = 45;
    this.polSrc   = 'unpolarized';
    this._polTick = 0;
    this._polRaf  = null;
    this.onUpdate = null;
    new ResizeObserver(() => { this.fit(); this.draw(); }).observe(canvas.parentElement || canvas);
  }

  fit() {
    const p = this.canvas.parentElement;
    if (!p) return;
    const r = p.getBoundingClientRect();
    this.W = this.canvas.width  = r.width  || p.offsetWidth  || 600;
    this.H = this.canvas.height = r.height || p.offsetHeight || 400;
  }

  setSubMode(sm) {
    this.subMode = sm;
    if (sm === 'polarization') {
      this._polStart();
    } else {
      this._polStop();
    }
    this.draw();
    if (this.onUpdate) this.onUpdate();
  }

  /* ── Newton Rings ──────────────────────────────────────── */
  _drawNewton() {
    const { ctx, W, H } = this;
    const nm    = window._obWavelength || 550;
    const R     = this.nR;
    const nMax  = this.nNmax;
    const white = window._obWhiteLight;

    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = '#08081a';
    ctx.fillRect(0, 0, W, H);

    const topH = Math.floor(H * 0.60);
    const cx = W / 2, cy = topH / 2;
    const maxR_mm = Math.sqrt(nMax * nm * 1e-6 * R);
    const scale   = Math.min(cx * 0.85, cy * 0.85) / (maxR_mm || 1);

    for (let n = nMax; n >= 0; n--) {
      const lambdas = white ? [420, 470, 510, 550, 590, 620, 680] : [nm];
      for (const lam of lambdas) {
        const rDark   = Math.sqrt(n * lam * 1e-6 * R) * scale;
        const rBright = Math.sqrt((n + 0.5) * lam * 1e-6 * R) * scale;
        if (rDark > 0.5) {
          ctx.beginPath();
          ctx.arc(cx, cy, rDark, 0, Math.PI * 2);
          ctx.strokeStyle = white
            ? wavelengthToRGB(lam).replace(')', ',0.5)').replace('rgb', 'rgba')
            : '#000000';
          ctx.lineWidth = white ? 1.2 : 1.5;
          ctx.stroke();
        }
        if (rBright > 0.5) {
          const al = white ? 0.22 : 0.55;
          ctx.beginPath();
          ctx.arc(cx, cy, rBright, 0, Math.PI * 2);
          ctx.strokeStyle = wavelengthToRGB(lam).replace(')', ',' + al + ')').replace('rgb', 'rgba');
          ctx.lineWidth = 2.5;
          ctx.stroke();
        }
      }
    }

    ctx.beginPath(); ctx.arc(cx, cy, 4, 0, Math.PI * 2);
    ctx.fillStyle = '#000000'; ctx.fill();

    if (window.LabFX && LabFX.glow && !white) {
      const r1b = Math.sqrt(0.5 * nm * 1e-6 * R) * scale;
      LabFX.glow.drawGlow(ctx, cx, cy, r1b, wavelengthToRGB(nm), 18);
    }

    ctx.beginPath(); ctx.arc(cx, cy, maxR_mm * scale * 1.05, 0, Math.PI * 2);
    ctx.strokeStyle = '#334455'; ctx.lineWidth = 1; ctx.stroke();

    const crossY0 = topH + 8;
    const crossH  = H - crossY0 - 40;
    if (crossH < 30) return;

    ctx.fillStyle = '#0d0d20';
    ctx.fillRect(0, crossY0, W, crossH + 36);

    const glassY  = crossY0 + crossH - 10;
    ctx.fillStyle = '#1a3a5c';
    ctx.fillRect(cx - maxR_mm * scale * 1.1, glassY, maxR_mm * scale * 2.2, 10);

    const sagitta = (maxR_mm * maxR_mm) / (2 * R);
    const sagPx   = sagitta * scale;
    ctx.beginPath();
    ctx.ellipse(cx, glassY - 1 - sagPx, maxR_mm * scale * 1.1, sagPx + 6, 0, 0, Math.PI);
    ctx.fillStyle = 'rgba(100,180,255,0.15)'; ctx.fill();
    ctx.strokeStyle = '#4499cc'; ctx.lineWidth = 1.5; ctx.stroke();

    for (let n = 0; n <= nMax; n++) {
      const rD = Math.sqrt(n * nm * 1e-6 * R) * scale;
      if (rD < 1) continue;
      ctx.beginPath();
      ctx.moveTo(cx + rD, glassY); ctx.lineTo(cx + rD, glassY + 8);
      ctx.moveTo(cx - rD, glassY); ctx.lineTo(cx - rD, glassY + 8);
      ctx.strokeStyle = 'rgba(255,255,255,0.25)'; ctx.lineWidth = 1; ctx.stroke();
    }

    ctx.font = '600 11px monospace'; ctx.fillStyle = '#667788'; ctx.textAlign = 'center';
    ctx.fillText('Cross-section', cx, crossY0 + 14);

    const r1d = Math.sqrt(nm * 1e-6 * R).toFixed(3);
    this._drawHUD(ctx, W, H,
      'r1 = sqrt(lam*R) = ' + r1d + ' mm  |  R=' + R + 'mm  |  lam=' + nm + 'nm');
  }

  /* ── Thin Film ─────────────────────────────────────────── */
  _thinFilmColor(t_nm, n_film, theta_deg) {
    const sinR = Math.sin(theta_deg * Math.PI / 180) / n_film;
    const cosR = Math.sqrt(Math.max(0, 1 - sinR * sinR));
    const opd  = 2 * n_film * t_nm * cosR;
    let rS = 0, gS = 0, bS = 0;
    for (let lam = 380; lam <= 780; lam += 5) {
      const phase = Math.PI * opd / lam;
      const I = Math.cos(phase) * Math.cos(phase);
      const rgb = wavelengthToRGB(lam);
      const m = rgb.match(/\d+/g);
      if (!m) continue;
      rS += I * +m[0]; gS += I * +m[1]; bS += I * +m[2];
    }
    const sc = 255 / Math.max(rS, gS, bS, 1);
    return 'rgb(' + Math.round(rS * sc) + ',' + Math.round(gS * sc) + ',' + Math.round(bS * sc) + ')';
  }

  _drawThinFilm() {
    const { ctx, W, H } = this;
    const t     = this.tfT;
    const nf    = this.tfN;
    const theta = this.tfTheta;

    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = '#08081a';
    ctx.fillRect(0, 0, W, H);

    const midY   = H * 0.40;
    const filmH  = Math.max(28, H * 0.12);
    const margin = W * 0.10;
    const ang    = theta * Math.PI / 180;
    const skew   = Math.tan(ang) * filmH * 0.5;

    const grad = ctx.createLinearGradient(margin, 0, W - margin, 0);
    for (let i = 0; i <= 20; i++) {
      const frac = i / 20;
      grad.addColorStop(frac, this._thinFilmColor(t * (0.3 + 0.7 * frac), nf, theta));
    }

    ctx.save();
    ctx.beginPath();
    ctx.moveTo(margin - skew,     midY - filmH / 2);
    ctx.lineTo(W - margin - skew, midY - filmH / 2);
    ctx.lineTo(W - margin + skew, midY + filmH / 2);
    ctx.lineTo(margin + skew,     midY + filmH / 2);
    ctx.closePath();
    ctx.fillStyle = grad; ctx.fill();
    ctx.strokeStyle = 'rgba(255,255,255,0.3)'; ctx.lineWidth = 1; ctx.stroke();
    ctx.restore();

    ctx.font = '700 11px sans-serif'; ctx.fillStyle = 'rgba(255,255,255,0.7)';
    ctx.textAlign = 'center';
    ctx.fillText('t=' + t + 'nm  n=' + nf.toFixed(2), W / 2, midY);

    const ax2 = W * 0.25, ay2 = midY - filmH / 2;
    const ax1 = ax2 - Math.cos(ang) * 40, ay1 = ay2 - Math.sin(ang) * 40 - 20;
    ctx.beginPath(); ctx.moveTo(ax1, ay1); ctx.lineTo(ax2, ay2);
    ctx.strokeStyle = '#8ab4e8'; ctx.lineWidth = 1.5; ctx.stroke();

    const col = this._thinFilmColor(t, nf, theta);
    ctx.beginPath(); ctx.moveTo(ax2, ay2); ctx.lineTo(ax2 - Math.cos(ang) * 40, ay1);
    ctx.strokeStyle = col; ctx.lineWidth = 2; ctx.stroke();

    const dx2 = Math.sin(ang) * filmH / nf;
    ctx.beginPath();
    ctx.moveTo(ax2 + dx2, ay2 + filmH);
    ctx.lineTo(ax2 + dx2 - Math.cos(ang) * 40, ay1 + filmH - 20);
    ctx.strokeStyle = col; ctx.lineWidth = 2;
    ctx.setLineDash([4, 3]); ctx.stroke(); ctx.setLineDash([]);

    const tvX0 = W * 0.55, tvW2 = W * 0.38;
    const tvY0 = H * 0.05, tvH2 = H * 0.60;
    ctx.fillStyle = '#0d0d22'; ctx.strokeStyle = '#2a2a4a'; ctx.lineWidth = 1;
    ctx.beginPath();
    if (ctx.roundRect) ctx.roundRect(tvX0, tvY0, tvW2, tvH2, 8);
    else ctx.rect(tvX0, tvY0, tvW2, tvH2);
    ctx.fill(); ctx.stroke();
    ctx.font = '600 10px sans-serif'; ctx.fillStyle = '#555'; ctx.textAlign = 'center';
    ctx.fillText('Top view', tvX0 + tvW2 / 2, tvY0 + 14);

    const tvRows = 28, tvCols = 36;
    const cW = tvW2 / tvCols, cH = (tvH2 - 20) / tvRows;
    for (let r = 0; r < tvRows; r++) {
      for (let c = 0; c < tvCols; c++) {
        ctx.fillStyle = this._thinFilmColor(t * (0.5 + c / tvCols), nf, theta * (r / tvRows));
        ctx.fillRect(tvX0 + c * cW, tvY0 + 20 + r * cH, cW + 0.5, cH + 0.5);
      }
    }

    const sinR2 = Math.sin(ang) / nf;
    const cosR2 = Math.sqrt(Math.max(0, 1 - sinR2 * sinR2));
    const opd2  = (2 * nf * t * cosR2).toFixed(0);
    this._drawHUD(ctx, W, H,
      '2nt*cos(th_r)=' + opd2 + 'nm  |  t=' + t + 'nm  n=' + nf.toFixed(2) + '  th=' + theta + 'deg');
  }

  /* ── Polarization ──────────────────────────────────────── */
  _polStart() {
    if (this._polRaf) return;
    const loop = () => { this._polTick++; this.draw(); this._polRaf = requestAnimationFrame(loop); };
    this._polRaf = requestAnimationFrame(loop);
  }

  _polStop() {
    if (this._polRaf) { cancelAnimationFrame(this._polRaf); this._polRaf = null; }
  }

  _drawPolarization() {
    const { ctx, W, H } = this;
    const theta   = this.polTheta * Math.PI / 180;
    const I_rel   = Math.cos(theta) * Math.cos(theta);
    const tick    = this._polTick;
    const white   = window._obWhiteLight;
    const nm      = window._obWavelength || 550;
    const beamCol = white ? '#ffffff' : wavelengthToRGB(nm);

    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = '#08081a';
    ctx.fillRect(0, 0, W, H);

    const axisY = H * 0.45;
    const stH   = H * 0.38;
    const st    = [
      { x: W * 0.12, label: 'Источник',       isFilter: false                    },
      { x: W * 0.38, label: 'Поляризатор P1',  isFilter: true,  angle: 0          },
      { x: W * 0.64, label: 'Анализатор P2',   isFilter: true,  angle: this.polTheta },
      { x: W * 0.88, label: 'Детектор',        isFilter: false                    },
    ];

    ctx.beginPath();
    ctx.moveTo(st[0].x - 20, axisY); ctx.lineTo(st[3].x + 20, axisY);
    ctx.strokeStyle = '#1a1a35'; ctx.lineWidth = 1; ctx.stroke();

    const segs = [
      { x0: st[0].x, x1: st[1].x, amp: 1,     unpol: this.polSrc === 'unpolarized', ang: 0           },
      { x0: st[1].x, x1: st[2].x, amp: 1,     unpol: false,                          ang: 0           },
      { x0: st[2].x, x1: st[3].x, amp: I_rel, unpol: false,                          ang: this.polTheta },
    ];

    for (const seg of segs) {
      const nA = 20;
      const sdx = (seg.x1 - seg.x0) / nA;
      for (let i = 0; i <= nA; i++) {
        const bx    = seg.x0 + i * sdx;
        const phase = (bx * 0.08 - tick * 0.04) % (Math.PI * 2);
        const bAmp  = stH * 0.28 * seg.amp;
        if (seg.unpol) {
          for (let d = 0; d < 4; d++) {
            const a  = d * Math.PI / 4;
            const oy = Math.sin(phase + d * 0.7) * bAmp;
            ctx.beginPath(); ctx.moveTo(bx, axisY);
            ctx.lineTo(bx + oy * Math.sin(a) * 0.25, axisY + oy * Math.cos(a));
            ctx.strokeStyle = 'rgba(200,200,255,0.22)'; ctx.lineWidth = 1; ctx.stroke();
          }
        } else {
          const oy = Math.sin(phase) * bAmp;
          const a  = seg.ang * Math.PI / 180;
          const py = oy * Math.cos(a), px = oy * Math.sin(a) * 0.35;
          ctx.beginPath();
          ctx.moveTo(bx - px, axisY - py); ctx.lineTo(bx + px, axisY + py);
          ctx.strokeStyle = (I_rel < 0.01 && seg.amp < 0.5)
            ? 'rgba(80,80,120,0.5)'
            : beamCol.replace(')', ',0.75)').replace('rgb', 'rgba');
          ctx.lineWidth = 1.5; ctx.stroke();
          if (i % 3 === 0 && bAmp > 2) {
            ctx.beginPath(); ctx.arc(bx + px, axisY + py, 2, 0, Math.PI * 2);
            ctx.fillStyle = beamCol; ctx.fill();
          }
        }
      }
    }

    for (const s of st) {
      if (!s.isFilter) continue;
      const a = s.angle * Math.PI / 180;
      ctx.save(); ctx.translate(s.x, axisY);
      ctx.fillStyle = 'rgba(80,120,200,0.18)';
      ctx.fillRect(-4, -stH / 2, 8, stH);
      ctx.strokeStyle = '#4466aa'; ctx.lineWidth = 1.5; ctx.strokeRect(-4, -stH / 2, 8, stH);
      const axLen = stH * 0.45;
      ctx.beginPath();
      ctx.moveTo(-Math.sin(a) * axLen, -Math.cos(a) * axLen);
      ctx.lineTo( Math.sin(a) * axLen,  Math.cos(a) * axLen);
      ctx.strokeStyle = '#7aaeff'; ctx.lineWidth = 2; ctx.stroke();
      ctx.restore();
      ctx.font = '700 10px monospace'; ctx.fillStyle = '#7aaeff'; ctx.textAlign = 'center';
      ctx.fillText(s.angle + 'deg', s.x, axisY + stH / 2 + 14);
    }

    for (const s of st) {
      ctx.font = '600 10px sans-serif'; ctx.fillStyle = '#667788'; ctx.textAlign = 'center';
      ctx.fillText(s.label, s.x, axisY - stH / 2 - 8);
    }

    const barX = W * 0.91, barW = 16;
    const barY0 = axisY - stH / 2;
    ctx.fillStyle = '#111122'; ctx.fillRect(barX, barY0, barW, stH);
    const fillH2 = stH * I_rel;
    if (fillH2 > 0) {
      const bg = ctx.createLinearGradient(barX, barY0 + stH - fillH2, barX, barY0 + stH);
      bg.addColorStop(0, beamCol); bg.addColorStop(1, 'rgba(0,0,0,0.2)');
      ctx.fillStyle = bg; ctx.fillRect(barX, barY0 + stH - fillH2, barW, fillH2);
    }
    ctx.strokeStyle = '#334455'; ctx.lineWidth = 1; ctx.strokeRect(barX, barY0, barW, stH);
    ctx.font = '600 9px monospace'; ctx.fillStyle = '#aaaaaa'; ctx.textAlign = 'center';
    ctx.fillText('I', barX + barW / 2, barY0 - 5);

    if (this.polTheta >= 88) {
      ctx.font = '700 13px sans-serif'; ctx.fillStyle = '#EF476F'; ctx.textAlign = 'center';
      ctx.fillText('Полное гашение', W / 2, H * 0.85);
    }

    ctx.font = '10px sans-serif'; ctx.fillStyle = '#444466'; ctx.textAlign = 'right';
    ctx.fillText('Угол Брюстера: отражённый свет поляризован (см. Преломление)', W - 10, H - 10);

    const pct = (I_rel * 100).toFixed(1);
    this._drawHUD(ctx, W, H,
      'I/I0=cos2(th)=cos2(' + this.polTheta + 'deg)=' + I_rel.toFixed(3) + '  (' + pct + '%)');
  }

  _drawHUD(ctx, W, H, text) {
    const pad = 8, fs = 11;
    ctx.font = '600 ' + fs + 'px monospace';
    const tw = ctx.measureText(text).width;
    const bx = (W - tw) / 2 - pad, by = H - 32;
    const bw = tw + pad * 2, bh = fs + pad * 2;
    ctx.fillStyle = 'rgba(10,10,30,0.82)';
    ctx.beginPath();
    if (ctx.roundRect) ctx.roundRect(bx, by, bw, bh, 5);
    else ctx.rect(bx, by, bw, bh);
    ctx.fill();
    ctx.fillStyle = '#c8d8ff';
    ctx.textAlign = 'left'; ctx.textBaseline = 'middle';
    ctx.fillText(text, bx + pad, by + bh / 2);
    ctx.textBaseline = 'alphabetic';
  }

  draw() {
    if (this.subMode === 'newton')            this._drawNewton();
    else if (this.subMode === 'thinfilm')     this._drawThinFilm();
    else if (this.subMode === 'polarization') this._drawPolarization();
  }
}

`;

const src = fs.readFileSync(targetFile, 'utf-8');
const markerStr = '4c. SPECTROMETER PANEL';
const markerIdx = src.indexOf(markerStr);
if (markerIdx < 0) {
  console.error('ERROR: marker not found');
  process.exit(1);
}
const insertIdx = src.lastIndexOf('/*', markerIdx);
if (insertIdx < 0) {
  console.error('ERROR: comment start not found');
  process.exit(1);
}

// Check InterferenceSim not already present
if (src.indexOf('class InterferenceSim') >= 0) {
  console.log('InterferenceSim already present — skipping JS insertion');
  process.exit(0);
}

const result = src.slice(0, insertIdx) + ifSimCode + src.slice(insertIdx);
fs.writeFileSync(targetFile, result, 'utf-8');
console.log('JS insertion OK. New size:', result.length);
