// =============================================================
// AUDIO — Web Audio synth SFX + a looping music track.
// Zero asset files: everything is generated with oscillators.
// AudioContext resumes on first user gesture (browser policy).
// Usage:  Audio.sfx('catch');  Audio.startMusic();  Audio.stopMusic();
// =============================================================
window.Audio = (function () {
  let ctx = null;
  let master = null;
  let musicNodes = null;
  let unlocked = false;
  let muted = false;

  function ensure() {
    if (ctx) return ctx;
    const AC = window.AudioContext || window.webkitAudioContext;
    ctx = new AC();
    master = ctx.createGain();
    master.gain.value = 0.5;
    master.connect(ctx.destination);
    return ctx;
  }

  // Resume on first gesture so sound is allowed to play.
  function unlock() {
    ensure();
    if (ctx.state === 'suspended') ctx.resume();
    unlocked = true;
  }

  function blip({ type = 'sine', freq = 440, dur = 0.12, vol = 0.3, slideTo = null, attack = 0.005 }) {
    if (!unlocked || muted) return;
    ensure();
    const t = ctx.currentTime;
    const osc = ctx.createOscillator();
    const g = ctx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freq, t);
    if (slideTo) osc.frequency.exponentialRampToValueAtTime(Math.max(1, slideTo), t + dur);
    g.gain.setValueAtTime(0.0001, t);
    g.gain.exponentialRampToValueAtTime(vol, t + attack);
    g.gain.exponentialRampToValueAtTime(0.0001, t + dur);
    osc.connect(g);
    g.connect(master);
    osc.start(t);
    osc.stop(t + dur + 0.02);
  }

  function noise({ dur = 0.2, vol = 0.4 }) {
    if (!unlocked || muted) return;
    ensure();
    const t = ctx.currentTime;
    const frames = Math.floor(ctx.sampleRate * dur);
    const buffer = ctx.createBuffer(1, frames, ctx.sampleRate);
    const data = buffer.getChannelData(0);
    for (let i = 0; i < frames; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / frames);
    const src = ctx.createBufferSource();
    src.buffer = buffer;
    const g = ctx.createGain();
    g.gain.value = vol;
    const filter = ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.value = 1200;
    src.connect(filter); filter.connect(g); g.connect(master);
    src.start(t);
  }

  // ---- named sound effects ----
  const SFX = {
    move:  () => blip({ type: 'sine',     freq: 320, dur: 0.05, vol: 0.08 }),
    catch: () => { blip({ type: 'triangle', freq: 520, slideTo: 880, dur: 0.14, vol: 0.3 });
                   blip({ type: 'sine', freq: 1040, dur: 0.08, vol: 0.15 }); },
    score: () => blip({ type: 'square',   freq: 660, slideTo: 990, dur: 0.18, vol: 0.2 }),
    hit:   () => { noise({ dur: 0.3, vol: 0.5 });
                   blip({ type: 'sawtooth', freq: 180, slideTo: 60, dur: 0.35, vol: 0.4 }); },
    lose:  () => blip({ type: 'sawtooth', freq: 300, slideTo: 80, dur: 0.6, vol: 0.4 }),
    start: () => blip({ type: 'square',   freq: 440, slideTo: 880, dur: 0.25, vol: 0.25 }),
    miss:  () => blip({ type: 'sine',     freq: 240, slideTo: 120, dur: 0.2, vol: 0.2 }),
  };

  function sfx(name) {
    const fn = SFX[name];
    if (fn) fn();
  }

  // ---- looping music: a simple arpeggio sequencer ----
  function startMusic() {
    if (!unlocked || muted || musicNodes) return;
    ensure();
    const notes = [220, 277, 330, 277, 247, 294, 370, 294]; // gentle minor-ish loop
    let step = 0;
    const bpmGain = ctx.createGain();
    bpmGain.gain.value = 0.06;
    bpmGain.connect(master);
    const id = setInterval(() => {
      if (muted) return;
      const t = ctx.currentTime;
      const osc = ctx.createOscillator();
      const g = ctx.createGain();
      osc.type = 'triangle';
      osc.frequency.value = notes[step % notes.length];
      g.gain.setValueAtTime(0.0001, t);
      g.gain.exponentialRampToValueAtTime(1, t + 0.02);
      g.gain.exponentialRampToValueAtTime(0.0001, t + 0.28);
      osc.connect(g); g.connect(bpmGain);
      osc.start(t); osc.stop(t + 0.3);
      step++;
    }, 260);
    musicNodes = { id, bpmGain };
  }

  function stopMusic() {
    if (!musicNodes) return;
    clearInterval(musicNodes.id);
    try { musicNodes.bpmGain.disconnect(); } catch (e) {}
    musicNodes = null;
  }

  function toggleMute() { muted = !muted; if (muted) stopMusic(); return muted; }

  return { unlock, sfx, startMusic, stopMusic, toggleMute, get muted() { return muted; } };
})();
