// AudioEngine - Handles all audio processing with AudioWorklet detection
class AudioEngine {
    constructor() {
        this.audioContext = null;
        this.gainNode = null;
        this.audioWorkletNode = null;
        this.volume = 0.5;
        this.isPlaying = false;
        this.isPaused = false;
        this.currentTime = 0;
        this.duration = 0;
        
        // Audio engines
        this.modulePlayer = null;
        this.chiptunePlayer = null;
        this.synthesizer = null;
        this.synthesizerReady = false;
        
        // Fallback mode detection
        this.useAudioWorklet = false;
        this.fallbackMode = false;
        
        this.uiController = null;
        this.progressInterval = null;
        this.midiPlaybackContext = null;
    }
    
    setUIController(uiController) {
        this.uiController = uiController;
    }
    
    async initialize() {
        try {
            // Initialize Web Audio Context
            this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            this.gainNode = this.audioContext.createGain();
            this.gainNode.connect(this.audioContext.destination);
            this.gainNode.gain.value = this.volume;
            
            this.updateStatus('Audio context initialized');
            
            // Check AudioWorklet support
            await this.checkAudioWorkletSupport();
            
            // Initialize audio engines
            await this.initializeOpenMPT();
            await this.initializeSynthesizer();
            await this.loadSoundFont();
            
            // Enable audio context on user interaction
            this.setupUserActivation();
            
            const mode = this.fallbackMode ? 'fallback mode' : 'AudioWorklet mode';
            this.updateStatus(`Audio engine ready (${mode})`);
            return true;
            
        } catch (error) {
            this.updateStatus('Audio engine initialization failed: ' + error.message);
            console.error('AudioEngine initialization error:', error);
            throw error;
        }
    }
    
