/* =========================================
   DOM ACCESS NODES
========================================= */
const darkModeToggle = document.getElementById("darkModeToggle");
const canvas = document.getElementById("visualization-canvas");
const playPauseBtn = document.getElementById("playPauseBtn");
const streamStatus = document.getElementById("streamStatus");
const streamStatusContainer = document.getElementById("streamStatusContainer");
const showTitle = document.getElementById("showTitle");
const showHost = document.getElementById("showHost");
const coverImage = document.getElementById("coverImage");
const showCover = document.querySelector('.show-cover');
const vizToggle = document.getElementById("vizToggle");

// Dual Hardware Components
const audioA = document.getElementById("audioA");
const audioB = document.getElementById("audioB");

let currentAudio = audioA;
let nextAudio = audioB;

/* =========================================
   CORE CONFIGURATION ENGINE CONSTANTS
========================================= */
const CROSSFADE_DURATION = 3000; 
const FADE_INTERVAL = 50; 
const FADE_STEP = FADE_INTERVAL / CROSSFADE_DURATION;

const audioSources = [
    { url: 'https://stream.sport45.us/vibe?.mp3',  duration: 480 * 60 * 1000, title: "AFROMIX RADIO", cover: "https://", stationInfoLink: "https://afromixradio.com" },
    { url: 'https://stream.sport45.us/vibe#t=106.187755,Infinity', duration: 480 * 60 * 1000, title: "AFROMIX RADIO",  cover: "https://", stationInfoLink: "https://afromixradio.com" },
    { url: 'https://stream.sport45.us/vibe#t=106.187755,Infinity', duration: 480 * 60 * 1000, title: "AFROMIX RADIO",  cover: "https://", stationInfoLink: "https://afromixradio.com" }
];

/* =========================================
   APPLICATION MEMORY POOLS
========================================= */
let currentIndex = 0;
let isPlaying = false;
let visualizationEnabled = true;

let syncCheckInterval = null;
let crossfadeInterval = null;
let trackStartTime = 0; 

let scene, camera, renderer, particles;
let audioContext = null, analyser = null, dataArray = null;
let sourceNodeA = null, sourceNodeB = null;

// Modern Web Icons (FontAwesome Mappings)
const playIconHTML = '<i class="fa-solid fa-play"></i>';
const stopIconHTML = '<i class="fa-solid fa-square"></i>';

/* =========================================
   AUDIO CONTROL DRIVERS
========================================= */
function clearAllTimersAndTransitions() {
    clearInterval(syncCheckInterval);
    clearInterval(crossfadeInterval);
    syncCheckInterval = null;
    crossfadeInterval = null;
}

async function playAudio() {
    isPlaying = true;
    clearAllTimersAndTransitions();
    initAudioAnalyser();

    if (audioContext && audioContext.state === 'suspended') {
        await audioContext.resume();
    }

    if (!currentAudio.src || currentAudio.src === window.location.href) {
        currentAudio.src = audioSources[currentIndex].url;
    }
    
    currentAudio.volume = 1;
    currentAudio.play().catch(err => console.warn("Audio stream resolution blocked: ", err));

    playPauseBtn.innerHTML = stopIconHTML;
    streamStatusContainer.classList.add('online');
    addPulsingOverlay();

    const track = audioSources[currentIndex];
    updateNowPlaying(track.title, track.host, "Online", track.cover, track.stationInfoLink);
    
    trackStartTime = Date.now();
    startSyncTracker();
}

function pauseAudio() {
    isPlaying = false;
    clearAllTimersAndTransitions();

    currentAudio.pause();
    nextAudio.pause();

    playPauseBtn.innerHTML = playIconHTML;
    streamStatusContainer.classList.remove('online');
    removePulsingOverlay();

    streamStatus.innerText = 'Stream: Not playing';
    document.title = "Stream - Stopped";
    
    if (navigator.mediaSession) {
        navigator.mediaSession.metadata = null;
    }
}

