// The Mind page — brainwave-band selector with binaural-beat audio.
//
// Brainwaves below ~20 Hz are sub-audible. To make them perceivable we
// use binaural beats: two slightly-different audible tones (one in each
// ear) whose interference is heard by the brain as a pulsing rhythm at
// the difference frequency. Headphones required.
//
// Bands:
//   Delta   1-4 Hz    deep dreamless sleep, healing, unconscious
//   Theta   4-8 Hz    REM/dreams, deep meditation, trance, intuition
//   Alpha   8-13 Hz   relaxed wakefulness, light meditation, flow
//   Beta    13-30 Hz  ordinary waking thought, focus, alertness
//   Gamma   30+  Hz   peak cognition, insight, transcendent states

(function () {
  const root = document.getElementById("brainwaves");
  if (!root) return;
  const view = document.querySelector('.view[data-view="mind"]');
  if (!view) return;

  const canvas   = root.querySelector(".bw-wave");
  const ctx      = canvas.getContext("2d");
  const bandEls  = root.querySelectorAll(".bw-band");
  const titleEl  = root.querySelector(".bw-current-name");
  const hzEl     = root.querySelector(".bw-current-hz");
  const descEl   = root.querySelector(".bw-current-desc");
  const audioBtn = root.querySelector(".bw-audio");

  const BANDS = {
    delta: { hz: 2,  color: "#6366f1", carrierL: 200 },
    theta: { hz: 6,  color: "#a855f7", carrierL: 220 },
    alpha: { hz: 10, color: "#10b981", carrierL: 240 },
    beta:  { hz: 20, color: "#f59e0b", carrierL: 280 },
    gamma: { hz: 40, color: "#ef4444", carrierL: 320 }
  };

  let current = "alpha";
  let phase = 0;
  let lastTs = null;
  let rafId = null;

  let audioCtx = null;
  let oscL = null, oscR = null;
  let pannerL = null, pannerR = null;
  let gainNode = null;
  let audioOn = false;

  function t(key, fallback) {
    return (window.i18n && window.i18n.t) ? window.i18n.t(key, fallback) : fallback;
  }

  function sizeCanvas() {
    const dpr = window.devicePixelRatio || 1;
    const r = canvas.getBoundingClientRect();
    canvas.width  = Math.round(r.width  * dpr);
    canvas.height = Math.round(r.height * dpr);
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }

  // ----- Visual: wave animates at the band's actual rate ----------------
  function loop(ts) {
    if (lastTs == null) lastTs = ts;
    const dt = (ts - lastTs) / 1000;
    lastTs = ts;
    const hz = BANDS[current].hz;
    // Real rate, scaled down so fast bands don't strobe.
    phase += 2 * Math.PI * Math.sqrt(hz) * 0.5 * dt;
    render();
    rafId = requestAnimationFrame(loop);
  }
  function render() {
    const w = canvas.width  / (window.devicePixelRatio || 1);
    const h = canvas.height / (window.devicePixelRatio || 1);
    ctx.clearRect(0, 0, w, h);
    ctx.strokeStyle = "rgba(255, 255, 255, 0.06)";
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(0, h / 2); ctx.lineTo(w, h / 2); ctx.stroke();

    const hz = BANDS[current].hz;
    const cycles = 1 + Math.sqrt(hz) * 0.9;
    const amp = h * 0.36;
    const color = BANDS[current].color;

    const grad = ctx.createLinearGradient(0, 0, w, 0);
    grad.addColorStop(0,    "rgba(255, 255, 255, 0)");
    grad.addColorStop(0.12, color);
    grad.addColorStop(0.88, color);
    grad.addColorStop(1,    "rgba(255, 255, 255, 0.9)");

    ctx.strokeStyle = grad;
    ctx.lineWidth = 2.5;
    ctx.lineJoin = "round";
    ctx.lineCap  = "round";
    ctx.beginPath();
    const samples = Math.max(120, Math.floor(w / 2));
    for (let i = 0; i <= samples; i++) {
      const x = (i / samples) * w;
      const u = i / samples;
      const y = h / 2 - Math.sin(phase + 2 * Math.PI * cycles * u) * amp;
      if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
    }
    ctx.stroke();

    // glow
    ctx.globalAlpha = 0.3;
    ctx.strokeStyle = color;
    ctx.lineWidth = 8;
    ctx.beginPath();
    for (let i = 0; i <= samples; i++) {
      const x = (i / samples) * w;
      const u = i / samples;
      const y = h / 2 - Math.sin(phase + 2 * Math.PI * cycles * u) * amp;
      if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
    }
    ctx.stroke();
    ctx.globalAlpha = 1;
  }
  function start() {
    if (rafId) return;
    sizeCanvas();
    lastTs = null;
    rafId = requestAnimationFrame(loop);
  }
  function stop() {
    if (rafId) cancelAnimationFrame(rafId);
    rafId = null;
  }

  // ----- Audio: binaural beat -------------------------------------------
  function ensureAudio() {
    if (audioCtx) return;
    const Ctx = window.AudioContext || window.webkitAudioContext;
    if (!Ctx) return;
    audioCtx = new Ctx();
    gainNode = audioCtx.createGain();
    gainNode.gain.value = 0;
    gainNode.connect(audioCtx.destination);
  }
  function startBeat() {
    ensureAudio();
    if (!audioCtx) return;
    stopBeat(true);
    const b = BANDS[current];
    const fL = b.carrierL;
    const fR = b.carrierL + b.hz;

    pannerL = audioCtx.createStereoPanner();
    pannerR = audioCtx.createStereoPanner();
    pannerL.pan.value = -1;
    pannerR.pan.value =  1;
    pannerL.connect(gainNode);
    pannerR.connect(gainNode);

    oscL = audioCtx.createOscillator();
    oscR = audioCtx.createOscillator();
    oscL.type = "sine"; oscR.type = "sine";
    oscL.frequency.value = fL;
    oscR.frequency.value = fR;
    oscL.connect(pannerL);
    oscR.connect(pannerR);
    oscL.start();
    oscR.start();

    const now = audioCtx.currentTime;
    gainNode.gain.cancelScheduledValues(now);
    gainNode.gain.setValueAtTime(0, now);
    gainNode.gain.linearRampToValueAtTime(0.06, now + 0.25);
  }
  function stopBeat(immediate) {
    if (!audioCtx) return;
    const now = audioCtx.currentTime;
    if (gainNode) {
      gainNode.gain.cancelScheduledValues(now);
      gainNode.gain.setValueAtTime(gainNode.gain.value, now);
      gainNode.gain.linearRampToValueAtTime(0, now + (immediate ? 0.01 : 0.18));
    }
    const a = oscL, b = oscR;
    setTimeout(() => {
      try { if (a) a.stop(); } catch (_) {}
      try { if (b) b.stop(); } catch (_) {}
    }, immediate ? 30 : 220);
    oscL = oscR = null;
  }
  function setAudio(on) {
    audioOn = on;
    audioBtn.setAttribute("aria-pressed", on ? "true" : "false");
    audioBtn.textContent = on
      ? t("page.mind.bw_sound_on", "Sound on")
      : t("page.mind.bw_sound_off", "Sound off");
    audioBtn.classList.toggle("on", on);
    if (on) startBeat();
    else stopBeat();
  }

  // ----- UI sync ---------------------------------------------------------
  function applyCurrent() {
    const b = BANDS[current];
    titleEl.textContent = t("page.mind.bw_band_" + current, current.toUpperCase());
    titleEl.style.color = b.color;
    hzEl.textContent = b.hz + " Hz";
    descEl.innerHTML = t("page.mind.bw_desc_" + current, "");
    bandEls.forEach((el) => el.classList.toggle("active", el.dataset.band === current));
    if (audioOn) startBeat();
  }

  bandEls.forEach((el) => el.addEventListener("click", () => {
    current = el.dataset.band;
    applyCurrent();
  }));
  audioBtn.addEventListener("click", () => {
    setAudio(!audioOn);
    if (audioCtx && audioCtx.state === "suspended") audioCtx.resume();
  });
  window.addEventListener("resize", sizeCanvas);
  window.addEventListener("langchange", () => {
    audioBtn.textContent = audioOn
      ? t("page.mind.bw_sound_on", "Sound on")
      : t("page.mind.bw_sound_off", "Sound off");
    applyCurrent();
  });

  function isActive() { return view.classList.contains("active"); }
  if ("MutationObserver" in window) {
    new MutationObserver(() => {
      if (isActive()) start();
      else { stop(); if (audioOn) setAudio(false); }
    }).observe(view, { attributes: true, attributeFilter: ["class"] });
    if (isActive()) start();
  } else {
    start();
  }
  document.addEventListener("visibilitychange", () => {
    if (document.hidden) { stop(); if (audioOn) setAudio(false); }
    else if (isActive()) start();
  });

  applyCurrent();
})();
