/* sound.js — synthesized UI sounds (WebAudio, no assets).
   Three themes: standard (soft synth), wooden (filtered-noise thock,
   like pieces on a real board), digital (clean app blips). */
'use strict';

import { settings } from './settings.js';

let ctx = null;
let noiseBuf = null;

function vol() { return settings.get('sound.enabled') ? settings.get('sound.volume') / 100 : 0; }
function theme() { return settings.get('sound.theme') || 'standard'; }

function ac() {
  ctx = ctx || new (window.AudioContext || window.webkitAudioContext)();
  return ctx;
}

function beep(freq, dur, type = 'sine', gain = 0.12, when = 0) {
  const v = vol();
  if (v <= 0) return;
  const a = ac();
  const t0 = a.currentTime + when;
  const o = a.createOscillator(), g = a.createGain();
  o.type = type; o.frequency.value = freq;
  g.gain.setValueAtTime(gain * v, t0);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  o.connect(g); g.connect(a.destination);
  o.start(t0); o.stop(t0 + dur + 0.02);
}

/** short filtered-noise burst — the "thock" of wood on wood */
function thock(cutoff, dur, gain = 0.5, when = 0) {
  const v = vol();
  if (v <= 0) return;
  const a = ac();
  if (!noiseBuf) {
    noiseBuf = a.createBuffer(1, a.sampleRate * 0.12, a.sampleRate);
    const d = noiseBuf.getChannelData(0);
    for (let i = 0; i < d.length; i++) d[i] = Math.random() * 2 - 1;
  }
  const t0 = a.currentTime + when;
  const src = a.createBufferSource();
  src.buffer = noiseBuf;
  const f = a.createBiquadFilter();
  f.type = 'lowpass'; f.frequency.value = cutoff; f.Q.value = 1.2;
  const g = a.createGain();
  g.gain.setValueAtTime(gain * v, t0);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  src.connect(f); f.connect(g); g.connect(a.destination);
  src.start(t0); src.stop(t0 + dur + 0.02);
}

/** short click: a tiny high tick layered over a soft body — the crisp
    "clack" of a chess.com-style move. */
function click(bodyFreq, gain = 0.5, when = 0, bright = 2600) {
  thock(bright, 0.028, gain * 0.5, when);          // tick transient
  beep(bodyFreq, 0.05, 'sine', gain * 0.5, when);  // soft body
  beep(bodyFreq * 0.5, 0.06, 'triangle', gain * 0.35, when);
}

const THEMES = {
  neo: {
    move()    { click(420, .5); },
    capture() { click(300, .55); thock(600, .08, .5, .03); },
    castle()  { click(420, .45); click(500, .4, .07); },
    check()   { click(520, .5); beep(700, .09, 'sine', .09, .05); },
    end()     { beep(523, .12, 'sine', .11); beep(659, .12, 'sine', .11, .11); beep(784, .22, 'sine', .13, .22); },
  },
  standard: {
    move()    { beep(280, .08, 'sine', .16); beep(180, .06, 'triangle', .1); },
    capture() { beep(150, .1, 'square', .1); beep(90, .12, 'sine', .18); },
    castle()  { beep(280, .07, 'sine', .13); beep(340, .07, 'sine', .13, .08); },
    check()   { beep(520, .09, 'triangle', .13); beep(660, .11, 'triangle', .13, .09); },
    end()     { beep(392, .16, 'sine', .13); beep(494, .16, 'sine', .13, .12); beep(587, .26, 'sine', .15, .24); },
  },
  wooden: {
    move()    { thock(900, .09, .55); },
    capture() { thock(700, .07, .6); thock(500, .12, .7, .05); },
    castle()  { thock(900, .08, .5); thock(850, .08, .5, .1); },
    check()   { thock(1400, .07, .5); beep(620, .1, 'sine', .07, .04); },
    end()     { thock(800, .1, .6); beep(392, .18, 'sine', .1, .1); beep(587, .28, 'sine', .12, .26); },
  },
  digital: {
    move()    { beep(660, .05, 'square', .07); },
    capture() { beep(440, .05, 'square', .09); beep(330, .07, 'square', .09, .05); },
    castle()  { beep(660, .05, 'square', .07); beep(880, .05, 'square', .07, .06); },
    check()   { beep(988, .06, 'square', .09); beep(1319, .08, 'square', .09, .07); },
    end()     { beep(523, .1, 'square', .08); beep(659, .1, 'square', .08, .1); beep(784, .18, 'square', .09, .2); },
  },
};

function ev(name) { (THEMES[theme()] || THEMES.standard)[name](); }

export const sound = {
  move()    { ev('move'); },
  capture() { ev('capture'); },
  castle()  { ev('castle'); },
  check()   { ev('check'); },
  end()     { ev('end'); },
  illegal() { theme() === 'wooden' ? thock(300, .1, .4) : beep(120, .1, 'sawtooth', .08); },
  lowTime() { if (settings.get('sound.lowTime')) { beep(880, .07, 'square', .07); beep(880, .07, 'square', .07, .12); } },
  correct() { beep(600, .08, 'sine', .12); beep(900, .12, 'sine', .12, .07); },
  wrong()   { beep(220, .12, 'triangle', .12); beep(160, .16, 'triangle', .12, .1); },
  tick()    { beep(700, .03, 'square', .05); },
};

// preview when the style changes
settings.on('change:sound.theme', () => { sound.move(); setTimeout(() => sound.capture(), 220); });
