// F14. Резонанс (§36 в ch4) — пружина + внешняя сила.
(function(){
'use strict';
const B = () => window.PHYS9_FLAG_BASE;
const C = () => window.PHYS9_COLORS || {};

function init(secId){
  if (!B()) return false;
  const body = ''
    + '<div class="flag-sliders">'
      + '<label>$m$, кг: <b id="F14-mv">1</b><input type="range" id="F14-m" min="0.1" max="5" step="0.1" value="1"></label>'
      + '<label>$k$, Н/м: <b id="F14-kv">40</b><input type="range" id="F14-k" min="5" max="200" step="5" value="40"></label>'
      + '<label>Внешняя частота $\\nu_{вн}$, Гц: <b id="F14-fv">1.00</b><input type="range" id="F14-f" min="0.05" max="3" step="0.05" value="1"></label>'
      + '<label>Затухание $\\gamma$: <b id="F14-gv">0.1</b><input type="range" id="F14-g" min="0.02" max="1" step="0.02" value="0.1"></label>'
    + '</div>'
    + '<canvas id="F14-cv" class="flag-canvas" width="700" height="380" style="height:380px"></canvas>'
    + '<div class="flag-controls">'
      + '<button class="flag-btn primary" id="F14-go">Запустить</button>'
      + '<button class="flag-btn" id="F14-reset">Сброс</button>'
      + '<button class="flag-btn" id="F14-tune">Настроить на резонанс</button>'
    + '</div>'
    + '<div class="flag-stats">'
      + '<div class="flag-stat"><span class="lbl">$\\nu_{собств}$</span><span class="val" id="F14-nu0">1.01 Гц</span></div>'
      + '<div class="flag-stat"><span class="lbl">$\\nu_{внешн}$</span><span class="val" id="F14-nuext">1.00 Гц</span></div>'
      + '<div class="flag-stat"><span class="lbl">Амплитуда</span><span class="val" id="F14-A">0 м</span></div>'
      + '<div class="flag-stat"><span class="lbl">Состояние</span><span class="val" id="F14-mode">—</span></div>'
    + '</div>'
    + '<div class="flag-feedback" id="F14-fb"></div>';

  const card = B().makeCard(secId,
    'F14. Резонанс пружинного маятника',
    'Слева: пружина с грузом + внешняя сила. Справа: график амплитуды от частоты — резонансный пик при $\\nu_{вн} = \\nu_{собств}$.',
    body);
  if (!card) return false;

  const cv = document.getElementById('F14-cv');
  const ctx = cv.getContext('2d');
  const W = cv.width, H = cv.height;
  const cx = W * 0.25;

  let st = { x: 0, v: 0, t: 0, running: false, ampHistory: [] };

  function nuOwn(){
    const m = +document.getElementById('F14-m').value;
    const k = +document.getElementById('F14-k').value;
    return Math.sqrt(k/m) / (2*Math.PI);
  }

  function update(){
    document.getElementById('F14-mv').textContent = (+document.getElementById('F14-m').value).toFixed(1);
    document.getElementById('F14-kv').textContent = +document.getElementById('F14-k').value;
    document.getElementById('F14-fv').textContent = (+document.getElementById('F14-f').value).toFixed(2);
    document.getElementById('F14-gv').textContent = (+document.getElementById('F14-g').value).toFixed(2);
    document.getElementById('F14-nu0').textContent = nuOwn().toFixed(2) + ' Гц';
    document.getElementById('F14-nuext').textContent = (+document.getElementById('F14-f').value).toFixed(2) + ' Гц';
  }

  function reset(){
    st = { x: 0, v: 0, t: 0, running: false, ampHistory: [] };
    document.getElementById('F14-go').textContent = 'Запустить';
    draw();
  }

  function tick(dt){
    if (!st.running) { draw(); return; }
    const m = +document.getElementById('F14-m').value;
    const k = +document.getElementById('F14-k').value;
    const omega_ext = 2*Math.PI*(+document.getElementById('F14-f').value);
    const gamma = +document.getElementById('F14-g').value;
    const F0 = 5;
    /* m·x'' = -kx - γ·v + F0·cos(ωt) */
    const N = 8;
    const ddt = dt / N;
    for (let i = 0; i < N; i++){
      const F = F0 * Math.cos(omega_ext * st.t);
      const a = (-k*st.x - gamma*st.v + F) / m;
      st.v += a * ddt;
      st.x += st.v * ddt;
      st.t += ddt;
    }
    /* записываем амплитуду */
    st.ampHistory.push({ t: st.t, x: st.x });
    if (st.ampHistory.length > 800) st.ampHistory.shift();
    /* текущая амплитуда — максимум за последние 2 с */
    let amp = 0;
    for (const h of st.ampHistory){
      if (st.t - h.t < 5) amp = Math.max(amp, Math.abs(h.x));
    }
    document.getElementById('F14-A').textContent = amp.toFixed(3) + ' м';
    /* классификация */
    const nu0 = nuOwn();
    const nuExt = +document.getElementById('F14-f').value;
    const mode = document.getElementById('F14-mode');
    const fb = document.getElementById('F14-fb');
    if (Math.abs(nu0 - nuExt) / nu0 < 0.08){
      mode.textContent = 'РЕЗОНАНС!';
      mode.style.color = 'var(--fail,#dc2626)';
      fb.className = 'flag-feedback fail show';
      fb.innerHTML = '&#9888; РЕЗОНАНС! Амплитуда нарастает. Так разрушаются мосты — рота не должна маршировать через них в ногу.';
    } else if (Math.abs(nu0 - nuExt) / nu0 < 0.25){
      mode.textContent = 'близко к резонансу';
      mode.style.color = 'var(--warn,#f59e0b)';
      fb.className = 'flag-feedback warn show';
      fb.innerHTML = 'Близко к резонансу — амплитуда заметно больше внешней силы.';
    } else {
      mode.textContent = 'вынужденные колебания';
      mode.style.color = 'var(--ok,#10b981)';
      fb.className = 'flag-feedback';
    }
    draw();
  }

  function draw(){
    const col = C();
    ctx.fillStyle = col.bg || '#fafafa';
    ctx.fillRect(0, 0, W, H);
    /* левая половина — пружина с грузом */
    /* потолок */
    ctx.fillStyle = col.surface || '#a16207';
    ctx.fillRect(cx - 60, 30, 120, 14);
    /* пружина */
    const massY = 160 + st.x * 400;
    ctx.strokeStyle = col.forceSpring || '#d97706';
    ctx.lineWidth = 2.5;
    ctx.beginPath();
    ctx.moveTo(cx, 44);
    for (let i = 0; i < 14; i++){
      const t = i / 14;
      const y = 44 + t * (massY - 44);
      const x = cx + Math.sin(t * 14 * Math.PI) * 12;
      ctx.lineTo(x, y);
    }
    ctx.lineTo(cx, massY);
    ctx.stroke();
    /* груз */
    ctx.fillStyle = col.forceGravity || '#2563eb';
    ctx.fillRect(cx - 25, massY, 50, 30);
    ctx.strokeStyle = col.bodyAccent || '#1e293b';
    ctx.strokeRect(cx - 25, massY, 50, 30);
    /* нулевая линия */
    ctx.strokeStyle = col.grid || '#e5e7eb';
    ctx.setLineDash([4, 4]);
    ctx.beginPath();
    ctx.moveTo(cx - 60, 160); ctx.lineTo(cx + 60, 160);
    ctx.stroke();
    ctx.setLineDash([]);
    /* стрелка внешней силы */
    const F_now = 5 * Math.cos(2*Math.PI*(+document.getElementById('F14-f').value) * st.t);
    if (st.running){
      B().arrow(ctx, cx + 35, massY + 15, cx + 35 + F_now*8, massY + 15, col.acceleration || '#ea580c', 2);
    }
    /* правая половина — график x(t) */
    const gx = W/2 + 20, gy = 40, gw = W/2 - 40, gh = H - 90;
    ctx.fillStyle = col.bgSubtle || '#f8fafc';
    ctx.fillRect(gx, gy, gw, gh);
    ctx.strokeStyle = col.axis || '#1e293b';
    ctx.lineWidth = 1.5;
    ctx.strokeRect(gx, gy, gw, gh);
    /* центральная ось */
    ctx.strokeStyle = col.grid || '#e5e7eb';
    ctx.beginPath(); ctx.moveTo(gx, gy + gh/2); ctx.lineTo(gx + gw, gy + gh/2); ctx.stroke();
    /* линия x(t) */
    if (st.ampHistory.length > 1){
      const tMin = Math.max(0, st.t - 20);
      const tMax = st.t;
      let maxAbsX = 0.1;
      for (const h of st.ampHistory) if (h.t >= tMin) maxAbsX = Math.max(maxAbsX, Math.abs(h.x));
      ctx.strokeStyle = col.velocity || '#0891b2';
      ctx.lineWidth = 2;
      ctx.beginPath();
      let first = true;
      for (const h of st.ampHistory){
        if (h.t < tMin) continue;
        const px = gx + ((h.t - tMin)/(tMax - tMin)) * gw;
        const py = gy + gh/2 - (h.x / maxAbsX) * (gh/2 - 5);
        if (first){ ctx.moveTo(px, py); first = false; } else ctx.lineTo(px, py);
      }
      ctx.stroke();
    }
    ctx.fillStyle = col.textMuted || '#64748b';
    ctx.font = '11px Inter,sans-serif';
    ctx.fillText('x(t) — последние 20 с', gx + 6, gy + 14);
    ctx.fillText('t →', gx + gw - 24, gy + gh - 4);
  }

  document.getElementById('F14-go').addEventListener('click', () => {
    st.running = !st.running;
    document.getElementById('F14-go').textContent = st.running ? 'Пауза' : 'Запустить';
  });
  document.getElementById('F14-reset').addEventListener('click', reset);
  document.getElementById('F14-tune').addEventListener('click', () => {
    document.getElementById('F14-f').value = nuOwn().toFixed(2);
    update();
    reset();
  });
  ['F14-m','F14-k','F14-f','F14-g'].forEach(id => document.getElementById(id).addEventListener('input', update));

  update();
  reset();
  B().startLoop('F14', cv, tick);
  return true;
}

if (window.PHYS9_FLAG_BASE) window.PHYS9_FLAG_BASE.register('F14', { init: init, cleanup: function(){} });
else document.addEventListener('DOMContentLoaded', ()=>{
  if (window.PHYS9_FLAG_BASE) window.PHYS9_FLAG_BASE.register('F14', { init: init, cleanup: function(){} });
});

})();
