/* eslint-disable no-undef */
// =====================================================
// Direction field viz: y' = f(x,y) with integral curves
// Default: y' = x - y
// =====================================================

const DF_EQUATIONS = {
  'x-y': {
    label: "y' = x − y",
    f: (x, y) => x - y,
    asymptote: { fn: (x) => x - 1, label: 'y = x − 1' },
    defaultC: [-4, -1.5, 0, 1.5, 4],
    // exact: y = x - 1 + C e^{-x}
    cFromInit: (x0, y0) => (y0 - x0 + 1) * Math.exp(x0),
  },
  'logistic': {
    label: "y' = y(1 − y)",
    f: (x, y) => y * (1 - y),
    asymptote: { fn: (x) => 1, label: 'y = 1' },
    defaultC: [-2, -0.5, 0.5, 1.5, 3],
    cFromInit: (x0, y0) => y0,
  },
  'sin': {
    label: "y' = sin(x) − y",
    f: (x, y) => Math.sin(x) - y,
    asymptote: null,
    defaultC: [-2, -1, 0, 1, 2],
    cFromInit: (x0, y0) => y0,
  },
  'xy': {
    label: "y' = xy",
    f: (x, y) => x * y,
    asymptote: null,
    defaultC: [-1.5, -0.5, 0.3, 1, 2],
    cFromInit: (x0, y0) => y0,
  },
};

