// SonicForge Studio SoundFont Player Service
(function () {
    'use strict';

    const activeOscillators = {};

    // Use the shared AudioContext from the main app (lazy init)
    let __gainNode = null;
    const getCtx = () => {
        if (typeof getAudioContext === 'function') {
            const ctx = getAudioContext();
            if (!__gainNode) {
                __gainNode = ctx.createGain();
                __gainNode.gain.value = 0.3;
                __gainNode.connect(ctx.destination);
            }
            return ctx;
        }
        if (!window.__sharedAudioCtx) {
            window.__sharedAudioCtx = new (window.AudioContext || window.webkitAudioContext)();
        }
        if (window.__sharedAudioCtx.state === 'suspended') {
            window.__sharedAudioCtx.resume();
        }
        if (!__gainNode) {
            __gainNode = window.__sharedAudioCtx.createGain();
            __gainNode.gain.value = 0.3;
            __gainNode.connect(window.__sharedAudioCtx.destination);
        }
        return window.__sharedAudioCtx;
    };

    const SonicSF = {
        loadedFonts: {},

        // Load SoundFont from URL → ArrayBuffer → store in memory
        loadSoundFont: async function (url) {
            if (this.loadedFonts[url]) return this.loadedFonts[url];
            const resp = await fetch(url);
            if (!resp.ok) throw new Error('Failed to load SoundFont: ' + url);
            const buffer = await resp.arrayBuffer();
            this.loadedFonts[url] = buffer;
            return buffer;
        },

        playNote: function (note, velocity, durationMs, startTime, program, destinationNode) {
            const ctx = getCtx();
            const freq = 440 * Math.pow(2, (note - 69) / 12);
            if (freq <= 0 || isNaN(freq)) return null;

            const osc = ctx.createOscillator();
            const noteGain = ctx.createGain();

            // Default settings
            let oscType = 'triangle';
            let attackTime = 0.01;
            let decayTime = 0.1;
            let sustainLevel = 0.5;
            let releaseTime = 0.2;
            let volFactor = 0.3;

            const prog = program !== undefined ? parseInt(program) : 0;
            if (prog >= 0 && prog <= 7) { // Pianos
                oscType = 'sine';
                decayTime = 0.3;
                sustainLevel = 0.1;
                releaseTime = 0.2;
            } else if (prog >= 8 && prog <= 15) { // Chromatic Perc
                oscType = 'sine';
                decayTime = 0.1;
                sustainLevel = 0.0;
                releaseTime = 0.1;
            } else if (prog >= 16 && prog <= 23) { // Organs
                oscType = 'sine';
                attackTime = 0.05;
                sustainLevel = 0.8;
                releaseTime = 0.1;
            } else if (prog >= 24 && prog <= 31) { // Guitars
                oscType = 'triangle';
                decayTime = 0.4;
                sustainLevel = 0.2;
                releaseTime = 0.3;
            } else if (prog >= 32 && prog <= 39) { // Basses
                oscType = 'triangle';
                attackTime = 0.02;
                decayTime = 0.2;
                sustainLevel = 0.6;
                releaseTime = 0.2;
            } else if (prog >= 40 && prog <= 47) { // Strings
                oscType = 'sawtooth';
                attackTime = 0.15;
                sustainLevel = 0.8;
                releaseTime = 0.5;
                volFactor = 0.15;
            } else if (prog >= 48 && prog <= 55) { // Ensemble / Choir
                oscType = 'sawtooth';
                attackTime = 0.2;
                sustainLevel = 0.8;
                releaseTime = 0.6;
                volFactor = 0.12;
            } else if (prog >= 56 && prog <= 63) { // Brass
                oscType = 'sawtooth';
                attackTime = 0.08;
                sustainLevel = 0.7;
                releaseTime = 0.3;
                volFactor = 0.15;
            } else if (prog >= 64 && prog <= 71) { // Reed
                oscType = 'square';
                attackTime = 0.05;
                sustainLevel = 0.6;
                releaseTime = 0.2;
                volFactor = 0.15;
            } else if (prog >= 72 && prog <= 79) { // Pipe
                oscType = 'sine';
                attackTime = 0.1;
                sustainLevel = 0.7;
                releaseTime = 0.3;
                volFactor = 0.2;
            } else if (prog >= 80 && prog <= 119) { // Synth Lead/Pad/FX
                oscType = 'sawtooth';
                attackTime = 0.05;
                sustainLevel = 0.6;
                releaseTime = 0.4;
                volFactor = 0.15;
            }

            osc.type = oscType;
            osc.frequency.value = freq;

            const startAt = startTime !== undefined ? startTime : ctx.currentTime;
            const durSec = durationMs / 1000;

            const vel = typeof velocity === 'number' ? (velocity > 1 ? velocity / 127 : velocity) : 0.8;
            const targetGain = vel * volFactor;

            // ADSR Envelope
            noteGain.gain.setValueAtTime(0, startAt);
            noteGain.gain.linearRampToValueAtTime(targetGain, startAt + attackTime);
            noteGain.gain.linearRampToValueAtTime(targetGain * sustainLevel, startAt + attackTime + decayTime);
            
            const releaseStart = startAt + Math.max(attackTime + decayTime, durSec);
            noteGain.gain.setValueAtTime(targetGain * sustainLevel, releaseStart);
            noteGain.gain.exponentialRampToValueAtTime(0.001, releaseStart + releaseTime);

            osc.connect(noteGain);
            
            const dest = destinationNode || __gainNode || ctx.destination;
            noteGain.connect(dest);

            osc.start(startAt);
            
            const stopAt = releaseStart + releaseTime + 0.05;
            osc.stop(stopAt);

            const oscId = `${note}_${Date.now()}_${Math.random()}`;
            activeOscillators[oscId] = osc;

            // Clean up active oscillator reference after it stops
            setTimeout(() => {
                delete activeOscillators[oscId];
            }, (stopAt - ctx.currentTime) * 1000 + 100);

            return osc;
        },

        stopAll: function () {
            Object.values(activeOscillators).forEach(osc => {
                try { osc.stop(); } catch (e) { }
            });
            Object.keys(activeOscillators).forEach(k => delete activeOscillators[k]);
        },

        // Save user SoundFont to IndexedDB via window.SonicStorage
        saveToIndexedDB: async function (name, arrayBuffer) {
            if (window.SonicStorage && window.SonicStorage.saveToIndexedDB) {
                await window.SonicStorage.saveToIndexedDB('soundfont_' + name, arrayBuffer);
            }
        },

        // Load user SoundFont from IndexedDB
        loadFromIndexedDB: async function (name) {
            if (window.SonicStorage && window.SonicStorage.loadFromIndexedDB) {
                return await window.SonicStorage.loadFromIndexedDB('soundfont_' + name);
            }
            return null;
        }
    };

    window.SonicSF = SonicSF;
})();
