// --- Sound Effects Synthesizer (Web Audio API) ---
const VERSION = 'Equal-1.04';

class SlotAudio {
    constructor() {
        this.ctx = null;
        this.enabled = true;
    }

    init() {
        if (!this.ctx) {
            this.ctx = new (window.AudioContext || window.webkitAudioContext)();
        }
        if (this.ctx.state === 'suspended') {
            this.ctx.resume();
        }
    }

    playTick(frequency = 600, duration = 0.04, volume = 0.08) {
        if (!this.enabled) return;
        this.init();
        
        try {
            const osc = this.ctx.createOscillator();
            const gain = this.ctx.createGain();
            
            osc.type = 'triangle';
            osc.frequency.setValueAtTime(frequency, this.ctx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(80, this.ctx.currentTime + duration);
            
            gain.gain.setValueAtTime(volume, this.ctx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
            
            osc.connect(gain);
            gain.connect(this.ctx.destination);
            
            osc.start();
            osc.stop(this.ctx.currentTime + duration + 0.01);
        } catch (e) {
            console.warn("Audio error:", e);
        }
    }

    playLeverPull() {
        if (!this.enabled) return;
        this.init();
        
        try {
            // Mechanical clink
            const osc = this.ctx.createOscillator();
            const gain = this.ctx.createGain();
            
            osc.type = 'sawtooth';
            osc.frequency.setValueAtTime(120, this.ctx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(30, this.ctx.currentTime + 0.12);
            
            gain.gain.setValueAtTime(0.12, this.ctx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.12);
            
            osc.connect(gain);
            gain.connect(this.ctx.destination);
            
            osc.start();
            osc.stop(this.ctx.currentTime + 0.15);
        } catch (e) {
            console.warn(e);
        }
    }

    playSpinHum(duration = 2.0, volume = 0.02) {
        if (!this.enabled) return;
        this.init();
        
        try {
            const osc = this.ctx.createOscillator();
            const gain = this.ctx.createGain();
            
            osc.type = 'sine';
            osc.frequency.setValueAtTime(75, this.ctx.currentTime); // low hum
            // frequency modulation for vibrating sound
            const fm = this.ctx.createOscillator();
            const fmGain = this.ctx.createGain();
            fm.frequency.value = 12; // 12 Hz wobble
            fmGain.gain.value = 10;
            
            fm.connect(fmGain);
            fmGain.connect(osc.frequency);
            
            gain.gain.setValueAtTime(volume, this.ctx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
            
            osc.connect(gain);
            gain.connect(this.ctx.destination);
            
            fm.start();
            osc.start();
            
            fm.stop(this.ctx.currentTime + duration);
            osc.stop(this.ctx.currentTime + duration);
        } catch (e) {
            console.warn(e);
        }
    }

    playWinSound(volume = 0.15) {
        if (!this.enabled) return;
        this.init();
        
        try {
            const now = this.ctx.currentTime;
            
            const playNote = (freq, start, dur) => {
                const osc = this.ctx.createOscillator();
                const gain = this.ctx.createGain();
                
                osc.type = 'sine';
                osc.frequency.setValueAtTime(freq, start);
                
                // Add a small vibrato
                const vibrato = this.ctx.createOscillator();
                const vibratoGain = this.ctx.createGain();
                vibrato.frequency.value = 6;
                vibratoGain.gain.value = 4;
                vibrato.connect(vibratoGain);
                vibratoGain.connect(osc.frequency);
                
                gain.gain.setValueAtTime(volume, start);
                gain.gain.exponentialRampToValueAtTime(0.001, start + dur);
                
                osc.connect(gain);
                gain.connect(this.ctx.destination);
                
                vibrato.start(start);
                osc.start(start);
                
                vibrato.stop(start + dur);
                osc.stop(start + dur);
            };
            
            // Triumphant arpeggio
            playNote(261.63, now, 0.15);        // C4
            playNote(329.63, now + 0.12, 0.15); // E4
            playNote(392.00, now + 0.24, 0.15); // G4
            playNote(523.25, now + 0.36, 0.2);  // C5
            playNote(659.25, now + 0.48, 0.2);  // E5
            playNote(783.99, now + 0.6, 0.2);   // G5
            playNote(1046.50, now + 0.72, 0.6);  // C6 (Tonic chord peak)
            playNote(1318.51, now + 0.72, 0.6);  // E6 (Harmonic third)
        } catch (e) {
            console.warn(e);
        }
    }
}

const soundManager = new SlotAudio();

// --- Confetti Particle System ---
class ConfettiEngine {
    constructor(canvasId) {
        this.canvas = document.getElementById(canvasId);
        this.ctx = this.canvas.getContext('2d');
        this.particles = [];
        this.active = false;
        this.colors = ['#ff007f', '#00f2fe', '#ffe600', '#39ff14', '#ffffff', '#e94560', '#ff8e53'];
        
        window.addEventListener('resize', () => {
            if (this.active) this.resizeCanvas();
        });
    }

    resizeCanvas() {
        const rect = this.canvas.parentElement.getBoundingClientRect();
        this.canvas.width = rect.width;
        this.canvas.height = rect.height;
    }

    start() {
        this.active = true;
        this.resizeCanvas();
        this.particles = [];
        
        // Spawn 150 confetti items
        for (let i = 0; i < 150; i++) {
            this.particles.push({
                x: this.canvas.width / 2,
                y: this.canvas.height / 2 + 50,
                vx: (Math.random() - 0.5) * 12,
                vy: (Math.random() - 0.8) * 16 - 5, // shoot upwards
                size: Math.random() * 8 + 6,
                color: this.colors[Math.floor(Math.random() * this.colors.length)],
                rotation: Math.random() * 360,
                rotationSpeed: (Math.random() - 0.5) * 10,
                opacity: 1,
                decay: Math.random() * 0.01 + 0.005
            });
        }
        
        this.loop();
    }

    stop() {
        this.active = false;
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }

    loop() {
        if (!this.active) return;
        
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        let alive = false;
        this.particles.forEach(p => {
            if (p.opacity <= 0) return;
            alive = true;
            
            // Physics
            p.x += p.vx;
            p.y += p.vy;
            p.vy += 0.35; // gravity
            p.vx *= 0.98; // drag
            p.rotation += p.rotationSpeed;
            p.opacity -= p.decay;
            
            // Draw
            this.ctx.save();
            this.ctx.translate(p.x, p.y);
            this.ctx.rotate((p.rotation * Math.PI) / 180);
            this.ctx.fillStyle = p.color;
            this.ctx.globalAlpha = p.opacity;
            
            // draw diamond shape
            this.ctx.beginPath();
            this.ctx.moveTo(-p.size / 2, 0);
            this.ctx.lineTo(0, -p.size);
            this.ctx.lineTo(p.size / 2, 0);
            this.ctx.lineTo(0, p.size);
            this.ctx.closePath();
            this.ctx.fill();
            
            this.ctx.restore();
        });
        
        if (alive) {
            requestAnimationFrame(() => this.loop());
        }
    }
}

// --- Application Core Logic ---
const App = {
    // Default Preset lists
    presets: {
        numbers: Array.from({ length: 150 }, (_, i) => String(i + 1).padStart(3, '0')),
    },

    // Elements
    el: {
        reelsContainer: document.getElementById('reels-container'),
        btnSpin: document.getElementById('btn-spin'),
        winModal: document.getElementById('win-modal'),
        winnerLabel: document.getElementById('winner-label-text'),
        winnerName: document.getElementById('winner-name-text'),
        //btnModalClose: document.getElementById('btn-modal-close'),
        btnModalClose: document.getElementById('win-modal-content'),
        marqueeText: document.getElementById('marquee-text')
    },

    // State Variables
    state: {
        names: [],
        isSpinning: false,
        history: [],
        activeReelsData: [], // Store state for running reels
        itemHeight: 70,       // Matches CSS height of .reel-item (70px)
        viewportOffset: 90,   // Center alignment offset: (viewportHeight / 2) - (itemHeight / 2) -> (250 / 2) - (70 / 2) = 90
        reelCount: 1,
        spinDuration: 10.0,
        removeWinner: true,
        soundEnabled: true
    },

    init() {
        if(localStorage.getItem('slot-version') != VERSION) {
          this.clearLocalStorage();
        }

        this.confetti = new ConfettiEngine('confetti-canvas');
        this.loadLocalStorage();
        this.bindEvents();

        if (!this.state.names.length) {
            this.state.names = [...this.presets.numbers];
        }

        this.rebuildReels();
    },

    loadLocalStorage() {
        const soundPref = localStorage.getItem('slot-sound-enabled');
        this.state.soundEnabled = soundPref !== null ? soundPref === 'true' : true;
        soundManager.enabled = this.state.soundEnabled;

        const removePref = localStorage.getItem('slot-remove-winner');
        this.state.removeWinner = removePref !== null ? removePref === 'true' : true;

        const durationPref = localStorage.getItem('slot-spin-duration');
        let duration = 15.0;
        if (durationPref !== null) {
            const parsedDuration = parseFloat(durationPref);
            if (!isNaN(parsedDuration) && parsedDuration >= 10.0 && parsedDuration <= 30.0) {
                duration = parsedDuration;
            }
        }
        this.state.spinDuration = duration;

        const reelPref = localStorage.getItem('slot-reel-count');
        let count = parseInt(reelPref, 10);
        if (isNaN(count) || count < 1 || count > 3) {
            count = 1;
        }
        this.state.reelCount = count;

        const themePref = localStorage.getItem('slot-theme');
        if (themePref) {
            document.body.className = `theme-${themePref}`;
        }

        const savedNames = localStorage.getItem('slot-names');
        if (savedNames && savedNames.trim()) {

            this.state.names = savedNames
                .split('\n')
                .map(n => n.trim())
                .filter(n => n.length > 0);
        }

        const savedHistory = localStorage.getItem('slot-history');
        if (savedHistory) {
            try {
                this.state.history = JSON.parse(savedHistory);
            } catch (e) {
                this.state.history = [];
            }
        }

        this.updateHistoryUI();
    },

    saveLocalStorage() {
        localStorage.setItem('slot-sound-enabled', this.state.soundEnabled.toString());
        localStorage.setItem('slot-remove-winner', this.state.removeWinner.toString());
        localStorage.setItem('slot-spin-duration', this.state.spinDuration.toString());
        localStorage.setItem('slot-reel-count', this.state.reelCount.toString());
        localStorage.setItem('slot-names', this.state.names.join('\n'));
        localStorage.setItem('slot-history', JSON.stringify(this.state.history));
    },

    clearLocalStorage() {
        localStorage.removeItem('slot-sound-enabled');
        localStorage.removeItem('slot-remove-winner');
        localStorage.removeItem('slot-spin-duration');
        localStorage.removeItem('slot-reel-count');
        localStorage.removeItem('slot-names');
        localStorage.removeItem('slot-history');
        localStorage.setItem('slot-version', VERSION);
    },

    bindEvents() {
        if (this.el.btnSpin) {
            this.el.btnSpin.addEventListener('click', () => this.spin());
        }

        if (this.el.btnModalClose) {
            this.el.btnModalClose.addEventListener('click', () => {
                if (this.el.winModal) {
                    this.el.winModal.classList.remove('active');
                }
                this.confetti.stop();
                if (this.el.marqueeText) {
                    this.el.marqueeText.textContent = "LUCKY WINNER";
                    this.el.marqueeText.style.animation = "marqueePulse 1.5s infinite alternate";
                }
                soundManager.playTick(500, 0.04, 0.06);
            });
        }
    },

    rebuildReels() {
        this.el.reelsContainer.innerHTML = '';
        const numReels = this.state.reelCount;
        
        if (this.state.names.length < 2) {
            this.el.reelsContainer.innerHTML = `
                <div class="reel-placeholder">
                    <div class="placeholder-text">⚠️ Input at least 2 names to spin!</div>
                </div>
            `;
            this.el.btnSpin.disabled = true;
            return;
        }

        this.el.btnSpin.disabled = false;

        // Build HTML for each active reel
        for (let r = 0; r < numReels; r++) {
            const reelCol = document.createElement('div');
            reelCol.className = 'reel-column';
            
            const reelTape = document.createElement('div');
            reelTape.className = 'reel-tape';
            reelTape.id = `reel-tape-${r}`;
            
            // Construct a long list of items for spinning
            // We repeat the name array so we have enough tape length to spin.
            // Let's create an initial state tape (e.g., at least 25 items, repeating the names list).
            const repeatCount = Math.ceil(40 / this.state.names.length);
            let tapeItems = [];
            
            for (let c = 0; c < repeatCount; c++) {
                tapeItems = tapeItems.concat(this.state.names);
            }
            
            // Build elements inside tape
            tapeItems.forEach((name, idx) => {
                const item = document.createElement('div');
                item.className = 'reel-item';
                if (idx === 0) item.classList.add('active'); // Center item active initially
                item.textContent = name;
                reelTape.appendChild(item);
            });
            
            reelCol.appendChild(reelTape);
            this.el.reelsContainer.appendChild(reelCol);
            
            // Position initial reel tape so that first item is centered
            reelTape.style.transform = `translateY(${this.state.viewportOffset}px)`;
        }
    },

    spin() {
        if (this.state.isSpinning || this.state.names.length < 2) return;

        this.state.isSpinning = true;
        this.disableControls(true);
        this.el.marqueeText.textContent = "LUCKY WINNER";
        this.el.marqueeText.style.animation = "marqueePulse 0.4s infinite alternate";
        
        const numReels = this.state.reelCount;
        const durationSetting = this.state.spinDuration * 1000;
        
        // Play slot spin hum
        soundManager.playSpinHum(durationSetting / 1000 + 1.5, 0.3);
        
        this.state.activeReelsData = [];

        // Helper functions to enforce min distance of 3 between consecutive numbers
        const getNumericVal = (val) => {
            if (!val) return null;
            const cleaned = val.replace(/\D/g, '');
            if (!cleaned) return null;
            return parseInt(cleaned, 10);
        };

        const isCompatible = (val1, val2) => {
            const num1 = getNumericVal(val1);
            const num2 = getNumericVal(val2);
            if (num1 === null || num2 === null) return true;
            return Math.abs(num1 - num2) >= 3;
        };

        const getCompatibleCandidates = (valBefore, valAfter = null) => {
            return this.state.names.filter(name => {
                if (valBefore && !isCompatible(valBefore, name)) return false;
                if (valAfter && !isCompatible(name, valAfter)) return false;
                return true;
            });
        };

        const getRandomCompatibleCandidate = (valBefore, valAfter = null) => {
            const candidates = getCompatibleCandidates(valBefore, valAfter);
            if (candidates.length === 0) {
                return this.state.names[Math.floor(Math.random() * this.state.names.length)];
            }
            return candidates[Math.floor(Math.random() * candidates.length)];
        };
        
        // Setup details for each reel spin
        for (let r = 0; r < numReels; r++) {
            const tape = document.getElementById(`reel-tape-${r}`);
            tape.classList.add('blurring');
            
            // Determine winner index
            const winIdx = Math.floor(Math.random() * this.state.names.length);
            const winner = this.state.names[winIdx];
            
            // Stagger the reel stops: reel 0 stops at base duration, reel 1 stops 1.2s later, reel 2 stops 2.4s later
            const staggerDelay = r * 1200;
            const reelDuration = durationSetting + staggerDelay;
            
            // Construct the spin tape for this specific spin.
            // The tape needs:
            // 1. Initial names.
            // 2. Many scrambled names in between for the fast spinning part.
            // 3. The final landing block containing the winner.
            
            // Let's build a dedicated spin sequence array
            const finalSpinItems = [];
            
            // Grab previous contents of the reel tape to avoid visual jump, but only keep visible items (active + next 2)
            const items = Array.from(tape.children);
            const activeIdx = Math.max(0, items.findIndex(item => item.classList.contains('active')));
            
            for (let i = activeIdx; i < Math.min(items.length, activeIdx + 3); i++) {
                finalSpinItems.push(items[i].textContent);
            }
            
            // We want the reel to scroll over roughly 70 to 120 items depending on reel index (staggering)
            // Let's calculate exactly how many items we should scroll through
            //const itemsToScroll = 45 + r * 15;
            let numItem = parseInt(items.length);
            if(numItem <= 45) {
              numItem += 45;
            }
            const itemsToScroll = numItem * reelDuration / 10000;
            const targetIdx = itemsToScroll - 1;
            
            // Fill middle with scrambled names that are numeric-compatible (difference >= 3)
            for (let i = finalSpinItems.length; i < targetIdx; i++) {
                const valBefore = finalSpinItems[i - 1];
                const valAfter = (i === targetIdx - 1) ? winner : null;
                finalSpinItems[i] = getRandomCompatibleCandidate(valBefore, valAfter);
            }
            
            // The item we land on should be the winner.
            finalSpinItems[targetIdx] = winner;
            
            // Add padding items after target index (must also be compatible)
            finalSpinItems.push(getRandomCompatibleCandidate(winner));
            finalSpinItems.push(getRandomCompatibleCandidate(finalSpinItems[targetIdx + 1]));
            
            // Re-render the tape content
            tape.innerHTML = '';


            finalSpinItems.forEach(name => {
                const item = document.createElement('div');
                item.className = 'reel-item';
                item.textContent = name;
                tape.appendChild(item);
            });
            
            // Reel position states
            const startY = this.state.viewportOffset; // reset start position to top
            const targetY = this.state.viewportOffset - (targetIdx * this.state.itemHeight);
            
            this.state.activeReelsData.push({
                el: tape,
                startY: startY,
                targetY: targetY,
                duration: reelDuration,
                startTime: null,
                lastTickIdx: 0,
                winnerName: winner,
                targetIdx: targetIdx,
                finished: false
            });
        }
        
        // Start the physics loop
        requestAnimationFrame((timestamp) => this.updateSpin(timestamp));
    },

    updateSpin(timestamp) {
        let allFinished = true;
        const now = timestamp;
        
        this.state.activeReelsData.forEach((reel, idx) => {
            if (!reel.startTime) {
                reel.startTime = now;
            }
            
            const elapsed = now - reel.startTime;
            let progress = elapsed / reel.duration;
            if (progress > 1) progress = 1;
            
            if (progress < 1) {
                allFinished = false;
                
                // Smooth Ease-Out function: heavy deceleration and no rollback
                const p = this.easeOutCubic(progress);
                const currentY = reel.startY + (reel.targetY - reel.startY) * p;
                
                reel.el.style.transform = `translateY(${currentY}px)`;
                
                // Motion blur filter based on speed
                // Velocity is highest at start, goes to 0 at end.
                const speed = (1 - progress);
                const blurVal = speed * 8; // Max blur 8px
                reel.el.style.filter = `blur(${blurVal}px)`;
                
                // Sound Ticks: calculate which item is currently passing the payline
                const passingIdx = Math.round((this.state.viewportOffset - currentY) / this.state.itemHeight);
                if (passingIdx !== reel.lastTickIdx && passingIdx >= 0 && passingIdx <= reel.targetIdx) {
                    reel.lastTickIdx = passingIdx;
                    
                    // Pitch increases slightly as it decelerates, mimicking mechanical lock gears
                    const tickPitch = 400 + (progress * 300);
                    const tickVol = 0.08 * (1 - progress * 0.6); // Soften volume as it stops
                    
                    soundManager.playTick(tickPitch, 0.03, tickVol);
                    
                    // Set active class to items crossing line
                    Array.from(reel.el.children).forEach((child, cIdx) => {
                        child.classList.toggle('active', cIdx === passingIdx);
                    });
                }
            } else if (!reel.finished) {
                // Settle final position exactly on target
                reel.finished = true;
                reel.el.classList.remove('blurring');
                reel.el.style.transform = `translateY(${reel.targetY}px)`;
                reel.el.style.filter = 'none';
                
                // Final winner item activation highlight
                Array.from(reel.el.children).forEach((child, cIdx) => {
                    child.classList.toggle('active', cIdx === reel.targetIdx);
                });
                
                // Mechanical lock click sound
                soundManager.playTick(300, 0.1, 0.25);
            }
        });
        
        if (!allFinished) {
            requestAnimationFrame((t) => this.updateSpin(t));
        } else {
            this.handleSpinComplete();
        }
    },

    // Smooth ease-out cubic formula (no overshoot or rollback)
    easeOutCubic(x) {
        return 1 - Math.pow(1 - x, 3);
    },

    handleSpinComplete() {
        this.state.isSpinning = false;
        
        // Compile list of winning names
        const winners = this.state.activeReelsData.map(r => r.winnerName);
        
        // Play victory sounds
        soundManager.playWinSound();
        
        // Add to history
        const timestamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
        winners.forEach(winner => {
            this.state.history.unshift({ name: winner, time: timestamp });
        });
        
        // Limit history to 30 items
        if (this.state.history.length > 30) {
            this.state.history = this.state.history.slice(0, 30);
        }
        
        this.saveLocalStorage();
        this.updateHistoryUI();
        
        // Show Winner splash modal
        setTimeout(() => {
            if (winners.length === 1) {
                this.el.winnerLabel.textContent = "Congratulations!";
                this.el.winnerName.textContent = winners[0];
            } else {
                this.el.winnerLabel.textContent = `Lucky Winners! (${winners.length} Reels)`;
                this.el.winnerName.textContent = winners.join(' ✦ ');
            }
            
            this.el.winModal.classList.add('active');
            this.confetti.start();
            
            // Marquee flashing lights
            this.el.marqueeText.textContent = "JACKPOT!";
            this.el.marqueeText.style.animation = "marqueePulse 0.2s infinite alternate";
            
            // If "remove winner" checked, remove the winning names from the list
            if (this.state.removeWinner) {
                let updatedNames = [...this.state.names];
                winners.forEach(winner => {
                    const wIdx = updatedNames.indexOf(winner);
                    if (wIdx > -1) {
                        updatedNames.splice(wIdx, 1);
                    }
                });

                this.state.names = updatedNames;
                this.saveLocalStorage();
                this.rebuildReels();
            }

            this.disableControls(false);
        }, 600);
    },

    disableControls(disable) {
        this.el.btnSpin.disabled = disable;
    },

    updateHistoryUI() {
        const historyEl = document.getElementById('history-list');
        if (!historyEl) return;

        if (this.state.history.length === 0) {
            historyEl.innerHTML = '<div class="empty-history">No winners yet. Spin the reels!</div>';
            return;
        }
        
        historyEl.innerHTML = '';
        this.state.history.forEach(item => {
            const div = document.createElement('div');
            div.className = 'history-item';
            div.innerHTML = `
                <span class="history-name">${item.name}</span>
                <span class="history-time">${item.time}</span>
            `;
            historyEl.appendChild(div);
        });
    }
};

// Start the application
window.addEventListener('DOMContentLoaded', () => {
    App.init();
});

// Reload data when returning to this page from another page
window.addEventListener('pageshow', () => {
    if (App) {
        App.loadLocalStorage();
        App.rebuildReels();
        App.updateHistoryUI();
    }
});
