// ===== Teuanjai Radio — audio engine =====
// สังเคราะห์เสียงดนตรีนุ่มๆ ต่อแต่ละเพลงด้วย Web Audio (ไม่ต้องมีไฟล์เสียง)
// หากต้องการต่อไฟล์จริง: ใส่ field `src` ใน track แล้วใช้ <audio> แทน engine นี้ได้

const AudioEngine = {
  ctx: null,
  nodes: null,
  arpTimer: null,
  muted: false,
  // โน้ตของแต่ละเพลง (Hz): แพดค้าง + โน้ตสำหรับอาร์เพจจิโอ
  voices: {
    t1: { pad: [130.81, 196.00, 261.63], arp: [392.00, 523.25, 659.25, 587.33], wave: "sine",     cutoff: 1100 }, // C major — chill
    t2: { pad: [110.00, 164.81, 220.00], arp: [329.63, 440.00, 659.25, 523.25], wave: "triangle", cutoff: 1300 }, // A minor — indie
  },

  ensure() {
    if (!this.ctx) {
      const AC = window.AudioContext || window.webkitAudioContext;
      if (!AC) return false;
      this.ctx = new AC();
    }
    if (this.ctx.state === "suspended") this.ctx.resume();
    return true;
  },

  setMuted(m) {
    this.muted = m;
    if (this.nodes) {
      try {
        this.nodes.master.gain.cancelScheduledValues(this.ctx.currentTime);
        this.nodes.master.gain.linearRampToValueAtTime(m ? 0 : 0.42, this.ctx.currentTime + 0.2);
      } catch (e) {}
    }
  },

  play(trackId) {
    if (!this.ensure()) return;
    this.stop(true);
    const v = this.voices[trackId] || this.voices.t1;
    const ctx = this.ctx;
    const now = ctx.currentTime;

    const master = ctx.createGain();
    master.gain.value = 0;
    master.connect(ctx.destination);

    const filter = ctx.createBiquadFilter();
    filter.type = "lowpass";
    filter.frequency.value = v.cutoff;
    filter.Q.value = 0.6;
    filter.connect(master);

    // soft reverb-ish: short feedback delay
    const delay = ctx.createDelay();
    delay.delayTime.value = 0.28;
    const fb = ctx.createGain();
    fb.gain.value = 0.22;
    const wet = ctx.createGain();
    wet.gain.value = 0.25;
    filter.connect(delay); delay.connect(fb); fb.connect(delay); delay.connect(wet); wet.connect(master);

    // sustained pad
    const oscs = v.pad.map((f, i) => {
      const o = ctx.createOscillator();
      o.type = v.wave;
      o.frequency.value = f;
      const g = ctx.createGain();
      g.gain.value = 0.09 - i * 0.015;
      // gentle detune drift
      const lfo = ctx.createOscillator();
      lfo.frequency.value = 0.07 + i * 0.03;
      const lg = ctx.createGain();
      lg.gain.value = 1.5;
      lfo.connect(lg); lg.connect(o.detune); lfo.start();
      o.connect(g); g.connect(filter); o.start();
      return { o, lfo };
    });

    // slow filter sweep for movement
    const flfo = ctx.createOscillator();
    flfo.frequency.value = 0.06;
    const flg = ctx.createGain();
    flg.gain.value = 380;
    flfo.connect(flg); flg.connect(filter.frequency); flfo.start();

    master.gain.linearRampToValueAtTime(this.muted ? 0 : 0.42, now + 0.7);

    this.nodes = { master, filter, oscs, flfo, delay };

    // gentle arpeggio plucks
    let i = 0;
    const arp = v.arp;
    const pluck = () => {
      if (!this.nodes) return;
      const t = ctx.currentTime;
      const o = ctx.createOscillator();
      o.type = "triangle";
      o.frequency.value = arp[i % arp.length];
      const g = ctx.createGain();
      g.gain.setValueAtTime(0.0001, t);
      g.gain.exponentialRampToValueAtTime(this.muted ? 0.0001 : 0.10, t + 0.02);
      g.gain.exponentialRampToValueAtTime(0.0001, t + 0.9);
      o.connect(g); g.connect(filter);
      o.start(t); o.stop(t + 1.0);
      i++;
    };
    pluck();
    this.arpTimer = setInterval(pluck, 1300);
  },

  stop(immediate) {
    if (this.arpTimer) { clearInterval(this.arpTimer); this.arpTimer = null; }
    if (!this.nodes) return;
    const ctx = this.ctx;
    const { master, oscs, flfo } = this.nodes;
    const fade = immediate ? 0.08 : 0.3;
    try {
      master.gain.cancelScheduledValues(ctx.currentTime);
      master.gain.setValueAtTime(master.gain.value, ctx.currentTime);
      master.gain.linearRampToValueAtTime(0, ctx.currentTime + fade);
    } catch (e) {}
    const old = { oscs, flfo };
    setTimeout(() => {
      try { old.oscs.forEach((x) => { x.o.stop(); x.lfo.stop(); }); old.flfo.stop(); } catch (e) {}
    }, fade * 1000 + 60);
    this.nodes = null;
  },
};

window.AudioEngine = AudioEngine;
