/* eslint-disable no-undef */
// =====================================================
// Pendulum phase portrait viz
// ÿ + sin(y) = 0  ->  system:  ẏ = v,  v̇ = -sin(y)
// First integral: E = v^2/2 - cos(y)
// =====================================================

function PendulumViz() {
  const [gamma, setGamma] = useState(0); // damping; default 0 -> conservative
  const [eValues, setEValues] = useState([-0.7, -0.3, 0.2, 0.7, 1.0, 1.3, 1.8, 2.4]);
  const [showField, setShowField] = useState(true);
  const [showSeparatrix, setShowSeparatrix] = useState(true);
  const [showCenters, setShowCenters] = useState(true);

  // For damped pendulum, we just show field + trajectories from a couple of initial points
  const isDamped = Math.abs(gamma) > 1e-3;

  const F = useMemo(() => ([y, v]) => [v, -Math.sin(y) - gamma * v], [gamma]);

  const canvasRef = useCanvas((ctx, W, H) => {
    const TAU = 2 * Math.PI;
    const T = makeTransform({ xMin: -TAU, xMax: TAU, yMin: -3, yMax: 3, W, H, pad: 42 });
    drawAxes(ctx, T, {
      tickStep: Math.PI / 2,
      xLabel: 'y (угол)',
      yLabel: 'v (угловая скорость)',
      xTickFormatter: (x) => {
        const n = x / Math.PI;
        if (Math.abs(n) < 1e-3) return '0';
        if (Math.abs(n - Math.round(n)) < 1e-3) {
          const k = Math.round(n);
          if (k === 1) return 'π';
          if (k === -1) return '−π';
          return `${k}π`;
        }
        if (Math.abs(n * 2 - Math.round(n * 2)) < 1e-3) {
          const k = Math.round(n * 2);
          if (k === 1) return 'π/2';
          if (k === -1) return '−π/2';
          if (k === 3) return '3π/2';
          if (k === -3) return '−3π/2';
        }
        return '';
      },
      gridColor: 'rgba(255,255,255,0.035)',
    });

    // background vector field arrows
    if (showField) {
      drawVectorField(ctx, T, F, {
        nx: 26, ny: 12,
        color: 'rgba(60,55,40,0.4)',
        length: 12,
      });
    }

    if (!isDamped) {
      // Plot level sets of E = v^2/2 - cos(y) for each chosen E
      // Color: E<1 oscillation (purple-magenta gradient), E=1 separatrix (teal), E>1 rotation (orange)
      for (let i = 0; i < eValues.length; i++) {
        const E = eValues[i];
        const isSep = Math.abs(E - 1) < 0.02;
        let color;
        let lw;
        if (isSep) { color = '#1f8a76'; lw = 2.8; }
        else if (E < 1) {
          // gradient from purple (deep) to red as E approaches 1
          const t = Math.max(0, Math.min(1, (E + 1) / 2));
          const hue = 280 - t * 60; // 280 purple -> 220 blue
          color = `hsl(${hue}, 45%, 42%)`;
          lw = 2.0;
        }
        else {
          color = '#b8581f';
          lw = 2.0;
        }

        // sample y in [T.xMin, T.xMax]
        const samples = 800;
        let upperRun = [], lowerRun = [];
        const upperRuns = [], lowerRuns = [];
        for (let k = 0; k <= samples; k++) {
          const y = T.xMin + (k / samples) * (T.xMax - T.xMin);
          const val = 2 * (E + Math.cos(y));
          if (val >= 0) {
            const v = Math.sqrt(val);
            upperRun.push([y, v]);
            lowerRun.push([y, -v]);
          } else {
            if (upperRun.length) upperRuns.push(upperRun);
            if (lowerRun.length) lowerRuns.push(lowerRun);
            upperRun = []; lowerRun = [];
          }
        }
        if (upperRun.length) upperRuns.push(upperRun);
        if (lowerRun.length) lowerRuns.push(lowerRun);
        for (const r of upperRuns) drawPath(ctx, T, r, { color, lineWidth: lw });
        for (const r of lowerRuns) drawPath(ctx, T, r, { color, lineWidth: lw });
      }

      // explicit separatrix (E=1) so it's prominent
      if (showSeparatrix) {
        const samples = 800;
        const upper = [], lower = [];
        for (let k = 0; k <= samples; k++) {
          const y = T.xMin + (k / samples) * (T.xMax - T.xMin);
          const val = 2 * (1 + Math.cos(y));
          if (val >= 0) {
            const v = Math.sqrt(val);
            upper.push([y, v]);
            lower.push([y, -v]);
          }
        }
        drawPath(ctx, T, upper, { color: '#1f8a76', lineWidth: 3 });
        drawPath(ctx, T, lower, { color: '#1f8a76', lineWidth: 3 });
      }
    } else {
      // damped: integrate trajectories from several initial conditions
      const inits = [];
      for (let v0 = 0.5; v0 <= 2.5; v0 += 0.5) inits.push([0, v0], [0, -v0]);
      for (let y0 = -5; y0 <= 5; y0 += 2) inits.push([y0, 0.0]);
      inits.forEach((p, idx) => {
        const fwd = integrateVector(F, 0, p, 30, 0.02, { yBound: 1e3, maxSteps: 4000 });
        const pts = fwd.map(s => s.y);
        drawPath(ctx, T, pts, { color: TRAJ_COLORS[idx % TRAJ_COLORS.length], lineWidth: 1.8, clip: false });
      });
    }

    // equilibrium markers
    if (showCenters) {
      // centers at (2πk, 0)
      for (let k = -1; k <= 1; k++) {
        const x = k * 2 * Math.PI;
        if (x >= T.xMin && x <= T.xMax) {
          drawDot(ctx, T, x, 0, { color: '#fbf6e9', stroke: '#2a5e9e', r: 5.5, lineWidth: 2.4 });
        }
      }
      // saddles at (π+2πk, 0)
      for (let k = -2; k <= 1; k++) {
        const x = Math.PI + k * 2 * Math.PI;
        if (x >= T.xMin && x <= T.xMax) {
          ctx.save();
          ctx.strokeStyle = '#a8392c';
          ctx.lineWidth = 2.6;
          const px = T.X(x), py = T.Y(0);
          const r = 6;
          ctx.beginPath();
          ctx.moveTo(px - r, py - r); ctx.lineTo(px + r, py + r);
          ctx.moveTo(px + r, py - r); ctx.lineTo(px - r, py + r);
          ctx.stroke();
          ctx.restore();
        }
      }
    }

  }, [gamma, eValues, showField, showSeparatrix, showCenters, isDamped, F]);

  return (
    <VizWrap
      title={`Маятник ${isDamped ? 'с трением' : 'без трения'}:`}
      formula={isDamped ? `ÿ + γẏ + sin y = 0` : `ÿ + sin y = 0`}
      captionUnder="Линии уровня энергии E = v²/2 − cos y. Внутри сепаратрисы (E < 1) — колебания вокруг центра. На E = 1 — сепаратриса через сёдла. Снаружи — вращательные режимы."
      controls={
        <React.Fragment>
          <h4>Параметры</h4>
          <Slider label="Трение γ" value={gamma} setValue={setGamma} min={0} max={1.0} step={0.02} />

          <h4>Линии уровня энергии</h4>
          {!isDamped ? (
            <React.Fragment>
              <div className="ctl-note">Выберите значения E (значения, на которых рисуются изолинии E = v²/2 − cos y):</div>
              {eValues.map((e, i) => (
                <Slider key={i} label={`E${i + 1}`} value={e} setValue={(v) => setEValues(prev => prev.map((x, j) => j === i ? v : x))} min={-1} max={3} step={0.05} />
              ))}
            </React.Fragment>
          ) : (
            <div className="ctl-note">С трением энергия не сохраняется — рисуются траектории, спирально притягивающиеся к центрам (2πk, 0) (устойчивые фокусы).</div>
          )}

          <h4>Отображение</h4>
          <Toggle label="Векторное поле" value={showField} setValue={setShowField} />
          {!isDamped ? <Toggle label="Сепаратриса E = 1" value={showSeparatrix} setValue={setShowSeparatrix} /> : null}
          <Toggle label="Особые точки" value={showCenters} setValue={setShowCenters} />
        </React.Fragment>
      }
    >
      <canvas ref={canvasRef} />
    </VizWrap>
  );
}

window.PendulumViz = PendulumViz;
