/* =========================================================================
   LIFE OS — audio.jsx
   A fully synthesized sound design kit (Web Audio API, zero asset files).
   Warm, soft, toy-like. Lazily inits on first user gesture. Mutable.
   window.SFX.play('click'|'gain'|'merge'|'discover'|'level'|'shadow'|
                   'heal'|'breakthrough'|'breach'|'ui'|'error'|'rest'|
                   'care'|'hatch'|'pop'|'maskon'|'maskoff'|'capstone')
   ========================================================================= */
(function () {
  let ctx = null, master = null, muted = false;
  const now = () => ctx.currentTime;

  function init() {
    if (ctx) return;
    try {
      ctx = new (window.AudioContext || window.webkitAudioContext)();
      master = ctx.createGain();
      master.gain.value = 0.5;
      // gentle master lowpass so nothing is harsh
      const lp = ctx.createBiquadFilter();
      lp.type = 'lowpass'; lp.frequency.value = 6800;
      master.connect(lp); lp.connect(ctx.destination);
    } catch (e) { ctx = null; }
  }
  function resume(){ if(ctx && ctx.state==='suspended') ctx.resume(); }

  function env(node, t0, a, d, peak, sus, rel, dur){
    const g = node.gain;
    g.cancelScheduledValues(t0);
    g.setValueAtTime(0, t0);
    g.linearRampToValueAtTime(peak, t0+a);
    g.linearRampToValueAtTime(sus, t0+a+d);
    g.setValueAtTime(sus, t0+Math.max(a+d,dur-rel));
    g.linearRampToValueAtTime(0, t0+dur);
  }

  function tone({freq=440, type='sine', t=0, dur=.18, peak=.3, sus=.12, a=.005, d=.05, rel=.08, detune=0, glideTo=0, pan=0}) {
    if(!ctx) return;
    const t0 = now()+t;
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = type; o.frequency.setValueAtTime(freq, t0); o.detune.value = detune;
    if (glideTo) o.frequency.exponentialRampToValueAtTime(glideTo, t0+dur);
    const p = ctx.createStereoPanner ? ctx.createStereoPanner() : null;
    if(p){ p.pan.value = pan; o.connect(g); g.connect(p); p.connect(master); }
    else { o.connect(g); g.connect(master); }
    env(g, t0, a, d, peak, sus, rel, dur);
    o.start(t0); o.stop(t0+dur+.02);
  }

  function noise({t=0, dur=.2, peak=.2, type='highpass', freq=1200, q=1, sweep=0}) {
    if(!ctx) return;
    const t0 = now()+t;
    const len = Math.floor(ctx.sampleRate*dur);
    const buf = ctx.createBuffer(1, len, ctx.sampleRate);
    const data = buf.getChannelData(0);
    for (let i=0;i<len;i++) data[i] = (Math.random()*2-1);
    const src = ctx.createBufferSource(); src.buffer = buf;
    const f = ctx.createBiquadFilter(); f.type=type; f.frequency.setValueAtTime(freq,t0); f.Q.value=q;
    if (sweep) f.frequency.exponentialRampToValueAtTime(Math.max(80,sweep), t0+dur);
    const g = ctx.createGain();
    src.connect(f); f.connect(g); g.connect(master);
    env(g, t0, .005, .04, peak, peak*.4, .05, dur);
    src.start(t0); src.stop(t0+dur+.02);
  }

  // pentatonic-ish helpers for pleasant arps
  const SC = [0,2,4,7,9,12,14,16];
  const semis = n => 261.63 * Math.pow(2, n/12);

  const R = {
    ui(){ tone({freq:880,type:'triangle',dur:.06,peak:.12,sus:.04,d:.02,rel:.04}); },
    click(){
      const base = 360 + Math.random()*40;
      tone({freq:base,type:'triangle',dur:.12,peak:.26,sus:.05,glideTo:base*1.5,d:.03,rel:.07,pan:(Math.random()*.4-.2)});
      tone({freq:base*2,type:'sine',dur:.08,peak:.08,sus:.02});
    },
    gain(){ tone({freq:660,type:'sine',dur:.1,peak:.16,glideTo:990,sus:.05}); },
    care(){ tone({freq:520,type:'sine',dur:.14,peak:.2,glideTo:780,sus:.06,d:.04}); tone({freq:780,t:.06,type:'triangle',dur:.12,peak:.1}); },
    pop(){ tone({freq:300,type:'sine',dur:.1,peak:.24,glideTo:900,a:.002,d:.02,sus:.0,rel:.05}); },
    level(){ [0,4,7].forEach((n,i)=>tone({freq:semis(12+n),type:'triangle',t:i*.07,dur:.18,peak:.18,sus:.06})); },
    discover(){ [0,2,4,7,9].forEach((n,i)=>tone({freq:semis(12+SC[i]),type:'triangle',t:i*.08,dur:.3,peak:.2,sus:.08,rel:.16})); },
    merge(){
      [0,.09,.18].forEach((t,i)=>tone({freq:semis(SC[i]),type:'sine',t,dur:.5,peak:.16,sus:.08,glideTo:semis(12+SC[i]),rel:.3}));
      noise({t:.18,dur:.4,peak:.06,type:'bandpass',freq:1400,sweep:400});
    },
    heal(){ [0,4,7,11].forEach((n,i)=>tone({freq:semis(n),type:'sine',t:i*.1,dur:.6,peak:.14,sus:.07,rel:.4})); },
    shadow(){ tone({freq:180,type:'sawtooth',dur:.6,peak:.16,glideTo:60,sus:.08,rel:.3}); noise({dur:.5,peak:.08,type:'lowpass',freq:600,sweep:120}); },
    breakthrough(){
      [0,7,12,16,19].forEach((n,i)=>tone({freq:semis(n),type:'sine',t:i*.12,dur:1.6,peak:.13,sus:.07,a:.05,rel:1.0}));
      noise({t:.1,dur:1.4,peak:.04,type:'highpass',freq:2000});
    },
    capstone(){ [0,4,7,12,16,19,24].forEach((n,i)=>tone({freq:semis(n),type:'sine',t:i*.16,dur:2.2,peak:.12,sus:.06,a:.06,rel:1.4})); },
    breach(){ noise({dur:.5,peak:.18,type:'bandpass',freq:2600,q:6,sweep:300}); tone({freq:140,type:'square',dur:.4,peak:.12,glideTo:70}); },
    error(){ tone({freq:160,type:'square',dur:.18,peak:.14,glideTo:120,sus:.05}); },
    rest(){ [0,5,7].forEach((n,i)=>tone({freq:semis(n)/2*2,type:'sine',t:i*.18,dur:1.0,peak:.12,sus:.06,a:.1,rel:.6})); },
    hatch(){ tone({freq:300,type:'triangle',dur:.3,peak:.2,glideTo:1200,sus:.05}); [0,4,7,12].forEach((n,i)=>tone({freq:semis(12+n),t:.2+i*.08,type:'sine',dur:.3,peak:.16})); },
    maskon(){ tone({freq:600,type:'square',dur:.2,peak:.12,glideTo:300}); tone({freq:300,t:.1,type:'sine',dur:.3,peak:.1}); },
    maskoff(){ tone({freq:300,type:'sine',dur:.3,peak:.14,glideTo:720}); },
  };

  const SFX = {
    play(name){ if(muted) return; init(); resume(); const f=R[name]; if(f) try{f();}catch(e){} },
    setMuted(m){ muted=m; try{localStorage.setItem('lifeos_muted', m?'1':'0');}catch(e){} },
    isMuted(){ return muted; },
    init, resume,
  };
  try { muted = localStorage.getItem('lifeos_muted')==='1'; } catch(e){}
  // unlock on first gesture
  ['pointerdown','keydown','touchstart'].forEach(ev =>
    window.addEventListener(ev, function once(){ init(); resume(); }, {once:true, passive:true}));

  window.SFX = SFX;
})();
