// ==========================================
// TechDJ Pro - Core DJ Logic
// ==========================================

// Server-side audio mode (true = server processes audio, false = browser processes)
const SERVER_SIDE_AUDIO = false;

let audioCtx;
const decks = {
    A: {
        type: 'local',
        playing: false,
        pausedAt: 0,
        duration: 0,
        localBuffer: null,
        localSource: null,
        gainNode: null,
        volumeGain: null,
        crossfaderGain: null,
        filters: {},
        cues: {},
        loopStart: null,
        loopEnd: null,
        loopActive: false,
        activeAutoLoop: null,
        waveformData: null,
        lastAnchorTime: 0,
        lastAnchorPosition: 0,
        loading: false,
        currentFile: null,
        lastSeekTime: 0
    },
    B: {
        type: 'local',
        playing: false,
        pausedAt: 0,
        duration: 0,
        localBuffer: null,
        localSource: null,
        gainNode: null,
        volumeGain: null,
        crossfaderGain: null,
        filters: {},
        cues: {},
        loopStart: null,
        loopEnd: null,
        loopActive: false,
        activeAutoLoop: null,
        waveformData: null,
        lastAnchorTime: 0,
        lastAnchorPosition: 0,
        loading: false,
        currentFile: null,
        lastSeekTime: 0
    }
};

let allSongs = [];
const settings = {
    repeatA: false,
    repeatB: false,
    autoMix: false,
    shuffleMode: false,
    quantize: false,
    autoPlay: true,
    glowA: false,
    glowB: false,
    glowIntensity: 30
};

// Queue system for both decks
const queues = {
    A: [],
    B: []
};

// System Initialization
function initSystem() {
    if (audioCtx) return;
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    document.getElementById('start-overlay').style.display = 'none';

    // Track dragging state per deck
    const draggingState = { A: false, B: false };

    // Setup audio path for both decks
    ['A', 'B'].forEach(id => {
        // Create separate volume and crossfader gain nodes
        const volumeGain = audioCtx.createGain();
        const crossfaderGain = audioCtx.createGain();
        const analyser = audioCtx.createAnalyser();
        analyser.fftSize = 256;

        const low = audioCtx.createBiquadFilter();
        const mid = audioCtx.createBiquadFilter();
        const high = audioCtx.createBiquadFilter();
        const lp = audioCtx.createBiquadFilter();
        const hp = audioCtx.createBiquadFilter();

        low.type = 'lowshelf'; low.frequency.value = 320;
        mid.type = 'peaking'; mid.frequency.value = 1000; mid.Q.value = 1;
        high.type = 'highshelf'; high.frequency.value = 3200;
        lp.type = 'lowpass'; lp.frequency.value = 22050; // default fully open
        hp.type = 'highpass'; hp.frequency.value = 0;    // default fully open

        // Connect: Filters -> Volume -> Analyser -> Crossfader -> Destination
        low.connect(mid);
        mid.connect(high);
        high.connect(lp);
        lp.connect(hp);
        hp.connect(volumeGain);
        volumeGain.connect(analyser);
        analyser.connect(crossfaderGain);
        crossfaderGain.connect(audioCtx.destination);

        // Set default values
        volumeGain.gain.value = 0.8; // 80% volume
        // Crossfader: A=1.0 at left (val=0), B=1.0 at right (val=100), both 0.5 at center (val=50)
        crossfaderGain.gain.value = id === 'A' ? 0.5 : 0.5; // Center position initially

        decks[id].volumeGain = volumeGain;
        decks[id].crossfaderGain = crossfaderGain;
        decks[id].gainNode = volumeGain; // Keep for compatibility
        decks[id].analyser = analyser;
        decks[id].filters = { low, mid, high, lp, hp };

        // Set canvas dimensions properly with DPI scaling
        const canvas = document.getElementById('waveform-' + id);
        const dpr = window.devicePixelRatio || 1;
        canvas.width = canvas.offsetWidth * dpr;
        canvas.height = 80 * dpr;

        // Scale context to match DPI (reset transform first to prevent stacking)
        const ctx = canvas.getContext('2d');
        ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset to identity matrix
        ctx.scale(dpr, dpr);

        // Waveform Scrubbing (Dragging to Seek)
        const handleScrub = (e) => {
            if (!decks[id].duration) return;
            const rect = canvas.getBoundingClientRect();
            const x = (e.clientX || (e.touches && e.touches[0].clientX)) - rect.left;
            const percent = Math.max(0, Math.min(1, x / rect.width));
            seekTo(id, percent * decks[id].duration);
        };

        canvas.addEventListener('mousedown', (e) => {
            draggingState[id] = true;
            handleScrub(e);
        });

        canvas.addEventListener('mousemove', (e) => {
            if (draggingState[id]) handleScrub(e);
        });

        canvas.addEventListener('mouseup', () => {
            draggingState[id] = false;
        });

        canvas.addEventListener('mouseleave', () => {
            draggingState[id] = false;
        });

        // Mobile Touch Support
        canvas.addEventListener('touchstart', (e) => {
            draggingState[id] = true;
            handleScrub(e);
            e.preventDefault();
        }, { passive: false });

        canvas.addEventListener('touchmove', (e) => {
            if (draggingState[id]) {
                handleScrub(e);
                e.preventDefault();
            }
        }, { passive: false });

        canvas.addEventListener('touchend', () => {
            draggingState[id] = false;
        });

        // Disk Scrubbing (Dragging on the Disk)
        const disk = document.querySelector(`#deck-${id} .dj-disk`);
        let isDiskDragging = false;

        disk.addEventListener('mousedown', (e) => {
            isDiskDragging = true;
            e.stopPropagation(); // Don't trigger the click/toggleDeck if we're dragging
        });

        disk.addEventListener('mousemove', (e) => {
            if (isDiskDragging && decks[id].playing) {
                // Scrub based on vertical movement
                const movement = e.movementY || 0;
                const currentPos = getCurrentPosition(id);
                const newPos = Math.max(0, Math.min(decks[id].duration, currentPos + movement * 0.1));
                seekTo(id, newPos);
            }
        });

        disk.addEventListener('mouseup', () => {
            isDiskDragging = false;
        });

        disk.addEventListener('mouseleave', () => {
            isDiskDragging = false;
        });

        disk.addEventListener('touchstart', (e) => {
            isDiskDragging = true;
            e.stopPropagation();
        }, { passive: false });

        disk.addEventListener('touchmove', (e) => {
            if (isDiskDragging && decks[id].playing && e.touches[0]) {
                const movement = e.touches[0].clientY - (disk._lastTouchY || e.touches[0].clientY);
                disk._lastTouchY = e.touches[0].clientY;
                const currentPos = getCurrentPosition(id);
                const newPos = Math.max(0, Math.min(decks[id].duration, currentPos + movement * 0.1));
                seekTo(id, newPos);
            }
        }, { passive: false });

        disk.addEventListener('touchend', () => {
            isDiskDragging = false;
            delete disk._lastTouchY;
        });

        // Handle click for toggle (only if not scrubbing)
        disk.addEventListener('click', (e) => {
            if (!isDiskDragging) {
                toggleDeck(id);
            }
        });
    });

    fetchLibrary();
    updateTimeDisplays();
    animateVUMeters(); // Start VU meter animation

    // Handle resize for DPI scaling
    window.addEventListener('resize', () => {
        ['A', 'B'].forEach(id => {
            const canvas = document.getElementById('waveform-' + id);
            if (!canvas) return;
            const dpr = window.devicePixelRatio || 1;
            canvas.width = canvas.offsetWidth * dpr;
            canvas.height = 80 * dpr;
            const ctx = canvas.getContext('2d');
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            ctx.scale(dpr, dpr);
            drawWaveform(id);
        });
    });

    // Initialize mobile view
    if (window.innerWidth <= 1024) {
        switchTab('library');
    }
}

// VU Meter Animation with smoothing
const vuMeterState = {
    A: { smoothedValues: [], peakValues: [] },
    B: { smoothedValues: [], peakValues: [] }
};

function animateVUMeters() {
    requestAnimationFrame(animateVUMeters);

    const anyPlaying = decks.A.playing || decks.B.playing;
    const isListener = window.location.port === '5001' || window.location.search.includes('listen=true');

    // Skip rendering if nothing is happening to save battery
    if (!anyPlaying && !isListener) return;

    ['A', 'B'].forEach(id => {
        const canvas = document.getElementById('viz-' + id);
        if (!canvas || !decks[id].analyser) return;

        const ctx = canvas.getContext('2d');
        const analyser = decks[id].analyser;
        const bufferLength = analyser.frequencyBinCount;
        const dataArray = new Uint8Array(bufferLength);

        analyser.getByteFrequencyData(dataArray);

        const width = canvas.width;
        const height = canvas.height;
        const barCount = 32;
        const barWidth = width / barCount;

        // Initialize smoothed values if needed
        if (!vuMeterState[id].smoothedValues.length) {
            vuMeterState[id].smoothedValues = new Array(barCount).fill(0);
            vuMeterState[id].peakValues = new Array(barCount).fill(0);
        }

        ctx.fillStyle = '#0a0a12';
        ctx.fillRect(0, 0, width, height);

        for (let i = 0; i < barCount; i++) {
            // Use logarithmic frequency distribution for better bass/mid/treble representation
            const freqIndex = Math.floor(Math.pow(i / barCount, 1.5) * bufferLength);
            const rawValue = dataArray[freqIndex] / 255;

            // Smooth the values for less jittery animation
            const smoothingFactor = 0.7; // Higher = smoother but less responsive
            const targetValue = rawValue;
            vuMeterState[id].smoothedValues[i] =
                (vuMeterState[id].smoothedValues[i] * smoothingFactor) +
                (targetValue * (1 - smoothingFactor));

            const value = vuMeterState[id].smoothedValues[i];
            const barHeight = value * height;

            // Peak hold with decay
            if (value > vuMeterState[id].peakValues[i]) {
                vuMeterState[id].peakValues[i] = value;
            } else {
                vuMeterState[id].peakValues[i] *= 0.95; // Decay rate
            }

            // Draw main bar with gradient
            const hue = id === 'A' ? 180 : 280; // Cyan for A, Magenta for B
            const saturation = 100;
            const lightness = 30 + (value * 50);

            // Create gradient for each bar
            const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight);
            gradient.addColorStop(0, `hsl(${hue}, ${saturation}%, ${lightness}%)`);
            gradient.addColorStop(1, `hsl(${hue}, ${saturation}%, ${Math.min(lightness + 20, 80)}%)`);

            ctx.fillStyle = gradient;
            ctx.fillRect(i * barWidth, height - barHeight, barWidth - 2, barHeight);

            // Draw peak indicator
            const peakY = height - (vuMeterState[id].peakValues[i] * height);
            ctx.fillStyle = `hsl(${hue}, 100%, 70%)`;
            ctx.fillRect(i * barWidth, peakY - 2, barWidth - 2, 2);
        }
    });
}

// Play/Pause Toggle for Disk
function toggleDeck(id) {
    if (decks[id].playing) {
        pauseDeck(id);
    } else {
        playDeck(id);
    }
}

// Mobile Tab Switching
function switchTab(tabId) {
    const container = document.querySelector('.app-container');
    const buttons = document.querySelectorAll('.tab-btn');

    // Remove all tab classes
    container.classList.remove('show-library', 'show-deck-A', 'show-deck-B');
    buttons.forEach(btn => btn.classList.remove('active'));

    // Add active class and button state
    container.classList.add('show-' + tabId);

    // Find the button and activate it
    buttons.forEach(btn => {
        if (btn.getAttribute('onclick').includes(tabId)) {
            btn.classList.add('active');
        }
    });

    // Redraw waveforms if switching to a deck
    if (tabId.startsWith('deck')) {
        const id = tabId.split('-')[1];
        setTimeout(() => drawWaveform(id), 100);
    }
}