function hardStop() {
    isPlaying = false;
    clearAllTimersAndTransitions();
    
    [audioA, audioB].forEach(audioNode => {
        if (!audioNode) return;
        audioNode.pause();
        audioNode.volume = 0;
        audioNode.src = "";
        audioNode.load(); 
    });

    playPauseBtn.innerHTML = playIconHTML;
    streamStatusContainer.classList.remove('online');
    removePulsingOverlay();
    streamStatus.innerText = 'Status: StreamStopped';
    document.title = "Stream - Not Playing";
}

/* =========================================
   STREAM SCHEDULERS & ATOMIC TRANSITIONS
========================================= */
function startSyncTracker() {
    clearInterval(syncCheckInterval);

    syncCheckInterval = setInterval(() => {
        if (!isPlaying) {
            clearAllTimersAndTransitions();
            return;
        }

        const currentTrack = audioSources[currentIndex];
        const elapsedTime = Date.now() - trackStartTime;
        const remainingTime = currentTrack.duration - elapsedTime;

        if (remainingTime <= CROSSFADE_DURATION) {
            clearInterval(syncCheckInterval); 
            
            const nextIndex = (currentIndex + 1) % audioSources.length;
            const nextTrack = audioSources[nextIndex];
            
            triggerCrossfade(nextIndex, nextTrack);
        }
    }, 1000); 
}

function triggerCrossfade(nextIndex, nextTrack) {
    if (!isPlaying) return; 

    nextAudio.src = nextTrack.url;
    nextAudio.volume = 0;
    nextAudio.play().catch(err => console.warn("Crossfade playback deferred: ", err));

    clearInterval(crossfadeInterval);
    
    crossfadeInterval = setInterval(() => {
        if (!isPlaying) {
            clearAllTimersAndTransitions();
            return;
        }

        let currentVol = Math.max(0, currentAudio.volume - FADE_STEP);
        let nextVol = Math.min(1, nextAudio.volume + FADE_STEP);

        currentAudio.volume = currentVol;
        nextAudio.volume = nextVol;

        if (currentVol <= 0 && nextVol >= 1) {
            clearInterval(crossfadeInterval);
            
            currentAudio.pause();
            currentAudio.src = ""; 
            
            const temp = currentAudio;
            currentAudio = nextAudio;
            nextAudio = temp;

            currentIndex = nextIndex;
            trackStartTime = Date.now(); 
            
            updateNowPlaying(nextTrack.title, nextTrack.host, "Online", nextTrack.cover, nextTrack.stationInfoLink);
            startSyncTracker();
        }
    }, FADE_INTERVAL);
}

/* =========================================
   WEB AUDIO API ANALYSER PIPELINE
========================================= */
function initAudioAnalyser() {
    if (audioContext) return;

    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    analyser = audioContext.createAnalyser();
    analyser.fftSize = 512;
    dataArray = new Uint8Array(analyser.frequencyBinCount);

    audioA.crossOrigin = "anonymous";
    audioB.crossOrigin = "anonymous";

    sourceNodeA = audioContext.createMediaElementSource(audioA);
    sourceNodeB = audioContext.createMediaElementSource(audioB);

    sourceNodeA.connect(analyser);
    sourceNodeB.connect(analyser);

    analyser.connect(audioContext.destination);
}

/* =========================================
   THREE.JS AMBIENT LIGHTING PARTICLE WORLD
   color mode: pulse | hue: 60° sat: 100% bri: 65% opa: 40%
========================================= */
function initThreeJS() {
    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 120;

    renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

    const particleCount = 4000;
    const geometry = new THREE.BufferGeometry();
    const positions = new Float32Array(particleCount * 3);

    for (let i = 0; i < positions.length; i++) {
        positions[i] = (Math.random() - 0.5) * 300;
    }

    geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));

    const material = new THREE.PointsMaterial({
        size: 2.0,
        color: 0xffff4d,
        transparent: true,
        opacity: 0.400,
        blending: THREE.AdditiveBlending,
        depthWrite: false
    });

    particles = new THREE.Points(geometry, material);
    scene.add(particles);
}


