/* ============================================================
   Wildlife Kingdom — js/audio.js
   Procedural WebAudio: gentle marimba soundtrack + synthesized
   SFX. No audio files, fully original. Global: WK.AudioSys
   ============================================================ */
(function(){
'use strict';
const WK = window.WK;

const A = {
  ctx:null, master:null, musicBus:null, sfxBus:null,
  enabled:true, musicOn:true, scene:'menu',
  timer:null, nextNote:0, step:0,
};

function ensure(){
  if(A.ctx){ if(A.ctx.state==='suspended') A.ctx.resume(); return true; }
  const AC = window.AudioContext || window.webkitAudioContext;
  if(!AC) return false;
  A.ctx = new AC();
  A.master = A.ctx.createGain(); A.master.gain.value=0.9; A.master.connect(A.ctx.destination);
  A.musicBus = A.ctx.createGain(); A.musicBus.gain.value = 0.5; A.musicBus.connect(A.master);
  A.sfxBus = A.ctx.createGain(); A.sfxBus.gain.value = 0.85; A.sfxBus.connect(A.master);
  loadPrefs();
  startScheduler();
  return true;
}

function loadPrefs(){
  try{
    const p = JSON.parse(localStorage.getItem('wk.audio')||'{}');
    if(p.enabled===false) A.enabled=false;
    if(p.musicOn===false) A.musicOn=false;
  }catch(e){}
}
function savePrefs(){
  try{ localStorage.setItem('wk.audio', JSON.stringify({enabled:A.enabled,musicOn:A.musicOn})); }catch(e){}
}

/* ---------- tiny synth helpers ---------- */
function tone(opt){
  // {f, f2, t='sine', dur, vol, bus, at, attack, release}
  const c=A.ctx; if(!c) return;
  const at = opt.at || c.currentTime;
  const o=c.createOscillator(), g=c.createGain();
  o.type=opt.t||'sine';
  o.frequency.setValueAtTime(opt.f,at);
  if(opt.f2) o.frequency.exponentialRampToValueAtTime(Math.max(20,opt.f2), at+(opt.dur||0.2));
  const v=(opt.vol==null?0.2:opt.vol);
  g.gain.setValueAtTime(0.0001,at);
  g.gain.linearRampToValueAtTime(v, at+(opt.attack||0.008));
  g.gain.exponentialRampToValueAtTime(0.0001, at+(opt.dur||0.2));
  o.connect(g); g.connect(opt.bus||A.sfxBus);
  o.start(at); o.stop(at+(opt.dur||0.2)+0.05);
}
function noiseHit(opt){
  // {dur,vol,f,q,bus,at}
  const c=A.ctx; if(!c) return;
  const at=opt.at||c.currentTime;
  const len=Math.max(1,Math.floor(c.sampleRate*(opt.dur||0.15)));
  const buf=c.createBuffer(1,len,c.sampleRate);
  const d=buf.getChannelData(0);
  for(let i=0;i<len;i++) d[i]=(Math.random()*2-1)*(1-i/len);
  const src=c.createBufferSource(); src.buffer=buf;
  const flt=c.createBiquadFilter(); flt.type='bandpass'; flt.frequency.value=opt.f||800; flt.Q.value=opt.q||1;
  const g=c.createGain(); g.gain.value=opt.vol==null?0.25:opt.vol;
  src.connect(flt); flt.connect(g); g.connect(opt.bus||A.sfxBus);
  src.start(at);
}

/* ---------- SFX vocabulary (all synthesized) ---------- */
const SFX = {
  click(){ tone({f:660,f2:880,t:'triangle',dur:0.07,vol:0.12}); },
  place(){ noiseHit({dur:0.09,vol:0.3,f:420,q:0.8}); tone({f:180,f2:120,t:'sine',dur:0.12,vol:0.22}); },
  dig(){ noiseHit({dur:0.14,vol:0.22,f:300,q:0.7}); },
  coin(){ tone({f:988,t:'square',dur:0.06,vol:0.08}); tone({f:1319,t:'square',dur:0.16,vol:0.09,at:A.ctx&&A.ctx.currentTime+0.06}); },
  error(){ tone({f:220,f2:160,t:'sawtooth',dur:0.18,vol:0.12}); },
  pop(){ tone({f:520,f2:1040,t:'sine',dur:0.09,vol:0.15}); },
  cheer(){ [523,659,784,1047].forEach((f,i)=>tone({f,t:'triangle',dur:0.18,vol:0.1,at:A.ctx&&A.ctx.currentTime+i*0.07})); },
  roar(){ tone({f:150,f2:70,t:'sawtooth',dur:0.5,vol:0.14}); noiseHit({dur:0.4,vol:0.1,f:200,q:0.6}); },
  chirp(){ tone({f:1400,f2:2100,t:'sine',dur:0.12,vol:0.1}); tone({f:1800,f2:2400,t:'sine',dur:0.1,vol:0.08,at:A.ctx&&A.ctx.currentTime+0.1}); },
  splash(){ noiseHit({dur:0.3,vol:0.28,f:900,q:0.4}); },
  trash(){ noiseHit({dur:0.12,vol:0.25,f:250,q:0.9}); },
  unlock(){ [392,523,659,784].forEach((f,i)=>tone({f,t:'sine',dur:0.25,vol:0.11,at:A.ctx&&A.ctx.currentTime+i*0.09})); },
};
WK.AudioSys = {
  ensure,
  get enabled(){return A.enabled;},
  get musicOn(){return A.musicOn;},
  sfx(name){
    if(!A.enabled) return;
    if(!ensure()) return;
    const fn=SFX[name]; if(fn) fn();
  },
  setScene(s){ A.scene=s; },
  toggleSound(){ A.enabled=!A.enabled; savePrefs(); if(A.enabled) ensure(); WK.UI&&WK.UI.refreshSoundBtn&&WK.UI.refreshSoundBtn(); return A.enabled; },
  toggleMusic(){ A.musicOn=!A.musicOn; savePrefs(); return A.musicOn; },
};

/* ---------- music scheduler ----------
   Relaxed pentatonic marimba + warm pad, randomized gentle walk.
   Menu: slower & dreamier. Game: slightly brighter tempo.      */
const PENTA=[0,2,4,7,9];
function nfreq(semi, base){ return base*Math.pow(2,semi/12); }

function startScheduler(){
  if(A.timer) return;
  A.timer=setInterval(()=>{
    if(!A.ctx || !A.enabled || !A.musicOn) return;
    const ahead = 0.45;
    const menu = A.scene==='menu';
    const stepDur = menu? 60/68/2 : 60/76/2;   // eighth notes
    const root = menu? 220 : 246.94;           // A3 / B3
    while(A.nextNote < A.ctx.currentTime + ahead){
      const t=Math.max(A.nextNote, A.ctx.currentTime+0.02);
      const bar=Math.floor(A.step/8), beat=A.step%8;
      // pad chord every bar
      if(beat===0){
        const deg=PENTA[(bar*2)%5];
        [0,7,12].forEach(iv=>tone({f:nfreq(deg+iv,root/2)*2,t:'sine',dur:stepDur*8,vol:0.035,bus:A.musicBus,at:t,attack:0.6}));
        tone({f:nfreq(PENTA[bar%5],root)/2,t:'triangle',dur:stepDur*3.6,vol:0.10,bus:A.musicBus,at:t,attack:0.01});
      }
      // pluck melody: sparse random walk on pentatonic
      const density = menu?0.42:0.55;
      if(WK.hash2(A.step,bar,7) < density){
        const idx = Math.floor(WK.hash2(A.step*13,bar,3)*PENTA.length);
        const oct = (WK.hash2(A.step,bar*3,11)<0.3)?12:0;
        tone({f:nfreq(PENTA[idx]+oct,root),t:'triangle',dur:stepDur*2.4,vol:0.085,bus:A.musicBus,at:t});
        if(WK.hash2(A.step,bar,23)<0.25)
          tone({f:nfreq(PENTA[idx]+oct+12,root)*2,t:'sine',dur:stepDur*1.6,vol:0.04,bus:A.musicBus,at:t+stepDur*0.5});
      }
      // soft shaker
      if(beat%2===1 && !menu) noiseHit({dur:0.03,vol:0.018,f:6000,q:1.2,bus:A.musicBus,at:t});
      A.nextNote = t + stepDur;
      A.step++;
    }
  },120);
}
})();