// Waveform Generation (Optimized for Speed)
function generateWaveformData(buffer) {
    const rawData = buffer.getChannelData(0);
    const samples = 1000;
    const blockSize = Math.floor(rawData.length / samples);
    // Only process a subset of samples for huge speed gain
    const step = Math.max(1, Math.floor(blockSize / 20));
    const filteredData = [];

    for (let i = 0; i < samples; i++) {
        let blockStart = blockSize * i;
        let sum = 0;
        let count = 0;
        for (let j = 0; j < blockSize; j += step) {
            const index = blockStart + j;
            // Bounds check to prevent NaN values
            if (index < rawData.length) {
                sum += Math.abs(rawData[index]);
                count++;
            }
        }
        filteredData.push(sum / (count || 1));
    }
    return filteredData;
}

function drawWaveform(id) {
    const canvas = document.getElementById('waveform-' + id);
    if (!canvas) return; // Null check
    const ctx = canvas.getContext('2d');
    const data = decks[id].waveformData;
    if (!data) return;

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    const width = canvas.width;
    const height = canvas.height;
    const barWidth = width / data.length;

    data.forEach((val, i) => {
        const h = val * height * 5;
        ctx.fillStyle = id === 'A' ? '#00f3ff' : '#bc13fe';
        ctx.fillRect(i * barWidth, (height - h) / 2, barWidth, h);
    });

    // Draw Cues
    if (decks[id].duration > 0) {
        Object.values(decks[id].cues).forEach(time => {
            const x = (time / decks[id].duration) * width;
            ctx.strokeStyle = '#fff';
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.moveTo(x, 0);
            ctx.lineTo(x, height);
            ctx.stroke();
        });
    }

    // Draw Loop Markers
    if (decks[id].loopStart !== null && decks[id].duration > 0) {
        const xIn = (decks[id].loopStart / decks[id].duration) * width;
        ctx.strokeStyle = '#ffbb00';
        ctx.lineWidth = 2;
        ctx.setLineDash([5, 3]);
        ctx.beginPath();
        ctx.moveTo(xIn, 0);
        ctx.lineTo(xIn, height);
        ctx.stroke();

        if (decks[id].loopActive && decks[id].loopEnd !== null) {
            const xOut = (decks[id].loopEnd / decks[id].duration) * width;
            ctx.strokeStyle = '#ffaa33';
            ctx.beginPath();
            ctx.moveTo(xOut, 0);
            ctx.lineTo(xOut, height);
            ctx.stroke();

            // Shade loop area
            ctx.fillStyle = 'rgba(255, 187, 0, 0.15)';
            ctx.fillRect(xIn, 0, xOut - xIn, height);
        }
        ctx.setLineDash([]);
    }
}

// BPM Detection (Optimized: Only check middle 60 seconds for speed)
function detectBPM(buffer) {
    const sampleRate = buffer.sampleRate;
    const duration = buffer.duration;

    // Pick a 60s window in the middle
    const startOffset = Math.max(0, Math.floor((duration / 2 - 30) * sampleRate));
    const endOffset = Math.min(buffer.length, Math.floor((duration / 2 + 30) * sampleRate));
    const data = buffer.getChannelData(0).slice(startOffset, endOffset);

    const bpmRange = [60, 180];
    const windowSize = Math.floor(sampleRate * 60 / bpmRange[1]);

    let peaks = 0;
    let threshold = 0;
    for (let i = 0; i < data.length; i += windowSize) {
        const slice = data.slice(i, i + windowSize);
        const avg = slice.reduce((a, b) => a + Math.abs(b), 0) / slice.length;
        if (avg > threshold) {
            peaks++;
            threshold = avg * 0.8;
        }
    }

    const windowDuration = data.length / sampleRate;
    const bpm = Math.round((peaks / windowDuration) * 60);
    return bpm > bpmRange[0] && bpm < bpmRange[1] ? bpm : 0;
}

// Time Display Updates
function updateTimeDisplays() {
    requestAnimationFrame(updateTimeDisplays);

    // Only update if at least one deck is playing
    const anyPlaying = decks.A.playing || decks.B.playing;
    if (!anyPlaying) return;

    ['A', 'B'].forEach(id => {
        if (decks[id].playing && decks[id].localBuffer) {
            const playbackRate = decks[id].localSource ? decks[id].localSource.playbackRate.value : 1.0;
            const realElapsed = audioCtx.currentTime - decks[id].lastAnchorTime;
            let current = decks[id].lastAnchorPosition + (realElapsed * playbackRate);

            // Handle Looping wrapping for UI
            if (decks[id].loopActive && decks[id].loopStart !== null && decks[id].loopEnd !== null) {
                const loopLen = decks[id].loopEnd - decks[id].loopStart;
                if (current >= decks[id].loopEnd && loopLen > 0) {
                    current = ((current - decks[id].loopStart) % loopLen) + decks[id].loopStart;
                }
            } else if (settings[`repeat${id}`]) {
                // Full song repeat wrapping for UI
                current = current % decks[id].duration;
            } else {
                current = Math.min(current, decks[id].duration);
            }

            document.getElementById('time-current-' + id).textContent = formatTime(current);

            // Update playhead
            const progress = (current / decks[id].duration) * 100;
            const playhead = document.getElementById('playhead-' + id);
            if (playhead) playhead.style.left = progress + '%';
        }
    });
}

function getCurrentPosition(id) {
    if (!decks[id].playing) return decks[id].pausedAt;
    if (!audioCtx) return decks[id].pausedAt;

    const playbackRate = decks[id].localSource ? decks[id].localSource.playbackRate.value : 1.0;
    const realElapsed = audioCtx.currentTime - decks[id].lastAnchorTime;
    let pos = decks[id].lastAnchorPosition + (realElapsed * playbackRate);

    // Handle wrapping for correct position return
    if (decks[id].loopActive && decks[id].loopStart !== null && decks[id].loopEnd !== null) {
        const loopLen = decks[id].loopEnd - decks[id].loopStart;
        if (pos >= decks[id].loopEnd && loopLen > 0) {
            pos = ((pos - decks[id].loopStart) % loopLen) + decks[id].loopStart;
        }
    } else if (settings[`repeat${id}`] && decks[id].duration > 0) {
        pos = pos % decks[id].duration;
    } else {
        pos = Math.min(decks[id].duration, pos);
    }

    return pos;
}

function formatTime(seconds) {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
}

// Playback Logic
function playDeck(id) {
    // Server-side audio mode
    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_play', { deck: id });
        decks[id].playing = true;
        const deckEl = document.getElementById('deck-' + id);
        if (deckEl) deckEl.classList.add('playing');
        console.log(`[Deck ${id}] Play command sent to server`);
        return;
    }

    // Browser-side audio mode (original code)
    if (decks[id].type === 'local' && decks[id].localBuffer) {
        if (decks[id].playing) return;

        try {
            console.log(`[Deck ${id}] Starting playback from ${decks[id].pausedAt}s`);
            decks[id].playing = true;
            seekTo(id, decks[id].pausedAt);

            const deckEl = document.getElementById('deck-' + id);
            if (deckEl) deckEl.classList.add('playing');

            if (audioCtx.state === 'suspended') {
                console.log(`[Deck ${id}] Resuming suspended AudioContext`);
                audioCtx.resume();
            }

            // Auto-start broadcast if enabled and not already broadcasting
            if (autoStartStream && !isBroadcasting && audioCtx) {
                setTimeout(() => {
                    if (!socket) initSocket();
                    setTimeout(() => startBroadcast(), 500);
                }, 100);
            }
        } catch (error) {
            console.error(`[Deck ${id}] Playback error:`, error);
            decks[id].playing = false;
            const deckEl = document.getElementById('deck-' + id);
            if (deckEl) deckEl.classList.remove('playing');
            alert(`Playback error: ${error.message}`);
        }
    } else {
        console.warn(`[Deck ${id}] Cannot play - no buffer loaded`);
    }
}

function pauseDeck(id) {
    // Server-side audio mode
    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_pause', { deck: id });
        decks[id].playing = false;
        document.getElementById('deck-' + id).classList.remove('playing');
        console.log(`[Deck ${id}] Pause command sent to server`);
        return;
    }

    // Browser-side audio mode (original code)
    if (decks[id].type === 'local' && decks[id].localSource && decks[id].playing) {
        if (!audioCtx) {
            console.warn(`[Deck ${id}] Cannot calculate pause position - audioCtx not initialized`);
            decks[id].playing = false;
        } else {
            const playbackRate = decks[id].localSource.playbackRate.value;
            const realElapsed = audioCtx.currentTime - decks[id].lastAnchorTime;
            decks[id].pausedAt = decks[id].lastAnchorPosition + (realElapsed * playbackRate);

            try {
                decks[id].localSource.stop();
                decks[id].localSource.onended = null;
            } catch (e) { }

            decks[id].localSource = null;
            decks[id].playing = false;
        }
    }
    document.getElementById('deck-' + id).classList.remove('playing');
}

function seekTo(id, time) {
    // Update local state and timestamp for seek protection
    decks[id].lastSeekTime = Date.now();

    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_seek', { deck: id, position: time });

        // Update local state immediately for UI responsiveness
        decks[id].lastAnchorPosition = time;
        if (!decks[id].playing) {
            decks[id].pausedAt = time;
        }

        // Update UI immediately (Optimistic UI)
        const progress = (time / decks[id].duration) * 100;
        const playhead = document.getElementById('playhead-' + id);
        if (playhead) playhead.style.left = progress + '%';

        const timer = document.getElementById('time-current-' + id);
        if (timer) timer.textContent = formatTime(time);

        return;
    }

    if (!decks[id].localBuffer) {
        console.warn(`[Deck ${id}] Cannot seek - no buffer loaded`);
        return;
    }

    try {
        if (decks[id].playing) {
            if (decks[id].localSource) {
                try {
                    decks[id].localSource.stop();
                    decks[id].localSource.onended = null;
                } catch (e) { }
            }

            const src = audioCtx.createBufferSource();
            src.buffer = decks[id].localBuffer;

            if (decks[id].loopActive) {
                src.loop = true;
                src.loopStart = decks[id].loopStart || 0;
                src.loopEnd = decks[id].loopEnd || decks[id].duration;
            }

            src.connect(decks[id].filters.low);
            const speedSlider = document.querySelector(`#deck-${id} .speed-slider`);
            const speed = speedSlider ? parseFloat(speedSlider.value) : 1.0;
            src.playbackRate.value = speed;

            decks[id].localSource = src;
            decks[id].lastAnchorTime = audioCtx.currentTime;
            decks[id].lastAnchorPosition = time;

            // Add error handler for the source
            src.onended = () => {
                console.log(`[Deck ${id}] Playback ended naturally`);
            };

            src.start(0, time);
            console.log(`[Deck ${id}] Playback started at ${time}s with speed ${speed}x`);
        } else {
            decks[id].pausedAt = time;
            decks[id].lastAnchorPosition = time;
            if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume();

            // Update UI immediately (Manual Position)
            const timer = document.getElementById('time-current-' + id);
            if (timer) timer.textContent = formatTime(time);
            const progress = (time / decks[id].duration) * 100;
            const playhead = document.getElementById('playhead-' + id);
            if (playhead) playhead.style.left = progress + '%';
        }
    } catch (error) {
        console.error(`[Deck ${id}] SeekTo error:`, error);
        // Reset playing state on error
        decks[id].playing = false;
        const deckEl = document.getElementById('deck-' + id);
        if (deckEl) deckEl.classList.remove('playing');
        document.body.classList.remove('playing-' + id);
    }
}

function changeSpeed(id, val) {
    // Server-side audio mode
    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_set_pitch', { deck: id, pitch: parseFloat(val) });
        return;
    }

    // Browser-side audio mode
    if (!audioCtx || !decks[id].localSource) return;

    const realElapsed = audioCtx.currentTime - decks[id].lastAnchorTime;
    decks[id].lastAnchorPosition += realElapsed * decks[id].localSource.playbackRate.value;
    decks[id].lastAnchorTime = audioCtx.currentTime;

    decks[id].localSource.playbackRate.value = parseFloat(val);
}