/* =========================================
   ANIMATION & AUDIO PULSE THREADS
   mode: pulse
========================================= */
function animate() {
    requestAnimationFrame(animate);

    if (!visualizationEnabled) {
        particles.rotation.y += 0.0002;
        renderer.render(scene, camera);
        return;
    }

    if (analyser && isPlaying) {
        analyser.getByteFrequencyData(dataArray);

        let bass = 0, treble = 0;
        const half = dataArray.length / 2;
        for (let i = 0; i < half; i++) { bass += dataArray[i]; }
        for (let i = half; i < dataArray.length; i++) { treble += dataArray[i]; }
        bass /= half; treble /= half;

        const scale = 1 + (bass / 255) * 1.5;
        particles.scale.set(scale, scale, scale);
        particles.rotation.y += 0.001 + treble / 2550;
        particles.rotation.x += 0.0005;

        // HSL pulse — brightness and saturation react to bass/treble
        const pulseBri = 0.650 * (0.7 + (bass / 255) * 0.6);
        const pulseSat = Math.min(1, 1.000 * (0.8 + (treble / 255) * 0.4));
        particles.material.color.setHSL(0.1667, pulseSat, pulseBri);
        particles.material.opacity = Math.min(1, 0.400 * (0.5 + (bass / 255) * 1.2));
    } else {
        particles.rotation.y += 0.0006;
        particles.material.color.setHSL(0.1667, 1.000, 0.650);
        particles.material.opacity = 0.400;
    }

    renderer.render(scene, camera);
}

/* =========================================
   METADATA INFRASTRUCTURE INTERFACES
========================================= */
function updateNowPlaying(title, host, status, cover, stationInfoLink) {
    showTitle.innerText = title;
    showHost.innerText = `Hosted By ${host}`;
    streamStatus.innerText = `Status: ${status}`;
    if (coverImage) coverImage.src = cover;

    document.title = `${status} | ${title}`;

    if (navigator.mediaSession) {
        navigator.mediaSession.metadata = new MediaMetadata({
            title:title,
            artist:host,
            album: "XAT45 RADIO",
            artwork: [{ src: cover, sizes: '300x300', type: 'image/png' }]
        });

        navigator.mediaSession.setActionHandler('play', playAudio);
        navigator.mediaSession.setActionHandler('pause', pauseAudio);
        navigator.mediaSession.setActionHandler('stop', hardStop);
        navigator.mediaSession.setActionHandler('seekto', (event) => {
                window.open(stationInfoLink, "_blank");
            });
    }
}

function addPulsingOverlay() {
    if (!showCover || document.getElementById('pulsingOverlay')) return;
    const overlay = document.createElement('div');
    overlay.classList.add('pulsing-overlay');
    overlay.id = 'pulsingOverlay';
    showCover.appendChild(overlay);
}

function removePulsingOverlay() {
    const overlay = document.getElementById('pulsingOverlay');
    if (overlay) overlay.remove();
}

/* =========================================
   GLOBAL SYSTEM EVENT BINDINGS
========================================= */
playPauseBtn.addEventListener("click", () => {
    if (isPlaying) {
        pauseAudio();
    } else {
        playAudio();
    }
});

window.addEventListener("resize", () => {
    if (!camera || !renderer) return;
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});

if (vizToggle) {
    vizToggle.addEventListener("change", () => {
        visualizationEnabled = vizToggle.checked;
        if (particles) particles.visible = vizToggle.checked;
    });
}

function disableViz() {
    if (vizToggle) vizToggle.checked = false;
    visualizationEnabled = false;
    if (particles) particles.visible = false;
}

/* =========================================
   SYSTEM RUN-LEVEL INITIALIZATION
========================================= */
initThreeJS();
animate();