function DirectionFieldViz() {
  const [eqKey, setEqKey] = useState('x-y');
  const [density, setDensity] = useState(22);
  const [showField, setShowField] = useState(true);
  const [showAsym, setShowAsym] = useState(true);
  const [curves, setCurves] = useState([
    { x0: 0, y0: -3, color: TRAJ_COLORS[0] }, // C=-4 for x-y
    { x0: 0, y0: -2.5, color: TRAJ_COLORS[1] }, // C=-1.5
    { x0: 0, y0: -1, color: TRAJ_COLORS[2] },   // C=0
    { x0: 0, y0: 0.5, color: TRAJ_COLORS[3] },   // C=1.5
    { x0: 0, y0: 3, color: TRAJ_COLORS[4] },     // C=4
  ]);

  const eq = DF_EQUATIONS[eqKey];

  // when eqKey changes, recompute default curves: pick initial conditions giving the defaultC values
  useEffect(() => {
    // we'll seed curves at x=0 to make C values intuitive
    const cs = eq.defaultC;
    setCurves(cs.map((C, i) => {
      // for x-y: y(0) = -1 + C
      // for others: y(0) = C (we set cFromInit to identity)
      let y0;
      if (eqKey === 'x-y') y0 = -1 + C;
      else y0 = C;
      return { x0: 0, y0, color: TRAJ_COLORS[i % TRAJ_COLORS.length] };
    }));
  }, [eqKey]);

  const canvasRef = useCanvas((ctx, W, H) => {
    const T = makeTransform({ xMin: -3, xMax: 3, yMin: -3, yMax: 3, W, H, pad: 36 });
    drawAxes(ctx, T, { tickStep: 1, xLabel: 'x', yLabel: 'y' });
    if (showField) {
      drawDirectionField(ctx, T, eq.f, {
        nx: density, ny: Math.round(density * 0.75),
        color: 'rgba(60,55,40,0.5)',
        length: 18,
      });
    }
    // asymptote
    if (showAsym && eq.asymptote) {
      const xs = [];
      for (let i = 0; i <= 100; i++) xs.push(T.xMin + (i / 100) * (T.xMax - T.xMin));
      const pts = xs.map(x => [x, eq.asymptote.fn(x)]);
      drawPath(ctx, T, pts, { color: '#b8581f', dashed: true, lineWidth: 1.8 });
    }
    // integral curves
    for (const c of curves) {
      const fwd = integrateScalar(eq.f, c.x0, c.y0, T.xMax + 0.5, 0.02, { yBound: 50 });
      const bwd = integrateScalar(eq.f, c.x0, c.y0, T.xMin - 0.5, 0.02, { yBound: 50 });
      const full = bwd.slice().reverse().concat(fwd.slice(1));
      drawPath(ctx, T, full, { color: c.color, lineWidth: 2.6 });
    }
  }, [eqKey, density, showField, showAsym, curves]);

  // Build a human-readable label for each curve
  const curveLabel = (c) => {
    const C = eq.cFromInit(c.x0, c.y0);
    if (eqKey === 'x-y') {
      // exact integration constant — round to 0.1
      const r = Math.abs(C) < 0.05 ? 0 : Number(C.toFixed(2));
      return `C = ${r}`;
    }
    // for others, show the initial value y(x0) = y0
    const x0r = Number(c.x0.toFixed(2));
    const y0r = Number(c.y0.toFixed(2));
    return `y(${x0r}) = ${y0r}`;
  };

  // click to add curves
  const onClick = (e) => {
    const rect = canvasRef.current.getBoundingClientRect();
    const px = e.clientX - rect.left;
    const py = e.clientY - rect.top;
    const W = rect.width, H = rect.height;
    const T = makeTransform({ xMin: -3, xMax: 3, yMin: -3, yMax: 3, W, H, pad: 36 });
    const xMin = -3, xMax = 3, yMin = -3, yMax = 3, pad = 36;
    const x = xMin + (px - pad) / (W - 2 * pad) * (xMax - xMin);
    const y = yMin + ((H - py - pad) / (H - 2 * pad)) * (yMax - yMin);
    const newC = { x0: x, y0: y, color: TRAJ_COLORS[curves.length % TRAJ_COLORS.length] };
    setCurves(prev => [...prev, newC]);
  };

  const reset = () => {
    const cs = eq.defaultC;
    setCurves(cs.map((C, i) => ({
      x0: 0, y0: eqKey === 'x-y' ? -1 + C : C,
      color: TRAJ_COLORS[i % TRAJ_COLORS.length],
    })));
  };

  return (
    <VizWrap
      title="Поле направлений и интегральные кривые"
      formula={eq.label}
      captionUnder="Каждая короткая черта — наклон в точке плоскости. Интегральная кривая всегда касается поля. Кликни по полю, чтобы добавить новую кривую."
      controls={
        <React.Fragment>
          <h4>Уравнение</h4>
          <ButtonGroup
            options={[
              { value: 'x-y',     label: "y′ = x − y" },
              { value: 'logistic',label: "y′ = y(1−y)" },
              { value: 'sin',     label: "y′ = sin x − y" },
              { value: 'xy',      label: "y′ = xy" },
            ]}
            value={eqKey}
            setValue={setEqKey}
            columns={1}
          />

          <h4>Поле направлений</h4>
          <Slider label="Плотность" value={density} setValue={setDensity} min={8} max={36} step={1} formatter={(v) => Math.round(v)} />
          <Toggle label="Показывать поле" value={showField} setValue={setShowField} />
          {eq.asymptote ? <Toggle label="Асимптота" value={showAsym} setValue={setShowAsym} /> : null}

          <h4>Кривые</h4>
          <div className="ctl-legend">
            {curves.map((c, i) => (
              <div key={i} className="ctl-legend-row">
                <span className="ctl-legend-swatch" style={{ background: c.color }}></span>
                <span>{curveLabel(c)}</span>
              </div>
            ))}
            {eq.asymptote && showAsym ? (
              <div className="ctl-legend-row" style={{ color: '#b8581f', marginTop: 4 }}>
                <span className="ctl-legend-swatch dashed" style={{ color: '#b8581f' }}></span>
                <span>{eq.asymptote.label}</span>
              </div>
            ) : null}
          </div>
          <div className="ctl-note">Кликни по графику, чтобы запустить кривую из этой точки.</div>
          <div className="ctl-btn" onClick={reset} style={{ marginTop: 10 }}>Сбросить</div>
        </React.Fragment>
      }
    >
      <canvas ref={canvasRef} onClick={onClick} style={{ cursor: 'crosshair' }} />
    </VizWrap>
  );
}

window.DirectionFieldViz = DirectionFieldViz;