function changeVolume(id, val) {
    // Server-side audio mode
    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_set_volume', { deck: id, volume: val / 100 });
        return;
    }

    // Browser-side audio mode
    if (decks[id].volumeGain) {
        decks[id].volumeGain.gain.value = val / 100;
    }
}

function changeEQ(id, band, val) {
    // Server-side audio mode
    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_set_eq', { deck: id, band: band, value: parseFloat(val) });
        return;
    }

    // Browser-side audio mode
    if (decks[id].filters[band]) decks[id].filters[band].gain.value = parseFloat(val);
}

function changeFilter(id, type, val) {
    if (!audioCtx || !decks[id].filters) return;
    const filter = type === 'lowpass' ? decks[id].filters.lp : decks[id].filters.hp;
    if (!filter) return;

    // Use exponential scaling for filter frequency (more musical)
    if (type === 'lowpass') {
        // Low-pass: 0 = low freq (muffled), 100 = high freq (open)
        const freq = 20 * Math.pow(22050 / 20, val / 100);
        filter.frequency.setTargetAtTime(freq, audioCtx.currentTime, 0.05);
    } else {
        // High-pass: 0 = low freq (open), 100 = high freq (thin)
        const freq = 20 * Math.pow(22050 / 20, val / 100);
        filter.frequency.setTargetAtTime(freq, audioCtx.currentTime, 0.05);
    }
}

// Hot Cue Functionality
function handleCue(id, cueNum) {
    if (!decks[id].localBuffer) {
        console.warn(`[Deck ${id}] No track loaded - cannot set cue`);
        return;
    }

    // If cue exists, jump to it and start playing
    if (decks[id].cues[cueNum] !== undefined) {
        const cueTime = decks[id].cues[cueNum];
        console.log(`[Deck ${id}] Jumping to cue ${cueNum} at ${cueTime.toFixed(2)}s`);

        // Always seek to cue point
        seekTo(id, cueTime);

        // Auto-play when triggering cue (like real DJ equipment)
        if (!decks[id].playing) {
            playDeck(id);
        }

        // Visual feedback
        const cueBtn = document.querySelectorAll(`#deck-${id} .cue-btn`)[cueNum - 1];
        if (cueBtn) {
            cueBtn.classList.add('cue-triggered');
            setTimeout(() => cueBtn.classList.remove('cue-triggered'), 200);
        }
    } else {
        // Set new cue at current position
        const currentTime = getCurrentPosition(id);
        decks[id].cues[cueNum] = currentTime;

        console.log(`[Deck ${id}] Set cue ${cueNum} at ${currentTime.toFixed(2)}s`);

        const cueBtn = document.querySelectorAll(`#deck-${id} .cue-btn`)[cueNum - 1];
        if (cueBtn) {
            cueBtn.classList.add('cue-set');
            // Flash to show it was set
            cueBtn.style.animation = 'none';
            setTimeout(() => {
                cueBtn.style.animation = 'flash 0.3s ease';
            }, 10);
        }

        drawWaveform(id);
    }
}

function clearCue(id, cueNum) {
    delete decks[id].cues[cueNum];
    const cueBtn = document.querySelectorAll(`#deck-${id} .cue-btn`)[cueNum - 1];
    if (cueBtn) cueBtn.classList.remove('cue-set');
    drawWaveform(id);
}

function setLoop(id, action) {
    if (!decks[id].localBuffer) {
        console.warn(`[Deck ${id}] No track loaded - cannot set loop`);
        return;
    }

    const currentTime = getCurrentPosition(id);

    if (action === 'in') {
        // Set loop start point
        decks[id].loopStart = currentTime;
        decks[id].loopEnd = null; // Clear loop end
        decks[id].loopActive = false; // Not active until OUT is set

        console.log(`[Deck ${id}] Loop IN set at ${currentTime.toFixed(2)}s`);

        // Visual feedback
        const loopInBtn = document.querySelector(`#deck-${id} .loop-controls button:nth-child(1)`);
        if (loopInBtn) {
            loopInBtn.style.background = 'rgba(255, 187, 0, 0.3)';
            loopInBtn.style.borderColor = '#ffbb00';
        }

    } else if (action === 'out') {
        // Set loop end point and activate
        if (decks[id].loopStart === null) {
            // If no loop start, set it to beginning
            decks[id].loopStart = 0;
            console.log(`[Deck ${id}] Auto-set loop IN at 0s`);
        }

        if (currentTime > decks[id].loopStart) {
            decks[id].loopEnd = currentTime;
            decks[id].loopActive = true;

            const loopLength = decks[id].loopEnd - decks[id].loopStart;
            console.log(`[Deck ${id}] Loop OUT set at ${currentTime.toFixed(2)}s (${loopLength.toFixed(2)}s loop)`);

            // Visual feedback
            const loopOutBtn = document.querySelector(`#deck-${id} .loop-controls button:nth-child(2)`);
            if (loopOutBtn) {
                loopOutBtn.style.background = 'rgba(255, 187, 0, 0.3)';
                loopOutBtn.style.borderColor = '#ffbb00';
            }

            // Restart playback to apply loop immediately
            if (decks[id].playing) {
                const currentPos = getCurrentPosition(id);
                seekTo(id, currentPos);
            }
        } else {
            console.warn(`[Deck ${id}] Loop OUT must be after Loop IN`);
            alert('Loop OUT must be after Loop IN!');
        }

    } else if (action === 'exit') {
        // Get current position BEFORE clearing loop state
        const currentPos = getCurrentPosition(id);

        // Exit/clear loop
        decks[id].loopActive = false;
        decks[id].loopStart = null;
        decks[id].loopEnd = null;

        console.log(`[Deck ${id}] Loop cleared at position ${currentPos.toFixed(2)}s`);

        // Clear visual feedback
        const loopBtns = document.querySelectorAll(`#deck-${id} .loop-controls button`);
        loopBtns.forEach(btn => {
            btn.style.background = '';
            btn.style.borderColor = '';
        });

        // Restart playback from current loop position to continue smoothly
        if (decks[id].playing) {
            seekTo(id, currentPos);
        } else {
            // If paused, just update the paused position
            decks[id].pausedAt = currentPos;
        }
    }

    drawWaveform(id);
}

// Auto-Loop with Beat Lengths (you.dj style)
function setAutoLoop(id, beats) {
    if (!decks[id].localBuffer) {
        console.warn(`[Deck ${id}] No track loaded - cannot set auto-loop`);
        return;
    }

    // Check if clicking the same active loop - if so, disable it
    if (decks[id].activeAutoLoop === beats && decks[id].loopActive) {
        // Get current position WITHIN the loop before disabling
        const currentPos = getCurrentPosition(id);

        // Disable loop
        decks[id].loopActive = false;
        decks[id].loopStart = null;
        decks[id].loopEnd = null;
        decks[id].activeAutoLoop = null;

        console.log(`[Deck ${id}] Auto-loop ${beats} beats disabled at position ${currentPos.toFixed(2)}s`);

        // Update UI
        updateAutoLoopButtons(id);
        drawWaveform(id);

        // Restart playback from current loop position to continue smoothly
        if (decks[id].playing) {
            seekTo(id, currentPos);
        } else {
            // If paused, just update the paused position
            decks[id].pausedAt = currentPos;
        }
        return;
    }

    // Check if BPM is detected
    if (!decks[id].bpm || decks[id].bpm === 0) {
        alert(`Cannot set auto-loop: BPM not detected for Deck ${id}.\nPlease use manual loop controls (LOOP IN/OUT).`);
        console.warn(`[Deck ${id}] BPM not detected - cannot calculate auto-loop`);
        return;
    }

    const currentTime = getCurrentPosition(id);
    const bpm = decks[id].bpm;

    // Calculate loop length in seconds: (beats / BPM) * 60
    const loopLength = (beats / bpm) * 60;

    // Set loop points
    decks[id].loopStart = currentTime;
    decks[id].loopEnd = currentTime + loopLength;
    decks[id].loopActive = true;
    decks[id].activeAutoLoop = beats;

    console.log(`[Deck ${id}] Auto-loop set: ${beats} beats = ${loopLength.toFixed(3)}s at ${bpm} BPM`);

    // Update UI
    updateAutoLoopButtons(id);
    drawWaveform(id);

    // Restart playback to apply loop immediately
    if (decks[id].playing) {
        seekTo(id, currentTime);
    }
}

// Update auto-loop button visual states
function updateAutoLoopButtons(id) {
    const buttons = document.querySelectorAll(`#deck-${id} .auto-loop-btn`);
    buttons.forEach(btn => {
        const btnBeats = parseFloat(btn.dataset.beats);
        if (decks[id].activeAutoLoop === btnBeats && decks[id].loopActive) {
            btn.classList.add('active');
        } else {
            btn.classList.remove('active');
        }
    });
}

function pitchBend(id, amount) {
    if (!audioCtx || !decks[id].localSource) return;

    const slider = document.querySelector(`#deck-${id} .speed-slider`);
    const baseSpeed = parseFloat(slider.value);

    // Re-anchor
    const realElapsed = audioCtx.currentTime - decks[id].lastAnchorTime;
    decks[id].lastAnchorPosition += realElapsed * decks[id].localSource.playbackRate.value;
    decks[id].lastAnchorTime = audioCtx.currentTime;

    if (amount !== 0) {
        decks[id].localSource.playbackRate.value = baseSpeed + amount;
    } else {
        decks[id].localSource.playbackRate.value = baseSpeed;
    }
}

function syncDecks(id) {
    const otherDeck = id === 'A' ? 'B' : 'A';
    if (!decks[id].bpm || !decks[otherDeck].bpm) return;

    // Calculate ratio to make other deck match this deck
    const ratio = decks[id].bpm / decks[otherDeck].bpm;
    const slider = document.querySelector(`#deck-${otherDeck} .speed-slider`);
    if (slider) slider.value = ratio;
    changeSpeed(otherDeck, ratio);
}

function updateCrossfader(val) {
    // Server-side audio mode
    if (SERVER_SIDE_AUDIO) {
        if (!socket) initSocket();
        socket.emit('audio_set_crossfader', { value: parseInt(val) });
        return;
    }

    // Browser-side audio mode
    const volA = (100 - val) / 100;
    const volB = val / 100;
    if (decks.A.crossfaderGain) decks.A.crossfaderGain.gain.value = volA;
    if (decks.B.crossfaderGain) decks.B.crossfaderGain.gain.value = volB;
}

// Library Functions
async function fetchLibrary() {
    try {
        const res = await fetch('library.json?t=' + new Date().getTime());
        allSongs = await res.json();
        renderLibrary(allSongs);
    } catch (e) {
        console.error("Library fetch failed", e);
    }
}

function renderLibrary(songs) {
    const list = document.getElementById('library-list');
    list.innerHTML = '';
    if (songs.length === 0) {
        list.innerHTML = '<div style="padding:10px; opacity:0.5;">Library empty. Download some music!</div>';
        return;
    }
    songs.forEach(t => {
        const item = document.createElement('div');
        item.className = 'track-row';

        const trackName = document.createElement('span');
        trackName.className = 'track-name';
        trackName.textContent = t.title; // Safe text assignment

        const loadActions = document.createElement('div');
        loadActions.className = 'load-actions';

        // LOAD buttons
        const btnA = document.createElement('button');
        btnA.className = 'load-btn btn-a';
        btnA.textContent = 'LOAD A';
        btnA.addEventListener('click', () => loadFromServer('A', t.file, t.title));

        const btnB = document.createElement('button');
        btnB.className = 'load-btn btn-b';
        btnB.textContent = 'LOAD B';
        btnB.addEventListener('click', () => loadFromServer('B', t.file, t.title));

        // QUEUE buttons
        const queueA = document.createElement('button');
        queueA.className = 'load-btn queue-btn-a';
        queueA.textContent = '📋 Q-A';
        queueA.title = 'Add to Queue A';
        queueA.addEventListener('click', () => addToQueue('A', t.file, t.title));

        const queueB = document.createElement('button');
        queueB.className = 'load-btn queue-btn-b';
        queueB.textContent = '📋 Q-B';
        queueB.title = 'Add to Queue B';
        queueB.addEventListener('click', () => addToQueue('B', t.file, t.title));

        loadActions.appendChild(btnA);
        loadActions.appendChild(queueA);
        loadActions.appendChild(btnB);
        loadActions.appendChild(queueB);
        item.appendChild(trackName);
        item.appendChild(loadActions);

        // Add data attribute for highlighting
        item.dataset.file = t.file;

        list.appendChild(item);
    });

    // Update highlighting after rendering
    updateLibraryHighlighting();
}