    async checkAudioWorkletSupport() {
        try {
            // Check if AudioWorklet is supported
            if (!this.audioContext.audioWorklet) {
                console.log('📟 AudioWorklet not supported, using fallback mode');
                this.fallbackMode = true;
                this.useAudioWorklet = false;
                this.updateStatus('AudioWorklet not supported - using fallback mode');
                return;
            }
            
            // Try to load AudioWorklet processor
            try {
                await this.audioContext.audioWorklet.addModule('/js/audio-worklet-processor.js');
                
                this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'fusion-audio-processor');
                this.audioWorkletNode.connect(this.gainNode);
                
                this.audioWorkletNode.port.onmessage = (event) => {
                    this.handleWorkletMessage(event.data);
                };
                
                this.sendToWorklet('init', {
                    sampleRate: this.audioContext.sampleRate
                });
                
                this.useAudioWorklet = true;
                this.fallbackMode = false;
                this.updateStatus('AudioWorklet loaded successfully');
                
            } catch (workletError) {
                console.warn('AudioWorklet loading failed, using fallback:', workletError);
                this.fallbackMode = true;
                this.useAudioWorklet = false;
                this.updateStatus('AudioWorklet loading failed - using fallback mode');
            }
            
        } catch (error) {
            console.warn('AudioWorklet check failed, using fallback:', error);
            this.fallbackMode = true;
            this.useAudioWorklet = false;
            this.updateStatus('AudioWorklet check failed - using fallback mode');
        }
    }
    
    handleWorkletMessage(data) {
        if (!this.useAudioWorklet) return;
        
        switch (data.type) {
            case 'timeUpdate':
                this.currentTime = data.currentTime;
                this.duration = data.duration;
                if (this.uiController) {
                    this.uiController.updateProgress(this.currentTime, this.duration);
                }
                break;
                
            case 'playStateChanged':
                this.isPlaying = data.playing;
                if (data.time !== undefined) {
                    this.currentTime = data.time;
                }
                if (this.uiController) {
                    this.uiController.updateControls(this.isPlaying, this.isPaused);
                }
                break;
                
            case 'trackEnded':
                this.isPlaying = false;
                this.isPaused = false;
                if (this.uiController) {
                    this.uiController.nextTrack();
                }
                break;
                
            case 'synthesizerReady':
                if (data.success) {
                    this.synthesizerReady = true;
                    this.updateStatus('JS-Synthesizer initialized with SoundFont');
                } else {
                    this.updateStatus('JS-Synthesizer initialization failed: ' + data.error);
                }
                break;
                
            case 'error':
                this.updateStatus('Audio engine error: ' + data.message);
                console.error('AudioWorklet error:', data);
                break;
        }
    }
    
    sendToWorklet(type, data = {}) {
        if (this.useAudioWorklet && this.audioWorkletNode) {
            this.audioWorkletNode.port.postMessage({ type, ...data });
        }
    }
    
    async initializeOpenMPT() {
        try {
            this.updateStatus('Waiting for OpenMPT/chiptune2.js...');
            
            // Wait for libraries to load
            let attempts = 0;
            while (typeof ChiptuneJsConfig === 'undefined' && typeof Module === 'undefined' && attempts < 50) {
                await new Promise(resolve => setTimeout(resolve, 100));
                attempts++;
            }
            
            if (typeof ChiptuneJsConfig !== 'undefined' && typeof ChiptuneJsPlayer !== 'undefined') {
                try {
                    this.chiptunePlayer = new ChiptuneJsPlayer(new ChiptuneJsConfig(-1, this.audioContext));
                    this.updateStatus('chiptune2.js loaded successfully');
                } catch (chipErr) {
                    console.error('ChiptuneJS initialization error:', chipErr);
                    this.updateStatus('ChiptuneJS failed to initialize');
                }
            } else if (typeof Module !== 'undefined') {
                this.updateStatus('libopenmpt loaded successfully');
            } else {
                this.updateStatus('OpenMPT.js failed to load - tracker modules may not play');
            }
        } catch (error) {
            this.updateStatus('OpenMPT initialization error: ' + error.message);
            console.error('OpenMPT initialization failed:', error);
        }
    }
    
    async initializeSynthesizer() {
        try {
            this.updateStatus('Loading MIDI Synthesizers...');
            
            // Try WebAudioTinySynth first (most reliable for fallback mode)
            if (typeof WebAudioTinySynth !== 'undefined') {
                try {
                    this.synthesizer = new WebAudioTinySynth({
                        quality: 1,
                        useReverb: 1,
                        voices: 32
                    });
                    this.synthesizerReady = true;
                    this.updateStatus('WebAudio-TinySynth loaded successfully');
                    return;
                } catch (tinyError) {
                    console.warn('WebAudioTinySynth failed:', tinyError);
                }
            }
            
            // Try JS-Synthesizer as fallback
            if (typeof JSSynth !== 'undefined') {
                try {
                    await JSSynth.waitForReady();
                    this.synthesizer = new JSSynth.Synthesizer();
                    await this.synthesizer.init(this.audioContext.sampleRate);
                    this.synthesizerReady = true;
                    this.updateStatus('JS-Synthesizer loaded successfully');
                    return;
                } catch (jsError) {
                    console.warn('JS-Synthesizer failed:', jsError);
                    this.updateStatus('JS-Synthesizer failed, trying fallback...');
                }
            }
            
            // Last resort: create a simple fallback synthesizer
            this.synthesizer = this.createFallbackSynthesizer();
            this.synthesizerReady = true;
            this.updateStatus('Using basic fallback synthesizer');
            
        } catch (error) {
            this.updateStatus('All MIDI synthesizers failed: ' + error.message);
            console.error('Synthesizer initialization failed:', error);
        }
    }
    
    createFallbackSynthesizer() {
        const oscillators = new Map();
        const gainNodes = new Map();
        
        return {
            send: (midiData) => {
                const [status, note, velocity] = midiData;
                const command = status & 0xF0;
                
                if (command === 0x90 && velocity > 0) {
                    this.playNote(note, velocity, oscillators, gainNodes);
                } else if (command === 0x80 || (command === 0x90 && velocity === 0)) {
                    this.stopNote(note, oscillators, gainNodes);
                }
            },
            stopMIDI: () => {
                oscillators.forEach(osc => {
                    try { osc.stop(); } catch (e) {}
                });
                oscillators.clear();
                gainNodes.clear();
            }
        };
    }
    
    playNote(note, velocity, oscillators, gainNodes) {
        try {
            this.stopNote(note, oscillators, gainNodes);
            
            const osc = this.audioContext.createOscillator();
            const gain = this.audioContext.createGain();
            
            const frequency = 440 * Math.pow(2, (note - 69) / 12);
            osc.frequency.setValueAtTime(frequency, this.audioContext.currentTime);
            osc.type = 'sawtooth';
            
            const volume = (velocity / 127) * 0.1;
            gain.gain.setValueAtTime(volume, this.audioContext.currentTime);
            
            osc.connect(gain);
            gain.connect(this.gainNode);
            osc.start();
            
            oscillators.set(note, osc);
            gainNodes.set(note, gain);
            
        } catch (error) {
            console.error('Error playing note:', error);
        }
    }
    
    stopNote(note, oscillators, gainNodes) {
        try {
            const osc = oscillators.get(note);
            const gain = gainNodes.get(note);
            
            if (osc && gain) {
                gain.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + 0.1);
                osc.stop(this.audioContext.currentTime + 0.1);
                oscillators.delete(note);
                gainNodes.delete(note);
            }
        } catch (error) {
            console.error('Error stopping note:', error);
        }
    }
    
    async loadSoundFont() {
        // Skip SoundFont loading for TinySynth as it has built-in sounds
        if (this.synthesizer instanceof WebAudioTinySynth) {
            this.updateStatus('TinySynth ready with built-in sounds');
            return;
        }
        
        if (!this.synthesizer) {
            this.updateStatus('No synthesizer available for SoundFont loading');
            return;
        }
        
        try {
            this.updateStatus('Loading SoundFont...');
            
            const response = await fetch('/soundfonts/default.sf2');
            if (!response.ok) {
                throw new Error('SoundFont not found');
            }
            
            const soundFontData = await response.arrayBuffer();
            
            if (this.synthesizer.loadSoundFont) {
                await this.synthesizer.loadSoundFont(new Uint8Array(soundFontData));
                this.synthesizerReady = true;
                this.updateStatus('SoundFont loaded successfully');
            } else {
                this.synthesizerReady = true;
                this.updateStatus('Synthesizer ready with built-in sounds');
            }
            
        } catch (error) {
            this.updateStatus('SoundFont loading failed, using built-in sounds: ' + error.message);
            this.synthesizerReady = true; // Allow playback with built-in sounds
        }
    }
    
    setupUserActivation() {
        const activateAudio = async () => {
            if (this.audioContext && this.audioContext.state === 'suspended') {
                await this.audioContext.resume();
                this.updateStatus('Audio context activated');
            }
        };
        
        document.addEventListener('click', activateAudio, { once: true });
        document.addEventListener('touchstart', activateAudio, { once: true });
    }
    
    async playTrack(trackData) {
        if (!trackData) {
            throw new Error('No track data provided');
        }
        
        this.stop(); // Stop any current playback
        
        const trackUrl = `/music/${trackData.filename}`;
        this.updateStatus(`Loading ${trackData.filename}...`);
        
        try {
            if (trackData.type === 'tracker') {
                await this.playTrackerModule(trackUrl);
            } else if (trackData.type === 'midi') {
                await this.playMidiFile(trackUrl, trackData);
            } else {
                throw new Error('Unsupported file type');
            }
        } catch (error) {
            this.updateStatus(`Playback failed: ${error.message}`);
            throw error;
        }
    }
    
    async playTrackerModule(url) {
        try {
            if (this.chiptunePlayer) {
                await this.playWithChiptune2(url);
            } else if (typeof Module !== 'undefined' && this.useAudioWorklet) {
                await this.playWithRawOpenMPT(url);
            } else {
                throw new Error('No tracker player available');
            }
        } catch (error) {
            throw new Error('Failed to load tracker module: ' + error.message);
        }
    }
    
    async playWithChiptune2(url) {
        const response = await fetch(url);
        const arrayBuffer = await response.arrayBuffer();
        
        try {
            const player = this.chiptunePlayer;
            
            if (player && typeof player.load === 'function') {
                // Use Promise wrapper for consistent handling
                await new Promise((resolve, reject) => {
                    if (player.load.length === 1) {
                        // Promise-based API
                        Promise.resolve(player.load(arrayBuffer))
                            .then(() => {
                                player.play();
                                resolve();
                            })
                            .catch(reject);
                    } else {
                        // Callback-based API
                        player.load(arrayBuffer, (error) => {
                            if (error) {
                                reject(error);
                                return;
                            }
                            player.play();
                            resolve();
                        });
                    }
                });
                
                this.isPlaying = true;
                this.updateStatus('Playing tracker module with chiptune2.js');
                
                // Start progress tracking for fallback mode
                if (this.fallbackMode) {
                    this.startChiptune2Progress();
                }
                
            } else {
                throw new Error('Chiptune2.js player not properly initialized');
            }
        } catch (error) {
            console.warn('Chiptune2.js failed:', error);
            throw error;
        }
    }
    
    async playWithRawOpenMPT(url) {
        if (!this.useAudioWorklet) {
            throw new Error('Raw OpenMPT requires AudioWorklet support');
        }
        
        const response = await fetch(url);
        const arrayBuffer = await response.arrayBuffer();
        const data = new Uint8Array(arrayBuffer);
        
        this.modulePlayer = new Module.OpenmptModule(data);
        this.duration = this.modulePlayer.get_duration_seconds();
        
        this.sendToWorklet('setEngine', {
            engine: this.modulePlayer,
            engineType: 'openmpt',
            duration: this.duration
        });
        
        this.sendToWorklet('play');
        this.isPlaying = true;
        this.updateStatus('Playing with raw OpenMPT via AudioWorklet');
    }
    
    startChiptune2Progress() {
        this.progressInterval = setInterval(() => {
            if (!this.isPlaying || !this.chiptunePlayer) {
                clearInterval(this.progressInterval);
                return;
            }
            
            try {
                if (this.chiptunePlayer.currentPlayingNode) {
                    const position = this.chiptunePlayer.currentPlayingNode.getPosition();
                    const duration = this.chiptunePlayer.currentPlayingNode.getDuration();
                    
                    if (position !== undefined && duration !== undefined) {
                        this.currentTime = position;
                        this.duration = duration;
                        
                        if (this.uiController) {
                            this.uiController.updateProgress(this.currentTime, this.duration);
                        }
                        
                        // Check if playback ended
                        if (position >= duration) {
                            this.isPlaying = false;
                            if (this.uiController) {
                                this.uiController.updateControls(false, false);
                                this.uiController.nextTrack();
                            }
                            clearInterval(this.progressInterval);
                        }
                    }
                }
            } catch (error) {
                console.error('Progress tracking error:', error);
            }
        }, 100);
    }
    
    async playMidiFile(url, trackData) {
        if (!this.synthesizerReady) {
            throw new Error('MIDI synthesizer not ready');
        }
        
        const response = await fetch(url);
        const arrayBuffer = await response.arrayBuffer();
        const midiData = new Uint8Array(arrayBuffer);
        
        try {
            // Use different approaches based on available synthesizer and mode
            if (this.synthesizer instanceof WebAudioTinySynth) {
                await this.playMidiWithTinySynth(midiData);
            } else if (this.useAudioWorklet) {
                // Use AudioWorklet for MIDI if available
                let midiFile;
                if (typeof MidiParser !== 'undefined') {
                    const parser = new MidiParser();
                    midiFile = parser.parse(midiData);
                } else {
                    midiFile = { events: [], duration: 120, ticksPerQuarter: 480, tempo: 500000 };
                }
                
                this.duration = midiFile.duration;
                
                this.sendToWorklet('setEngine', {
                    engine: null,
                    engineType: 'synthesizer',
                    duration: midiFile.duration,
                    midiEvents: midiFile.events,
                    ticksPerQuarter: midiFile.ticksPerQuarter,
                    tempo: midiFile.tempo
                });
                
                this.sendToWorklet('play');
            } else {
                // Fallback MIDI playback
                await this.playMidiWithFallback(midiData);
            }
            
            this.isPlaying = true;
            this.updateStatus(`Playing MIDI: ${trackData.filename}`);
            
        } catch (error) {
            throw new Error('Failed to load MIDI file: ' + error.message);
        }
    }
    
    async playMidiWithTinySynth(midiData) {
        try {
            // Create a blob URL for the MIDI data
            const blob = new Blob([midiData], { type: 'audio/midi' });
            const url = URL.createObjectURL(blob);
            
            // Load and play with TinySynth
            if (this.synthesizer.loadMIDIUrl) {
                await this.synthesizer.loadMIDIUrl(url);
                this.synthesizer.playMIDI();
                
                this.duration = 120; // Default duration
                if (this.fallbackMode) {
                    this.startTinySynthProgress();
                }
            }
            
            // Clean up blob URL
            setTimeout(() => URL.revokeObjectURL(url), 1000);
            
        } catch (error) {
            console.error('TinySynth MIDI playback failed:', error);
            throw error;
        }
    }
    
    async playMidiWithFallback(midiData) {
        // Simple fallback MIDI playback
        this.duration = 120; // Default duration
        this.startSimpleMidiProgress();
    }
    
    startTinySynthProgress() {
        let startTime = Date.now();
        
        this.progressInterval = setInterval(() => {
            if (!this.isPlaying) {
                clearInterval(this.progressInterval);
                return;
            }
            
            this.currentTime = (Date.now() - startTime) / 1000;
            
            if (this.uiController) {
                this.uiController.updateProgress(this.currentTime, this.duration);
            }
            
            if (this.currentTime >= this.duration) {
                this.isPlaying = false;
                if (this.uiController) {
                    this.uiController.updateControls(false, false);
                    this.uiController.nextTrack();
                }
                clearInterval(this.progressInterval);
            }
        }, 100);
    }
    
    startSimpleMidiProgress() {
        let startTime = Date.now();
        
        this.progressInterval = setInterval(() => {
            if (!this.isPlaying) {
                clearInterval(this.progressInterval);
                return;
            }
            
            this.currentTime = (Date.now() - startTime) / 1000;
            
            if (this.uiController) {
                this.uiController.updateProgress(this.currentTime, this.duration);
            }
            
            if (this.currentTime >= this.duration) {
                this.isPlaying = false;
                if (this.uiController) {
                    this.uiController.nextTrack();
                }
                clearInterval(this.progressInterval);
            }
        }, 100);
    }
    
    pause() {
        this.isPaused = true;
        this.isPlaying = false;
        
        if (this.chiptunePlayer && this.chiptunePlayer.pause) {
            this.chiptunePlayer.pause();
        }
        
        if (this.synthesizer && this.synthesizer.stopMIDI) {
            this.synthesizer.stopMIDI();
        }
        
        if (this.useAudioWorklet) {
            this.sendToWorklet('pause');
        }
        
        if (this.progressInterval) {
            clearInterval(this.progressInterval);
        }
        
        this.updateStatus('Playback paused');
    }
    
    resume() {
        this.isPaused = false;
        this.isPlaying = true;
        
        if (this.chiptunePlayer && this.chiptunePlayer.play) {
            this.chiptunePlayer.play();
        }
        
        if (this.synthesizer && this.synthesizer.playMIDI) {
            this.synthesizer.playMIDI();
        }
        
        if (this.useAudioWorklet) {
            this.sendToWorklet('play');
        }
        
        this.updateStatus('Playback resumed');
    }
    
    stop() {
        this.isPlaying = false;
        this.isPaused = false;
        this.currentTime = 0;
        
        // Clear progress tracking
        if (this.progressInterval) {
            clearInterval(this.progressInterval);
            this.progressInterval = null;
        }
        
        // Stop chiptune2.js playback
        if (this.chiptunePlayer && this.chiptunePlayer.stop) {
            this.chiptunePlayer.stop();
        }
        
        // Stop synthesizer
        if (this.synthesizer) {
            if (this.synthesizer.stopMIDI) {
                this.synthesizer.stopMIDI();
            }
            // Send all notes off for MIDI
            if (this.synthesizer.send) {
                for (let channel = 0; channel < 16; channel++) {
                    this.synthesizer.send([0xB0 | channel, 123, 0], 0); // All notes off
                    this.synthesizer.send([0xB0 | channel, 120, 0], 0); // All sound off
                }
            }
        }
        
        if (this.useAudioWorklet) {
            this.sendToWorklet('stop');
        }
        
        if (this.modulePlayer) {
            try {
                this.modulePlayer.delete();
            } catch (e) {
                console.warn('Error deleting module player:', e);
            }
            this.modulePlayer = null;
        }
        
        this.updateStatus('Playback stopped');
    }
    
    seek(time) {
        if (this.useAudioWorklet) {
            this.sendToWorklet('seek', { time });
        }
        this.currentTime = Math.max(0, time);
    }
    
    setVolume(level) {
        this.volume = Math.max(0, Math.min(1, level));
        if (this.gainNode) {
            this.gainNode.gain.value = this.volume;
        }
        
        if (this.useAudioWorklet) {
            this.sendToWorklet('setVolume', { volume: this.volume });
        }
        
        // Set volume on synthesizer if supported
        if (this.synthesizer && this.synthesizer.setMasterVolume) {
            this.synthesizer.setMasterVolume(this.volume);
        }
    }
    
    updateStatus(message) {
        if (this.uiController) {
            this.uiController.updateSystemStatus(message);
        } else {
            console.log('AudioEngine:', message);
        }
    }
    
    // Getters for current state
    getCurrentTime() {
        return this.currentTime;
    }
    
    getDuration() {
        return this.duration;
    }
    
    getVolume() {
        return this.volume;
    }
    
    getPlayState() {
        return {
            isPlaying: this.isPlaying,
            isPaused: this.isPaused,
            currentTime: this.currentTime,
            duration: this.duration
        };
    }
}