// Update library highlighting to show which tracks are loaded
function updateLibraryHighlighting() {
    const trackRows = document.querySelectorAll('.track-row');

    trackRows.forEach(row => {
        const file = row.dataset.file;

        // Remove all deck classes
        row.classList.remove('loaded-deck-a', 'loaded-deck-b', 'loaded-both');

        const onDeckA = decks.A.currentFile && decks.A.currentFile.includes(file);
        const onDeckB = decks.B.currentFile && decks.B.currentFile.includes(file);

        if (onDeckA && onDeckB) {
            row.classList.add('loaded-both');
        } else if (onDeckA) {
            row.classList.add('loaded-deck-a');
        } else if (onDeckB) {
            row.classList.add('loaded-deck-b');
        }
    });
}

// Utility function no longer needed but kept for future use
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

function filterLibrary() {
    const query = document.getElementById('lib-search').value.toLowerCase();
    const filtered = allSongs.filter(s => s.title.toLowerCase().includes(query));
    renderLibrary(filtered);
}

function refreshLibrary() {
    fetchLibrary();
}

// YouTube Search Functions
let youtubeSearchTimeout = null;

function handleYouTubeSearch(query) {
    // Debounce search - wait 300ms after user stops typing
    clearTimeout(youtubeSearchTimeout);

    if (!query || query.trim().length < 2) {
        document.getElementById('youtube-results').innerHTML = '';
        return;
    }

    youtubeSearchTimeout = setTimeout(() => {
        searchYouTube(query.trim());
    }, 300);
}

async function searchYouTube(query) {
    const resultsDiv = document.getElementById('youtube-results');
    resultsDiv.innerHTML = '<div class="search-loading">🔍 Searching YouTube...</div>';

    try {
        const response = await fetch(`/search_youtube?q=${encodeURIComponent(query)}`);
        const data = await response.json();

        if (!data.success) {
            resultsDiv.innerHTML = `<div class="search-error">❌ ${data.error}</div>`;
            return;
        }

        displayYouTubeResults(data.results);
    } catch (error) {
        console.error('YouTube search error:', error);
        resultsDiv.innerHTML = '<div class="search-error">❌ Search failed. Check console.</div>';
    }
}

function displayYouTubeResults(results) {
    const resultsDiv = document.getElementById('youtube-results');

    if (results.length === 0) {
        resultsDiv.innerHTML = '<div class="search-empty">No results found</div>';
        return;
    }

    resultsDiv.innerHTML = '';

    results.forEach(result => {
        const resultCard = document.createElement('div');
        resultCard.className = 'youtube-result-card';

        resultCard.innerHTML = `
            <img src="${result.thumbnail}" alt="${result.title}" class="result-thumbnail">
            <div class="result-info">
                <div class="result-title">${result.title}</div>
                <div class="result-channel">${result.channel}</div>
            </div>
            <button class="result-download-btn" onclick="downloadYouTubeResult('${result.url}', '${result.title.replace(/'/g, "\\'")}')">
                ⬇️
            </button>
        `;

        resultsDiv.appendChild(resultCard);
    });
}

async function downloadYouTubeResult(url, title) {
    const resultsDiv = document.getElementById('youtube-results');
    const originalContent = resultsDiv.innerHTML;

    resultsDiv.innerHTML = '<div class="search-loading">⏳ Downloading...</div>';

    try {
        const response = await fetch('/download', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ url: url, quality: '320' })
        });

        const result = await response.json();

        if (result.success) {
            resultsDiv.innerHTML = '<div class="search-success">✅ Downloaded! Refreshing library...</div>';
            await fetchLibrary();
            setTimeout(() => {
                resultsDiv.innerHTML = originalContent;
            }, 2000);
        } else {
            resultsDiv.innerHTML = `<div class="search-error">❌ Download failed: ${result.error}</div>`;
            setTimeout(() => {
                resultsDiv.innerHTML = originalContent;
            }, 3000);
        }
    } catch (error) {
        console.error('Download error:', error);
        resultsDiv.innerHTML = '<div class="search-error">❌ Download failed</div>';
        setTimeout(() => {
            resultsDiv.innerHTML = originalContent;
        }, 3000);
    }
}

async function loadFromServer(id, url, title) {
    const d = document.getElementById('display-' + id);
    d.innerText = '⏳ LOADING...';
    d.classList.add('blink');

    console.log(`[Deck ${id}] Loading: ${title} from ${url}`);

    // Server-side audio mode: Send command immediately but CONTINUE for local UI/waveform
    if (SERVER_SIDE_AUDIO) {
        // Extract filename from URL and DECODE IT (for spaces etc)
        const filename = decodeURIComponent(url.split('/').pop());

        if (!socket) initSocket();
        socket.emit('audio_load_track', { deck: id, filename: filename });
        console.log(`[Deck ${id}] 📡 Load command sent to server: ${filename}`);

        // We DON'T return here anymore. We continue below to load for the UI.
    }

    // Browser-side audio mode (original code)
    const wasPlaying = decks[id].playing;
    const wasBroadcasting = isBroadcasting;

    if (wasPlaying && !wasBroadcasting) {
        pauseDeck(id);
        console.log(`[Deck ${id}] Paused for song load`);
    } else if (wasPlaying && wasBroadcasting) {
        console.log(`[Deck ${id}] ⚡ BROADCAST MODE: Keeping deck playing during load to maintain stream`);
    }

    decks[id].waveformData = null;
    decks[id].bpm = null;
    decks[id].cues = {};
    decks[id].pausedAt = 0;
    decks[id].currentFile = url;

    // Clear UI state
    const bpmEl = document.getElementById('bpm-' + id);
    if (bpmEl) bpmEl.textContent = '';
    const cueBtns = document.querySelectorAll(`#deck-${id} .cue-btn`);
    cueBtns.forEach(btn => btn.classList.remove('cue-set'));

    try {
        // Fetch the audio file
        const res = await fetch(url);
        if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);

        console.log(`[Deck ${id}] Fetch successful, decoding audio...`);
        const arrayBuffer = await res.arrayBuffer();

        // Decode audio data
        let buffer;
        try {
            buffer = await audioCtx.decodeAudioData(arrayBuffer);
        } catch (decodeError) {
            console.error(`[Deck ${id}] Audio decode failed:`, decodeError);
            throw new Error(`Cannot decode audio file`);
        }

        console.log(`[Deck ${id}] Successfully decoded! Duration: ${buffer.duration}s`);

        // Track is now ready!
        decks[id].localBuffer = buffer;
        decks[id].duration = buffer.duration;
        decks[id].lastAnchorPosition = 0;

        // Update UI - track is ready!
        d.innerText = title.toUpperCase();
        d.classList.remove('blink');
        document.getElementById('time-total-' + id).textContent = formatTime(buffer.duration);
        document.getElementById('time-current-' + id).textContent = '0:00';

        console.log(`[Deck ${id}] Ready to play!`);

        // AUTO-RESUME for broadcast continuity
        if (wasPlaying && wasBroadcasting) {
            console.log(`[Deck ${id}] 🎵 Auto-resuming playback to maintain broadcast stream`);
            // Small delay to ensure buffer is fully ready
            setTimeout(() => {
                playDeck(id);
            }, 50);
        }

        // Update library highlight
        updateLibraryHighlighting();

        // Process waveform and BPM in background
        const processInBackground = () => {
            try {
                console.log(`[Deck ${id}] Starting background processing...`);

                // Generate waveform
                const startWave = performance.now();
                decks[id].waveformData = generateWaveformData(buffer);
                console.log(`[Deck ${id}] Waveform generated in ${(performance.now() - startWave).toFixed(0)}ms`);
                drawWaveform(id);

                // Detect BPM
                const startBPM = performance.now();
                decks[id].bpm = detectBPM(buffer);
                console.log(`[Deck ${id}] BPM detected in ${(performance.now() - startBPM).toFixed(0)}ms`);
                if (decks[id].bpm) {
                    const bpmEl = document.getElementById('bpm-' + id);
                    if (bpmEl) bpmEl.textContent = `${decks[id].bpm} BPM`;
                }
            } catch (bgError) {
                console.warn(`[Deck ${id}] Background processing error:`, bgError);
            }
        };

        if (window.requestIdleCallback) {
            requestIdleCallback(processInBackground, { timeout: 2000 });
        } else {
            setTimeout(processInBackground, 50);
        }

    } catch (error) {
        console.error(`[Deck ${id}] Load error:`, error);
        d.innerText = 'LOAD ERROR';
        d.classList.remove('blink');
        setTimeout(() => { d.innerText = 'NO TRACK'; }, 3000);
    }
}

// Download Functionality
async function downloadFromPanel(deckId) {
    const input = document.getElementById('search-input-' + deckId);
    const statusDiv = document.getElementById('download-status-' + deckId);
    const qualitySelect = document.getElementById('quality-' + deckId);
    const url = input.value.trim();
    if (!url) return;

    statusDiv.innerHTML = '<div class="progress-text">Downloading...</div>';
    try {
        const res = await fetch('/download', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ url: url, quality: qualitySelect.value })
        });
        const result = await res.json();
        if (result.success) {
            statusDiv.innerHTML = '✅ Complete!';
            fetchLibrary();
            setTimeout(() => statusDiv.innerHTML = '', 3000);
        } else {
            statusDiv.innerHTML = '❌ Failed';
        }
    } catch (e) {
        statusDiv.innerHTML = '❌ Error';
    }
}

// Settings
function toggleSettings() {
    const p = document.getElementById('settings-panel');
    p.classList.toggle('active');
}

function toggleRepeat(id, val) { settings[`repeat${id}`] = val; }
function toggleAutoMix(val) { settings.autoMix = val; }
function toggleShuffle(val) { settings.shuffleMode = val; }
function toggleQuantize(val) { settings.quantize = val; }
function toggleAutoPlay(val) { settings.autoPlay = val; }

function updateManualGlow(id, val) {
    settings[`glow${id}`] = val;
    if (val) {
        document.body.classList.add(`playing-${id}`);
    } else {
        document.body.classList.remove(`playing-${id}`);
    }
}

function updateGlowIntensity(val) {
    settings.glowIntensity = parseInt(val);
    const opacity = settings.glowIntensity / 100;
    const spread = (settings.glowIntensity / 100) * 80;

    // Dynamically update CSS variables for the glow
    document.documentElement.style.setProperty('--glow-opacity', opacity);
    document.documentElement.style.setProperty('--glow-spread', `${spread}px`);
}

// Dismiss landscape prompt
function dismissLandscapePrompt() {
    const prompt = document.getElementById('landscape-prompt');
    if (prompt) {
        prompt.classList.add('dismissed');
        // Store preference in localStorage
        localStorage.setItem('landscapePromptDismissed', 'true');
    }
}

// Check if prompt was previously dismissed
window.addEventListener('DOMContentLoaded', () => {
    const wasDismissed = localStorage.getItem('landscapePromptDismissed');
    if (wasDismissed === 'true') {
        const prompt = document.getElementById('landscape-prompt');
        if (prompt) prompt.classList.add('dismissed');
    }

    // Initialize glow intensity
    updateGlowIntensity(settings.glowIntensity);
    const glowAToggle = document.getElementById('glow-A');
    if (glowAToggle) glowAToggle.checked = settings.glowA;
    const glowBToggle = document.getElementById('glow-B');
    if (glowBToggle) glowBToggle.checked = settings.glowB;
    const intensitySlider = document.getElementById('glow-intensity');
    if (intensitySlider) intensitySlider.value = settings.glowIntensity;

    // Apply initial glow state
    updateManualGlow('A', settings.glowA);
    updateManualGlow('B', settings.glowB);

    // Dual-Port Logic: Port 5001 is for Listeners, 5000 is for DJs
    const isListenerPort = window.location.port === '5001';
    const urlParams = new URLSearchParams(window.location.search);

    if (isListenerPort || urlParams.get('listen') === 'true') {
        initListenerMode();
    }

    if (!isListenerPort) {
        // Set stream URL to the dedicated listener port (5001)
        const streamUrl = `${window.location.protocol}//${window.location.hostname}:5001`;
        const streamInput = document.getElementById('stream-url');
        if (streamInput) streamInput.value = streamUrl;
    }
});

// ========== LIVE STREAMING FUNCTIONALITY ==========

let socket = null;
let streamDestination = null;
let streamProcessor = null;
let isBroadcasting = false;
let autoStartStream = false;
let listenerAudioContext = null;
let listenerGainNode = null;

// Initialize SocketIO connection
function initSocket() {
    if (socket) return socket;

    // Log connection details
    const serverUrl = window.location.origin;
    console.log(`🔌 Initializing Socket.IO connection to: ${serverUrl}`);
    console.log(`   Protocol: ${window.location.protocol}`);
    console.log(`   Host: ${window.location.host}`);

    socket = io({
        transports: ['websocket'],
        reconnection: true,
        reconnectionAttempts: 10
    });

    socket.on('connect', () => {
        console.log('✅ Connected to streaming server');
        console.log(`   Socket ID: ${socket.id}`);
        console.log(`   Transport: ${socket.io.engine.transport.name}`);
    });

    socket.on('connect_error', (error) => {
        console.error('❌ Connection error:', error.message);
        console.error('   Make sure server is running on', serverUrl);
    });

    socket.on('disconnect', (reason) => {
        console.log('❌ Disconnected from streaming server');
        console.log(`   Reason: ${reason}`);
    });

    socket.on('listener_count', (data) => {
        const el = document.getElementById('listener-count');
        if (el) el.textContent = data.count;
    });

    socket.on('broadcast_started', () => {
        console.log('🎙️ Broadcast started notification received');
    });

    socket.on('broadcast_stopped', () => {
        console.log('🛑 Broadcast stopped notification received');
    });

    socket.on('mixer_status', (data) => {
        updateUIFromMixerStatus(data);
    });

    socket.on('deck_status', (data) => {
        console.log(`📡 Server: Deck ${data.deck_id} status update:`, data);
        // This is handled by a single status update too, but helpful for immediate feedback
    });

    socket.on('error', (data) => {
        console.error('📡 Server error:', data.message);
        alert(`SERVER ERROR: ${data.message}`);
    });

    return socket;
}

// Update DJ UI from server status
function updateUIFromMixerStatus(status) {
    if (!status) return;

    ['A', 'B'].forEach(id => {
        const deckStatus = id === 'A' ? status.deck_a : status.deck_b;
        if (!deckStatus) return;

        // Update position (only if not currently dragging the waveform)
        const timeSinceSeek = Date.now() - (decks[id].lastSeekTime || 0);
        if (timeSinceSeek < 1500) {
            // Seek Protection: Ignore server updates for 1.5s after manual seek
            return;
        }

        // Update playing state
        decks[id].playing = deckStatus.playing;

        // Update loaded track if changed
        if (deckStatus.filename && (!decks[id].currentFile || decks[id].currentFile !== deckStatus.filename)) {
            console.log(`📡 Server synced: Deck ${id} is playing ${deckStatus.filename}`);
            decks[id].currentFile = deckStatus.filename;
            decks[id].duration = deckStatus.duration;

            // Update UI elements for track title
            const titleEl = document.getElementById(`display-${id}`);
            if (titleEl) {
                const name = deckStatus.filename.split('/').pop().replace(/\.[^/.]+$/, "");
                titleEl.textContent = name;
            }
        }

        // Sync playback rate for interpolation
        const speedSlider = document.querySelector(`#deck-${id} .speed-slider`);
        if (speedSlider) speedSlider.value = deckStatus.pitch;

        // Update anchor for local interpolation
        decks[id].lastAnchorPosition = deckStatus.position;
        decks[id].lastAnchorTime = audioCtx ? audioCtx.currentTime : 0;

        if (!decks[id].playing) {
            decks[id].pausedAt = deckStatus.position;
        }

        // Forced UI update to snap to server reality
        const currentPos = getCurrentPosition(id);
        const progress = (currentPos / decks[id].duration) * 100;
        const playhead = document.getElementById('playhead-' + id);
        if (playhead) playhead.style.left = progress + '%';
        const timer = document.getElementById('time-current-' + id);
        if (timer) timer.textContent = formatTime(currentPos);
    });

    // Update crossfader if changed significantly
    if (Math.abs(decks.crossfader - status.crossfader) > 1) {
        // We'd update the UI slider here
    }
}

// Toggle streaming panel
function toggleStreamingPanel() {
    const panel = document.getElementById('streaming-panel');
    panel.classList.toggle('active');

    // Initialize socket when panel is opened
    if (panel.classList.contains('active') && !socket) {
        initSocket();
    }
}

// Toggle broadcast
function toggleBroadcast() {
    if (!audioCtx) {
        alert('Please initialize the system first (click INITIALIZE SYSTEM)');
        return;
    }

    if (!socket) initSocket();

    if (isBroadcasting) {
        stopBroadcast();
    } else {
        startBroadcast();
    }
}

// Start broadcasting
// Start broadcasting
function startBroadcast() {
    try {
        console.log('🎙️ Starting broadcast...');

        if (!audioCtx) {
            alert('Please initialize the system first!');
            return;
        }

        // Server-side audio mode
        if (SERVER_SIDE_AUDIO) {
            isBroadcasting = true;
            document.getElementById('broadcast-btn').classList.add('active');
            document.getElementById('broadcast-text').textContent = 'STOP BROADCAST';
            document.getElementById('broadcast-status').textContent = '🔴 LIVE';
            document.getElementById('broadcast-status').classList.add('live');

            if (!socket) initSocket();
            socket.emit('start_broadcast');
            socket.emit('get_listener_count');

            console.log('✅ Server-side broadcast started');
            return;
        }

        // Browser-side audio mode (original code)
        // Check if any audio is playing
        const anyPlaying = decks.A.playing || decks.B.playing;
        if (!anyPlaying) {
            console.warn('⚠️ WARNING: No decks are currently playing! Start playing a track for audio to stream.');
        }

        // Create MediaStreamDestination to capture audio output
        streamDestination = audioCtx.createMediaStreamDestination();

        // IMPORTANT: Properly manage audio graph connections
        // Disconnect from speakers first, then connect to stream AND speakers
        // This prevents dual-connection instability
        if (decks.A.crossfaderGain) {
            try {
                decks.A.crossfaderGain.disconnect();
            } catch (e) {
                console.warn('Deck A crossfader already disconnected');
            }
            decks.A.crossfaderGain.connect(streamDestination);
            decks.A.crossfaderGain.connect(audioCtx.destination); // Re-add for local monitoring
            console.log('✅ Deck A connected to stream + speakers');
        }
        if (decks.B.crossfaderGain) {
            try {
                decks.B.crossfaderGain.disconnect();
            } catch (e) {
                console.warn('Deck B crossfader already disconnected');
            }
            decks.B.crossfaderGain.connect(streamDestination);
            decks.B.crossfaderGain.connect(audioCtx.destination); // Re-add for local monitoring
            console.log('✅ Deck B connected to stream + speakers');
        }

        // Verify stream has audio tracks
        const stream = streamDestination.stream;
        console.log(`📊 Stream tracks: ${stream.getAudioTracks().length} audio tracks`);

        if (stream.getAudioTracks().length === 0) {
            throw new Error('No audio tracks in stream! Audio routing failed.');
        }

        // Get selected quality from dropdown
        const qualitySelect = document.getElementById('stream-quality');
        const selectedBitrate = parseInt(qualitySelect.value) * 1000; // Convert kbps to bps
        console.log(`🎚️ Starting broadcast at ${qualitySelect.value}kbps`);

        mediaRecorder = new MediaRecorder(stream, {
            mimeType: 'audio/webm;codecs=opus',
            audioBitsPerSecond: selectedBitrate
        });

        let chunkCount = 0;
        let lastLogTime = Date.now();
        let silenceWarningShown = false;

        // Send audio chunks via SocketIO
        mediaRecorder.ondataavailable = async (event) => {
            if (event.data.size > 0 && isBroadcasting && socket) {
                chunkCount++;

                // Warn if chunks are too small (likely silence)
                if (event.data.size < 100 && !silenceWarningShown) {
                    console.warn('⚠️ Audio chunks are very small - might be silence. Make sure audio is playing!');
                    silenceWarningShown = true;
                }

                // Convert blob to array buffer for transmission
                const buffer = await event.data.arrayBuffer();
                socket.emit('audio_chunk', buffer); // Send raw ArrayBuffer directly

                // Log every second
                const now = Date.now();
                if (now - lastLogTime > 1000) {
                    console.log(`📡 Broadcasting: ${chunkCount} chunks sent (${(event.data.size / 1024).toFixed(1)} KB/chunk)`);
                    lastLogTime = now;

                    // Reset silence warning
                    if (event.data.size > 100) {
                        silenceWarningShown = false;
                    }
                }
            } else {
                // Debug why chunks aren't being sent
                if (event.data.size === 0) {
                    console.warn('⚠️ Received empty audio chunk');
                }
                if (!isBroadcasting) {
                    console.warn('⚠️ Broadcasting flag is false');
                }
                if (!socket) {
                    console.warn('⚠️ Socket not connected');
                }
            }
        };

        mediaRecorder.onerror = (error) => {
            console.error('❌ MediaRecorder error:', error);
            // Try to recover from error
            if (isBroadcasting) {
                console.log('🔄 Attempting to recover from MediaRecorder error...');
                setTimeout(() => {
                    if (isBroadcasting) {
                        restartBroadcast();
                    }
                }, 2000);
            }
        };

        mediaRecorder.onstart = () => {
            console.log('✅ MediaRecorder started');
        };

        mediaRecorder.onstop = (event) => {
            console.warn('⚠️ MediaRecorder stopped!');
            console.log(`   State: ${mediaRecorder.state}`);
            console.log(`   isBroadcasting flag: ${isBroadcasting}`);

            // If we're supposed to be broadcasting but MediaRecorder stopped, restart it
            if (isBroadcasting) {
                console.error('❌ MediaRecorder stopped unexpectedly while broadcasting!');
                console.log('🔄 Auto-recovery: Attempting to restart broadcast in 2 seconds...');

                setTimeout(() => {
                    if (isBroadcasting) {
                        console.log('🔄 Executing auto-recovery...');
                        restartBroadcast();
                    }
                }, 2000);
            }
        };

        mediaRecorder.onpause = (event) => {
            console.warn('⚠️ MediaRecorder paused unexpectedly!');

            // If we're broadcasting and MediaRecorder paused, resume it
            if (isBroadcasting && mediaRecorder.state === 'paused') {
                console.log('🔄 Auto-resuming MediaRecorder...');
                try {
                    mediaRecorder.resume();
                    console.log('✅ MediaRecorder resumed');
                } catch (e) {
                    console.error('❌ Failed to resume MediaRecorder:', e);
                    // If resume fails, try full restart
                    setTimeout(() => {
                        if (isBroadcasting) {
                            restartBroadcast();
                        }
                    }, 1000);
                }
            }
        };

        // 1000ms chunks: Dramatically reduces CPU interrupts on low-RAM machines
        // Validate state before starting
        if (mediaRecorder.state === 'inactive') {
            mediaRecorder.start(1000);
            streamProcessor = mediaRecorder;
            console.log('✅ MediaRecorder started in state:', mediaRecorder.state);
        } else {
            console.error('❌ Cannot start MediaRecorder - already in state:', mediaRecorder.state);
            throw new Error(`MediaRecorder is already ${mediaRecorder.state}`);
        }

        // Update UI
        isBroadcasting = true;
        document.getElementById('broadcast-btn').classList.add('active');
        document.getElementById('broadcast-text').textContent = 'STOP BROADCAST';
        document.getElementById('broadcast-status').textContent = '🔴 LIVE';
        document.getElementById('broadcast-status').classList.add('live');

        // Notify server
        if (!socket) initSocket();
        socket.emit('start_broadcast');
        socket.emit('get_listener_count');

        console.log('✅ Broadcasting started successfully!');
        console.log('💡 TIP: Play a track on Deck A or B to stream audio');

        // Monitor audio levels
        setTimeout(() => {
            if (chunkCount === 0) {
                console.error('❌ NO AUDIO CHUNKS after 2 seconds! Check:');
                console.error('   1. Is audio playing on either deck?');
                console.error('   2. Is volume turned up?');
                console.error('   3. Is crossfader in the middle?');
            }
        }, 2000);

    } catch (error) {
        console.error('❌ Failed to start broadcast:', error);
        alert('Failed to start broadcast: ' + error.message);
        isBroadcasting = false;
    }
}

// Stop broadcasting
function stopBroadcast() {
    console.log('🛑 Stopping broadcast...');

    if (SERVER_SIDE_AUDIO) {
        isBroadcasting = false;
        if (socket) {
            socket.emit('stop_broadcast');
        }
    } else {
        if (streamProcessor) {
            streamProcessor.stop();
            streamProcessor = null;
        }

        if (streamDestination) {
            // Disconnect from stream destination and restore normal playback
            if (decks.A.crossfaderGain) {
                try {
                    decks.A.crossfaderGain.disconnect(streamDestination);
                    // Ensure connection to speakers is maintained
                    decks.A.crossfaderGain.disconnect();
                    decks.A.crossfaderGain.connect(audioCtx.destination);
                } catch (e) {
                    console.warn('Error restoring Deck A audio:', e);
                }
            }
            if (decks.B.crossfaderGain) {
                try {
                    decks.B.crossfaderGain.disconnect(streamDestination);
                    // Ensure connection to speakers is maintained
                    decks.B.crossfaderGain.disconnect();
                    decks.B.crossfaderGain.connect(audioCtx.destination);
                } catch (e) {
                    console.warn('Error restoring Deck B audio:', e);
                }
            }
            streamDestination = null;
        }
        isBroadcasting = false;

        // Notify server (browser-side mode also needs to tell server to stop relaying)
        if (socket) {
            socket.emit('stop_broadcast');
        }
    }

    // Update UI
    document.getElementById('broadcast-btn').classList.remove('active');
    document.getElementById('broadcast-text').textContent = 'START BROADCAST';
    document.getElementById('broadcast-status').textContent = 'Offline';
    document.getElementById('broadcast-status').classList.remove('live');

    console.log('✅ Broadcast stopped');
}


// Restart broadcasting (for auto-recovery)
function restartBroadcast() {
    console.log('🔄 Restarting broadcast...');

    // Clean up old MediaRecorder without changing UI state
    if (streamProcessor) {
        try {
            streamProcessor.stop();
        } catch (e) {
            console.warn('Could not stop old MediaRecorder:', e);
        }
        streamProcessor = null;
    }

    // Clean up old stream destination
    if (streamDestination) {
        if (decks.A.crossfaderGain) {
            try {
                decks.A.crossfaderGain.disconnect(streamDestination);
                decks.A.crossfaderGain.disconnect();
                decks.A.crossfaderGain.connect(audioCtx.destination);
            } catch (e) {
                console.warn('Error cleaning up Deck A:', e);
            }
        }
        if (decks.B.crossfaderGain) {
            try {
                decks.B.crossfaderGain.disconnect(streamDestination);
                decks.B.crossfaderGain.disconnect();
                decks.B.crossfaderGain.connect(audioCtx.destination);
            } catch (e) {
                console.warn('Error cleaning up Deck B:', e);
            }
        }
        streamDestination = null;
    }

    // Preserve broadcasting state
    const wasBroadcasting = isBroadcasting;
    isBroadcasting = false; // Temporarily set to false so startBroadcast works

    // Small delay to ensure cleanup completes
    setTimeout(() => {
        if (wasBroadcasting) {
            startBroadcast();
            console.log('✅ Broadcast restarted successfully');
        }
    }, 100);
}

// Copy stream URL to clipboard
function copyStreamUrl() {
    const urlInput = document.getElementById('stream-url');
    urlInput.select();
    urlInput.setSelectionRange(0, 99999); // For mobile

    try {
        document.execCommand('copy');
        const btn = event.target;
        const originalText = btn.textContent;
        btn.textContent = '✓';
        setTimeout(() => {
            btn.textContent = originalText;
        }, 2000);
    } catch (err) {
        console.error('Failed to copy:', err);
    }
}

// Toggle auto-start stream
function toggleAutoStream(enabled) {
    autoStartStream = enabled;
    localStorage.setItem('autoStartStream', enabled);
}

// ========== LISTENER MODE ==========

function initListenerMode() {
    console.log('🎧 Initializing listener mode (MediaSource Pipeline)...');

    // UI Feedback for listener
    const appContainer = document.querySelector('.app-container');
    const settingsBtn = document.querySelector('.settings-btn');
    const streamingBtn = document.querySelector('.streaming-btn');
    if (appContainer) appContainer.style.display = 'none';
    if (settingsBtn) settingsBtn.style.display = 'none';
    if (streamingBtn) streamingBtn.style.display = 'none';

    const startOverlay = document.getElementById('start-overlay');
    if (startOverlay) startOverlay.style.display = 'none';

    // Hide landscape prompt for listeners (not needed)
    const landscapePrompt = document.getElementById('landscape-prompt');
    if (landscapePrompt) landscapePrompt.style.display = 'none';

    const listenerMode = document.getElementById('listener-mode');
    if (listenerMode) listenerMode.style.display = 'flex';

    // Add atmospheric glow for listeners
    document.body.classList.add('listener-glow');
    document.body.classList.add('listening-active'); // For global CSS targeting
    updateGlowIntensity(settings.glowIntensity || 30);

    // Setup Audio Context for volume/routing
    listenerAudioContext = new (window.AudioContext || window.webkitAudioContext)();
    listenerGainNode = listenerAudioContext.createGain();
    listenerGainNode.gain.value = 0.8;
    listenerGainNode.connect(listenerAudioContext.destination);

    // Create a hidden audio element to handle the MediaSource
    const audio = new Audio();
    audio.autoplay = true;
    audio.hidden = true;
    document.body.appendChild(audio);

    // Bridge Audio Element to AudioContext
    const sourceNode = listenerAudioContext.createMediaElementSource(audio);
    sourceNode.connect(listenerGainNode);

    // Initialize MediaSource for streaming binary chunks
    const mediaSource = new MediaSource();
    audio.src = URL.createObjectURL(mediaSource);

    let sourceBuffer = null;
    let audioQueue = [];
    let chunksReceived = 0;
    let lastStatusUpdate = 0;

    mediaSource.addEventListener('sourceopen', () => {
        console.log('📦 MediaSource opened');
        try {
            sourceBuffer = mediaSource.addSourceBuffer('audio/webm;codecs=opus');
            sourceBuffer.mode = 'sequence';

            // Request the startup header now that we are ready to receive it
            console.log('📡 Requesting stream header...');
            socket.emit('request_header');

            // Kick off first append if data is already in queue
            if (audioQueue.length > 0 && !sourceBuffer.updating && mediaSource.readyState === 'open') {
                sourceBuffer.appendBuffer(audioQueue.shift());
            }

            sourceBuffer.addEventListener('updateend', () => {
                if (audioQueue.length > 0 && !sourceBuffer.updating) {
                    sourceBuffer.appendBuffer(audioQueue.shift());
                }
            });
        } catch (e) {
            console.error('❌ Failed to add SourceBuffer:', e);
        }
    });

    // Show enable audio button instead of attempting autoplay
    const enableAudioBtn = document.getElementById('enable-audio-btn');
    const statusEl = document.getElementById('connection-status');

    if (enableAudioBtn) {
        enableAudioBtn.style.display = 'flex';
    }
    if (statusEl) {
        statusEl.textContent = '🔵 Click "Enable Audio" to start listening';
    }

    // Store audio element and context for later activation
    window.listenerAudio = audio;
    window.listenerMediaSource = mediaSource;

    // Initialize socket and join
    initSocket();
    socket.emit('join_listener');

    let hasHeader = false;

    socket.on('audio_data', (data) => {
        // We MUST have the header before we can do anything with broadcast chunks
        const isHeaderDirect = data instanceof ArrayBuffer && data.byteLength > 1000; // Heuristic

        hasHeader = true; // No header request needed for WebM relay

        chunksReceived++;
        audioQueue.push(data);

        // JITTER BUFFER: Reduced to 1 segments (buffered) for WebM/Opus
        const isHeader = false;

        if (sourceBuffer && !sourceBuffer.updating && mediaSource.readyState === 'open') {
            if (audioQueue.length >= 1) {
                try {
                    const next = audioQueue.shift();
                    sourceBuffer.appendBuffer(next);

                    // Periodic cleanup of old buffer data to prevent memory bloat
                    // Keep the last 60 seconds of audio data
                    if (audio.buffered.length > 0 && !sourceBuffer.updating) {
                        const end = audio.buffered.end(audio.buffered.length - 1);
                        const start = audio.buffered.start(0);
                        if (end - start > 120) { // If buffer is > 2 mins
                            sourceBuffer.remove(0, end - 60);
                        }
                    }

                    // Reset error counter on success
                    if (window.sourceBufferErrorCount) window.sourceBufferErrorCount = 0;
                } catch (e) {
                    console.error('Buffer append error:', e);
                    window.sourceBufferErrorCount = (window.sourceBufferErrorCount || 0) + 1;

                    if (window.sourceBufferErrorCount >= 5) {
                        console.error('❌ Too many SourceBuffer errors - attempting recovery...');
                        const statusEl = document.getElementById('connection-status');
                        if (statusEl) statusEl.textContent = '⚠️ Stream error - reconnecting...';
                        audioQueue = [];
                        chunksReceived = 0;
                        window.sourceBufferErrorCount = 0;
                        setTimeout(() => {
                            if (socket) socket.emit('request_header');
                        }, 1000);
                    }
                }
            }
        }

        // UI Update
        const now = Date.now();
        if (now - lastStatusUpdate > 1000) {
            const statusEl = document.getElementById('connection-status');
            if (statusEl) {
                statusEl.textContent = `🟢 Connected - ${chunksReceived} chunks (${audioQueue.length} buffered)`;
            }
            lastStatusUpdate = now;
        }
    });

    socket.on('broadcast_started', () => {
        const nowPlayingEl = document.getElementById('listener-now-playing');
        if (nowPlayingEl) nowPlayingEl.textContent = '🎵 Stream is live!';
        // Reset MediaSource for fresh stream if needed
    });

    socket.on('stream_status', (data) => {
        const nowPlayingEl = document.getElementById('listener-now-playing');
        if (nowPlayingEl) {
            nowPlayingEl.textContent = data.active ? '🎵 Stream is live!' : 'Stream offline - waiting for DJ...';
        }
    });

    socket.on('broadcast_stopped', () => {
        const nowPlayingEl = document.getElementById('listener-now-playing');
        if (nowPlayingEl) nowPlayingEl.textContent = 'Stream ended';
        chunksReceived = 0;
        audioQueue = [];
    });

    socket.on('connect', () => {
        const statusEl = document.getElementById('connection-status');
        if (statusEl) statusEl.textContent = '🟢 Connected';
    });

    socket.on('disconnect', () => {
        const statusEl = document.getElementById('connection-status');
        if (statusEl) statusEl.textContent = '🔴 Disconnected';
    });
}

// Enable audio for listener mode (called when user clicks the button)
async function enableListenerAudio() {
    console.log('🎧 Enabling audio via user gesture...');

    const enableAudioBtn = document.getElementById('enable-audio-btn');
    const statusEl = document.getElementById('connection-status');
    const audioText = enableAudioBtn ? enableAudioBtn.querySelector('.audio-text') : null;

    if (audioText) audioText.textContent = 'INITIALIZING...';

    try {
        // 1. Create AudioContext if it somehow doesn't exist
        if (!listenerAudioContext) {
            listenerAudioContext = new (window.AudioContext || window.webkitAudioContext)();
        }

        // 2. Resume audio context (CRITICAL for Chrome/Safari)
        if (listenerAudioContext.state === 'suspended') {
            await listenerAudioContext.resume();
            console.log('✅ Audio context resumed');
        }

        // 3. Kickstart the hidden audio element
        if (window.listenerAudio) {
            // Unmute just in case
            window.listenerAudio.muted = false;
            window.listenerAudio.volume = settings.volume || 0.8;

            // Attempt playback
            await window.listenerAudio.play();
            console.log('✅ Audio playback started successfully');
        }

        // 4. Hide the button and update status
        if (enableAudioBtn) {
            enableAudioBtn.style.opacity = '0';
            setTimeout(() => {
                enableAudioBtn.style.display = 'none';
            }, 300);
        }

        if (statusEl) {
            statusEl.textContent = '🟢 Audio Active - Enjoy the stream';
            statusEl.classList.add('glow-text');
        }

    } catch (error) {
        console.error('❌ Failed to enable audio:', error);
        const stashedStatus = document.getElementById('connection-status');
        const stashedBtn = document.getElementById('enable-audio-btn');
        const audioText = stashedBtn ? stashedBtn.querySelector('.audio-text') : null;

        if (audioText) audioText.textContent = 'RETRY ENABLE';
        if (stashedStatus) {
            stashedStatus.textContent = '⚠️ Browser blocked audio. Please click again.';
        }
    }
}

function setListenerVolume(value) {
    if (listenerGainNode) {
        listenerGainNode.gain.value = value / 100;
    }
}

// Load auto-start preference
window.addEventListener('load', () => {
    const autoStart = localStorage.getItem('autoStartStream');
    if (autoStart === 'true') {
        document.getElementById('auto-start-stream').checked = true;
        autoStartStream = true;
    }
});

// Monitoring
function monitorTrackEnd() {
    setInterval(() => {
        if (!audioCtx) return; // Safety check

        // In server-side mode, poll for status from server
        if (SERVER_SIDE_AUDIO && socket && socket.connected) {
            socket.emit('get_mixer_status');
        }

        ['A', 'B'].forEach(id => {
            if (decks[id].playing && decks[id].localBuffer && !decks[id].loading) {
                const playbackRate = decks[id].localSource ? decks[id].localSource.playbackRate.value : 1.0;
                const realElapsed = audioCtx.currentTime - decks[id].lastAnchorTime;
                const current = decks[id].lastAnchorPosition + (realElapsed * playbackRate);
                const remaining = decks[id].duration - current;

                // If end reached (with 0.5s buffer for safety)
                if (remaining <= 0.5) {
                    // Don't pause during broadcast - let the track end naturally
                    if (isBroadcasting) {
                        console.log(`🎙️ Track ending during broadcast on Deck ${id} - continuing stream`);
                        if (settings[`repeat${id}`]) {
                            console.log(`🔁 Repeating track on Deck ${id}`);
                            seekTo(id, 0);
                        }
                        // Skip pause/stop during broadcast to maintain stream
                        return;
                    }

                    if (settings[`repeat${id}`]) {
                        // Full song repeat
                        console.log(`🔁 Repeating track on Deck ${id}`);
                        seekTo(id, 0);
                    } else if (settings.autoPlay) {
                        // Prevent race condition
                        decks[id].loading = true;
                        pauseDeck(id);

                        // Check queue for auto-play
                        if (queues[id] && queues[id].length > 0) {
                            console.log(`📋 Auto-play: Loading next from Queue ${id}...`);
                            const next = queues[id].shift();
                            renderQueue(id); // Update queue UI

                            loadFromServer(id, next.file, next.title).then(() => {
                                decks[id].loading = false;
                                playDeck(id);
                            }).catch(() => {
                                decks[id].loading = false;
                            });
                        } else {
                            // No queue - just stop
                            console.log(`⏹️ Track ended, queue empty - stopping playback`);
                            decks[id].loading = false;
                            pauseDeck(id);
                            decks[id].pausedAt = 0;
                        }
                    } else {
                        // Just stop if no auto-play
                        pauseDeck(id);
                        decks[id].pausedAt = 0;
                    }
                }
            }
        });
    }, 500); // Check every 0.5s
}
monitorTrackEnd();
// Reset Deck to Default Settings
function resetDeck(id) {
    console.log(`🔄 Resetting Deck ${id} to defaults...`);

    if (!audioCtx) {
        console.warn('AudioContext not initialized');
        return;
    }

    // Reset EQ to neutral (0dB gain)
    if (decks[id].filters) {
        if (decks[id].filters.low) decks[id].filters.low.gain.value = 0;
        if (decks[id].filters.mid) decks[id].filters.mid.gain.value = 0;
        if (decks[id].filters.high) decks[id].filters.high.gain.value = 0;

        // Reset UI sliders
        const eqSliders = document.querySelectorAll(`#deck-${id} .eq-band input`);
        eqSliders.forEach(slider => slider.value = 0); // 0 = neutral for -20/20 range
    }

    // Reset filters to fully open
    if (decks[id].filters.lp) decks[id].filters.lp.frequency.value = 22050;
    if (decks[id].filters.hp) decks[id].filters.hp.frequency.value = 0;

    // Reset UI filter sliders
    const lpSlider = document.querySelector(`#deck-${id} .filter-lp`);
    const hpSlider = document.querySelector(`#deck-${id} .filter-hp`);
    if (lpSlider) lpSlider.value = 100;
    if (hpSlider) hpSlider.value = 0;

    // Reset volume to 80%
    if (decks[id].volumeGain) {
        decks[id].volumeGain.gain.value = 0.8;
    }
    const volumeSlider = document.querySelector(`#deck-${id} .volume-fader`);
    if (volumeSlider) volumeSlider.value = 80;

    // Reset pitch/speed to 1.0 (100%)
    const speedSlider = document.querySelector(`#deck-${id} .speed-slider`);
    if (speedSlider) {
        speedSlider.value = 1.0;
        // Use changeSpeed function to properly update playback rate
        changeSpeed(id, 1.0);
    }

    // Clear all hot cues
    decks[id].cues = {};
    const cueBtns = document.querySelectorAll(`#deck-${id} .cue-btn`);
    cueBtns.forEach(btn => {
        btn.classList.remove('cue-set');
        btn.style.animation = '';
    });

    // Clear loops
    decks[id].loopStart = null;
    decks[id].loopEnd = null;
    decks[id].loopActive = false;

    const loopBtns = document.querySelectorAll(`#deck-${id} .loop-controls button`);
    loopBtns.forEach(btn => {
        btn.style.background = '';
        btn.style.borderColor = '';
    });

    // Redraw waveform to clear cue/loop markers
    drawWaveform(id);

    console.log(`✅ Deck ${id} reset complete!`);

    // Visual feedback
    const resetBtn = document.querySelector(`#deck-${id} .reset-btn`);
    if (resetBtn) {
        resetBtn.style.transform = 'rotate(360deg)';
        resetBtn.style.transition = 'transform 0.5s ease';
        setTimeout(() => {
            resetBtn.style.transform = '';
        }, 500);
    }
}
// ==========================================
// QUEUE SYSTEM
// ==========================================

// Add track to queue
function addToQueue(deckId, file, title) {
    queues[deckId].push({ file, title });
    renderQueue(deckId);
    console.log(`📋 Added "${title}" to Queue ${deckId} (${queues[deckId].length} tracks)`);

    // Sync with server if in server-side mode
    if (SERVER_SIDE_AUDIO && socket) {
        socket.emit('audio_sync_queue', { deck: deckId, tracks: queues[deckId] });
    }
}

// Remove track from queue
function removeFromQueue(deckId, index) {
    const removed = queues[deckId].splice(index, 1)[0];
    renderQueue(deckId);
    console.log(`🗑️ Removed "${removed.title}" from Queue ${deckId}`);

    // Sync with server if in server-side mode
    if (SERVER_SIDE_AUDIO && socket) {
        socket.emit('audio_sync_queue', { deck: deckId, tracks: queues[deckId] });
    }
}

// Clear entire queue
function clearQueue(deckId) {
    const count = queues[deckId].length;
    queues[deckId] = [];
    renderQueue(deckId);
    console.log(`🗑️ Cleared Queue ${deckId} (${count} tracks removed)`);

    // Sync with server if in server-side mode
    if (SERVER_SIDE_AUDIO && socket) {
        socket.emit('audio_sync_queue', { deck: deckId, tracks: queues[deckId] });
    }
}

// Load next track from queue
function loadNextFromQueue(deckId) {
    if (queues[deckId].length === 0) {
        console.log(`📋 Queue ${deckId} is empty`);
        return false;
    }

    const next = queues[deckId].shift();
    console.log(`📋 Loading next from Queue ${deckId}: "${next.title}"`);
    loadFromServer(deckId, next.file, next.title);
    renderQueue(deckId);

    // Sync with server if in server-side mode (after shift)
    if (SERVER_SIDE_AUDIO && socket) {
        socket.emit('audio_sync_queue', { deck: deckId, tracks: queues[deckId] });
    }

    return true;
}

// Render queue UI
function renderQueue(deckId) {
    const queueList = document.getElementById(`queue-list-${deckId}`);
    if (!queueList) return;

    if (queues[deckId].length === 0) {
        queueList.innerHTML = '<div class="queue-empty">Drop tracks here or click "Queue to ' + deckId + '" in library</div>';
        return;
    }

    queueList.innerHTML = '';
    queues[deckId].forEach((track, index) => {
        const item = document.createElement('div');
        item.className = 'queue-item';
        item.draggable = true;

        const number = document.createElement('span');
        number.className = 'queue-number';
        number.textContent = (index + 1) + '.';

        const title = document.createElement('span');
        title.className = 'queue-track-title';
        title.textContent = track.title;

        const actions = document.createElement('div');
        actions.className = 'queue-actions';

        const loadBtn = document.createElement('button');
        loadBtn.className = 'queue-load-btn';
        loadBtn.textContent = '▶';
        loadBtn.title = 'Load now';
        loadBtn.onclick = () => {
            loadFromServer(deckId, track.file, track.title);
            removeFromQueue(deckId, index);
        };

        const removeBtn = document.createElement('button');
        removeBtn.className = 'queue-remove-btn';
        removeBtn.textContent = '✕';
        removeBtn.title = 'Remove from queue';
        removeBtn.onclick = () => removeFromQueue(deckId, index);

        actions.appendChild(loadBtn);
        actions.appendChild(removeBtn);

        item.appendChild(number);
        item.appendChild(title);
        item.appendChild(actions);

        // Drag and drop reordering
        item.ondragstart = (e) => {
            e.dataTransfer.effectAllowed = 'move';
            e.dataTransfer.setData('queueIndex', index);
            e.dataTransfer.setData('queueDeck', deckId);
            item.classList.add('dragging');
        };

        item.ondragend = () => {
            item.classList.remove('dragging');
        };

        item.ondragover = (e) => {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
        };

        item.ondrop = (e) => {
            e.preventDefault();
            const fromIndex = parseInt(e.dataTransfer.getData('queueIndex'));
            const fromDeck = e.dataTransfer.getData('queueDeck');

            if (fromDeck === deckId && fromIndex !== index) {
                const [moved] = queues[deckId].splice(fromIndex, 1);
                queues[deckId].splice(index, 0, moved);
                renderQueue(deckId);
                console.log(`🔄 Reordered Queue ${deckId}`);
            }
        };

        queueList.appendChild(item);
    });
}

// Auto-load next track when current track ends
function checkAndLoadNextFromQueue(deckId) {
    if (settings.autoPlay && queues[deckId].length > 0) {
        console.log(`🎵 Auto-loading next track from Queue ${deckId}...`);
        setTimeout(() => {
            loadNextFromQueue(deckId);
        }, 500);
    }
}
// ==========================================
// KEYBOARD SHORTCUTS SYSTEM
// ==========================================

// Default keyboard mappings (can be customized)
let keyboardMappings = {
    // Deck A Controls
    'q': { action: 'playDeckA', label: 'Play Deck A' },
    'a': { action: 'pauseDeckA', label: 'Pause Deck A' },
    '1': { action: 'cueA1', label: 'Deck A Cue 1' },
    '2': { action: 'cueA2', label: 'Deck A Cue 2' },
    '3': { action: 'cueA3', label: 'Deck A Cue 3' },
    '4': { action: 'cueA4', label: 'Deck A Cue 4' },
    'z': { action: 'loopInA', label: 'Loop IN Deck A' },
    'x': { action: 'loopOutA', label: 'Loop OUT Deck A' },
    'c': { action: 'loopExitA', label: 'Loop EXIT Deck A' },
    'r': { action: 'resetDeckA', label: 'Reset Deck A' },

    // Deck B Controls
    'w': { action: 'playDeckB', label: 'Play Deck B' },
    's': { action: 'pauseDeckB', label: 'Pause Deck B' },
    '7': { action: 'cueB1', label: 'Deck B Cue 1' },
    '8': { action: 'cueB2', label: 'Deck B Cue 2' },
    '9': { action: 'cueB3', label: 'Deck B Cue 3' },
    '0': { action: 'cueB4', label: 'Deck B Cue 4' },
    'n': { action: 'loopInB', label: 'Loop IN Deck B' },
    'm': { action: 'loopOutB', label: 'Loop OUT Deck B' },
    ',': { action: 'loopExitB', label: 'Loop EXIT Deck B' },
    't': { action: 'resetDeckB', label: 'Reset Deck B' },

    // Crossfader & Mixer
    'ArrowLeft': { action: 'crossfaderLeft', label: 'Crossfader Left' },
    'ArrowRight': { action: 'crossfaderRight', label: 'Crossfader Right' },
    'ArrowDown': { action: 'crossfaderCenter', label: 'Crossfader Center' },

    // Pitch Bend
    'e': { action: 'pitchBendAUp', label: 'Pitch Bend A +' },
    'd': { action: 'pitchBendADown', label: 'Pitch Bend A -' },
    'i': { action: 'pitchBendBUp', label: 'Pitch Bend B +' },
    'k': { action: 'pitchBendBDown', label: 'Pitch Bend B -' },

    // Utility
    'f': { action: 'toggleLibrary', label: 'Toggle Library' },
    'b': { action: 'toggleBroadcast', label: 'Toggle Broadcast' },
    'h': { action: 'showKeyboardHelp', label: 'Show Keyboard Help' },
    'Escape': { action: 'closeAllPanels', label: 'Close All Panels' }
};

// Load custom mappings from localStorage
function loadKeyboardMappings() {
    const saved = localStorage.getItem('keyboardMappings');
    if (saved) {
        try {
            keyboardMappings = JSON.parse(saved);
            console.log('✅ Loaded custom keyboard mappings');
        } catch (e) {
            console.error('Failed to load keyboard mappings:', e);
        }
    }
}

// Save custom mappings to localStorage
function saveKeyboardMappings() {
    localStorage.setItem('keyboardMappings', JSON.stringify(keyboardMappings));
    console.log('💾 Saved keyboard mappings');
}

// Execute action based on key
function executeKeyboardAction(action) {
    const actions = {
        // Deck A
        'playDeckA': () => playDeck('A'),
        'pauseDeckA': () => pauseDeck('A'),
        'cueA1': () => handleCue('A', 1),
        'cueA2': () => handleCue('A', 2),
        'cueA3': () => handleCue('A', 3),
        'cueA4': () => handleCue('A', 4),
        'loopInA': () => setLoop('A', 'in'),
        'loopOutA': () => setLoop('A', 'out'),
        'loopExitA': () => setLoop('A', 'exit'),
        'resetDeckA': () => resetDeck('A'),

        // Deck B
        'playDeckB': () => playDeck('B'),
        'pauseDeckB': () => pauseDeck('B'),
        'cueB1': () => handleCue('B', 1),
        'cueB2': () => handleCue('B', 2),
        'cueB3': () => handleCue('B', 3),
        'cueB4': () => handleCue('B', 4),
        'loopInB': () => setLoop('B', 'in'),
        'loopOutB': () => setLoop('B', 'out'),
        'loopExitB': () => setLoop('B', 'exit'),
        'resetDeckB': () => resetDeck('B'),

        // Crossfader
        'crossfaderLeft': () => {
            const cf = document.getElementById('crossfader');
            cf.value = Math.max(0, parseInt(cf.value) - 10);
            updateCrossfader(cf.value);
        },
        'crossfaderRight': () => {
            const cf = document.getElementById('crossfader');
            cf.value = Math.min(100, parseInt(cf.value) + 10);
            updateCrossfader(cf.value);
        },
        'crossfaderCenter': () => {
            const cf = document.getElementById('crossfader');
            cf.value = 50;
            updateCrossfader(50);
        },

        // Pitch Bend
        'pitchBendAUp': () => pitchBend('A', 0.05),
        'pitchBendADown': () => pitchBend('A', -0.05),
        'pitchBendBUp': () => pitchBend('B', 0.05),
        'pitchBendBDown': () => pitchBend('B', -0.05),

        // Utility
        'toggleLibrary': () => switchTab('library'),
        'toggleBroadcast': () => toggleBroadcast(),
        'showKeyboardHelp': () => openKeyboardSettings(),
        'closeAllPanels': () => {
            document.getElementById('settings-panel').classList.remove('active');
            document.getElementById('streaming-panel').classList.remove('active');
        }
    };

    if (actions[action]) {
        actions[action]();
    } else {
        console.warn('Unknown action:', action);
    }
}

// Global keyboard event listener
document.addEventListener('keydown', (e) => {
    // Ignore if typing in input field
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
        return;
    }

    const key = e.key;
    const mapping = keyboardMappings[key];

    if (mapping) {
        e.preventDefault();
        console.log(`⌨️ Keyboard: ${key} → ${mapping.label}`);
        executeKeyboardAction(mapping.action);
    }
});

// Handle pitch bend release
document.addEventListener('keyup', (e) => {
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;

    const key = e.key;
    const mapping = keyboardMappings[key];

    // Release pitch bend on key up
    if (mapping && mapping.action.includes('pitchBend')) {
        const deck = mapping.action.includes('A') ? 'A' : 'B';
        pitchBend(deck, 0);
    }
});

// Open keyboard settings panel
function openKeyboardSettings() {
    const panel = document.getElementById('keyboard-settings-panel');
    if (panel) {
        panel.classList.toggle('active');
    } else {
        createKeyboardSettingsPanel();
    }
}

// Create keyboard settings UI
function createKeyboardSettingsPanel() {
    const panel = document.createElement('div');
    panel.id = 'keyboard-settings-panel';
    panel.className = 'settings-panel active';
    panel.innerHTML = `
        <div class="panel-header">
            <h2>⌨️ Keyboard Shortcuts</h2>
            <button onclick="closeKeyboardSettings()">✕</button>
        </div>
        <div class="panel-content">
            <p style="margin-bottom: 15px; color: #888;">Click on a key to reassign it. Press ESC to cancel.</p>
            <div id="keyboard-mappings-list"></div>
            <div style="margin-top: 20px; display: flex; gap: 10px;">
                <button onclick="resetKeyboardMappings()" class="btn-secondary">Reset to Defaults</button>
                <button onclick="exportKeyboardMappings()" class="btn-secondary">Export</button>
                <button onclick="importKeyboardMappings()" class="btn-secondary">Import</button>
            </div>
        </div>
    `;
    document.body.appendChild(panel);
    renderKeyboardMappings();
}

function closeKeyboardSettings() {
    const panel = document.getElementById('keyboard-settings-panel');
    if (panel) panel.classList.remove('active');
}

// Render keyboard mappings list
function renderKeyboardMappings() {
    const list = document.getElementById('keyboard-mappings-list');
    if (!list) return;

    list.innerHTML = '';

    Object.entries(keyboardMappings).forEach(([key, mapping]) => {
        const item = document.createElement('div');
        item.className = 'keyboard-mapping-item';
        item.innerHTML = `
            <span class="key-display">${formatKeyName(key)}</span>
            <span class="key-arrow">→</span>
            <span class="action-label">${mapping.label}</span>
            <button class="key-reassign-btn" onclick="reassignKey('${key}')">Change</button>
        `;
        list.appendChild(item);
    });
}

// Format key name for display
function formatKeyName(key) {
    const names = {
        'ArrowLeft': '← Left',
        'ArrowRight': '→ Right',
        'ArrowUp': '↑ Up',
        'ArrowDown': '↓ Down',
        'Escape': 'ESC',
        ' ': 'Space'
    };
    return names[key] || key.toUpperCase();
}

// Reassign a key
function reassignKey(oldKey) {
    const item = event.target.closest('.keyboard-mapping-item');
    item.classList.add('listening');
    item.querySelector('.key-reassign-btn').textContent = 'Press new key...';

    const listener = (e) => {
        e.preventDefault();

        if (e.key === 'Escape') {
            item.classList.remove('listening');
            item.querySelector('.key-reassign-btn').textContent = 'Change';
            document.removeEventListener('keydown', listener);
            return;
        }

        const newKey = e.key;

        // Check if key already assigned
        if (keyboardMappings[newKey] && newKey !== oldKey) {
            if (!confirm(`Key "${formatKeyName(newKey)}" is already assigned to "${keyboardMappings[newKey].label}". Overwrite?`)) {
                item.classList.remove('listening');
                item.querySelector('.key-reassign-btn').textContent = 'Change';
                document.removeEventListener('keydown', listener);
                return;
            }
            delete keyboardMappings[newKey];
        }

        // Move mapping to new key
        keyboardMappings[newKey] = keyboardMappings[oldKey];
        delete keyboardMappings[oldKey];

        saveKeyboardMappings();
        renderKeyboardMappings();
        document.removeEventListener('keydown', listener);

        console.log(`✅ Remapped: ${formatKeyName(oldKey)} → ${formatKeyName(newKey)}`);
    };

    document.addEventListener('keydown', listener);
}

// Reset to default mappings
function resetKeyboardMappings() {
    if (confirm('Reset all keyboard shortcuts to defaults?')) {
        localStorage.removeItem('keyboardMappings');
        location.reload();
    }
}

// Export mappings
function exportKeyboardMappings() {
    const json = JSON.stringify(keyboardMappings, null, 2);
    const blob = new Blob([json], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'techdj-keyboard-mappings.json';
    a.click();
    URL.revokeObjectURL(url);
}

// Import mappings
function importKeyboardMappings() {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'application/json';
    input.onchange = (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.onload = (event) => {
            try {
                const imported = JSON.parse(event.target.result);
                keyboardMappings = imported;
                saveKeyboardMappings();
                renderKeyboardMappings();
                alert('✅ Keyboard mappings imported successfully!');
            } catch (err) {
                alert('❌ Failed to import: Invalid file format');
            }
        };
        reader.readAsText(file);
    };
    input.click();
}

// Initialize on load
loadKeyboardMappings();
console.log('⌨️ Keyboard shortcuts enabled. Press H for help.');
