import { world, system } from "@minecraft/server";
import { fo } from './utilities/fo_constants.js';



export function initSoundDesign() {

    const
        forebodingScoreCheck3 = fo.scoreChapter3,
        forebodingScoreCheck4 = fo.scoreChapter4,
        forebodingScoreCheck5 = fo.scoreChapter5,
        forebodingScoreCheck7 = -10;

    
    const PYRAMID_INFLUENCE_PROPERTY = "pyramidInfluenceExposure";
    const influenceTime = 53200; 

    function saveExposureData(exposureData) {
        world.setDynamicProperty(PYRAMID_INFLUENCE_PROPERTY, JSON.stringify(exposureData));
    }

    
    system.runInterval(() => {

        let
            savedPyramidData = world.getDynamicProperty("mainPyramid"),
            pyramidData = savedPyramidData ? JSON.parse(savedPyramidData) : false;
        if (pyramidData.stage === "building" || pyramidData.stage === "complete") {
            const
                forebodingScore = fo.forebodingScore,
                overworld = fo.overworld,
                theEnd = fo.theEnd,
                pyramidCenter = { x: pyramidData.x, y: 82, z: pyramidData.z },
                monolithCenter = { x: 25992, y: 60, z: 25416 },
                voidVillageCenter = { x: 400512, y: 2, z: 400528 };

            overworld.playSound("forefeel", pyramidCenter, {
                volume: 32,
            });

            overworld.playSound("forefeel", monolithCenter, {
                volume: 32,
            });

            updatePyramidInfluence(overworld, theEnd, pyramidCenter, monolithCenter, voidVillageCenter, forebodingScore);
            system.runTimeout(() => { decayPyramidInfluence(overworld, theEnd, pyramidCenter, monolithCenter, voidVillageCenter, forebodingScore) }, 1);
        }

    }, 560) 

    
    system.runTimeout(() => {
        playWhispers();
    }, 572);

    function updatePyramidInfluence(overworld, theEnd, pyramidCenter, monolithCenter, voidVillageCenter, forebodingScore) {
        
        const exposureData = JSON.parse(world.getDynamicProperty(PYRAMID_INFLUENCE_PROPERTY) || "{}");

        if (forebodingScore < forebodingScoreCheck7) {
            const players = overworld.getPlayers();
            for (let player of players) {
                const playerName = player.name;

                if (!exposureData[playerName]) {
                    exposureData[playerName] = {
                        exposure: 0,
                        lastUpdate: Date.now()
                    };
                } else {
                    exposureData[playerName].exposure += 280;
                    exposureData[playerName].exposure = Math.min(exposureData[playerName].exposure, influenceTime / 2)
                }
                exposureData[playerName].lastUpdate = Date.now();
            }
            saveExposureData(exposureData);
            return;
        }

        const
            pyramidInfluenceRadius = 64,
            monolithInfluenceRadius = 96,
            voidVillageInfluenceRadius = 500,
            pyramidPlayers = overworld.getPlayers({
                location: pyramidCenter,
                maxDistance: pyramidInfluenceRadius,
                scoreOptions: [{
                    objective: "Random",
                    minScore: 0,
                }]
            }),
            monolithPlayers = overworld.getPlayers({
                location: monolithCenter,
                maxDistance: monolithInfluenceRadius,
                scoreOptions: [{
                    objective: "Random",
                    minScore: 0,
                }]
            }),
            VoidVillagePlayers = theEnd.getPlayers({
                location: voidVillageCenter,
                maxDistance: voidVillageInfluenceRadius,
                scoreOptions: [{
                    objective: "Random",
                    minScore: 0,
                }]
            });

        
        const players = [...new Set([...pyramidPlayers, ...monolithPlayers, ...VoidVillagePlayers])];

        
        for (let player of players) {
            const playerName = player.name;

            if (!exposureData[playerName]) {
                exposureData[playerName] = {
                    exposure: 0,
                    lastUpdate: Date.now()
                };
            } else {
                
                const timePassed = Math.floor((Date.now() - exposureData[playerName].lastUpdate) / 50); 
                exposureData[playerName].exposure += timePassed;
                exposureData[playerName].exposure = Math.min(exposureData[playerName].exposure, influenceTime)
            }

            
            exposureData[playerName].lastUpdate = Date.now();
            
        }

        
        saveExposureData(exposureData);
    };

    function playWhispers() {
        const
            soundLength = 2640, 
            exposureData = JSON.parse(world.getDynamicProperty(PYRAMID_INFLUENCE_PROPERTY) || "{}"),
            currentTime = Date.now();

        for (let playerName in exposureData) {
            const
                playerData = exposureData[playerName],
                player = world.getPlayers({ name: playerName })[0],
                maxVolume = 0.6;

            if (!player) {
                
                continue;
            }

            
            const timePassed = Math.floor((currentTime - playerData.lastUpdate) / 50);
            const currentExposure = playerData.exposure + timePassed;

            
            const volume = Math.max(Math.floor((currentExposure / influenceTime) * maxVolume * 100) / 100, 0.01);

            
            player.playSound("fo_whispers2", {
                location: player.location,
                volume: volume,
            });
        }

        
        saveExposureData(exposureData);

        system.runTimeout(() => { playWhispers() }, soundLength)
    }

    
    function decayPyramidInfluence(overworld, theEnd, pyramidCenter, monolithCenter, voidVillageCenter, forebodingScore) {

        const
            pyramidInfluenceRadius = 64,
            monolithInfluenceRadius = 96,
            voidVillageInfluenceRadius = 500,
            exposureData = JSON.parse(world.getDynamicProperty(PYRAMID_INFLUENCE_PROPERTY) || "{}"),

            pyramidPlayersOptions = forebodingScore < forebodingScoreCheck7 ? {} : { 
                location: pyramidCenter,
                maxDistance: pyramidInfluenceRadius
            },

            pyramidPlayers = overworld.getPlayers(pyramidPlayersOptions).map(p => p.name),

            monolithPlayers = overworld.getPlayers({
                location: monolithCenter,
                maxDistance: monolithInfluenceRadius
            }).map(p => p.name),

            
            VoidVillagePlayers = theEnd.getPlayers({
                location: voidVillageCenter,
                maxDistance: voidVillageInfluenceRadius
            }).map(p => p.name),

            playersInInfluenceZones = [...new Set([...pyramidPlayers, ...monolithPlayers, ...VoidVillagePlayers])],

            currentTime = Date.now();

        for (let playerName in exposureData) {
            const playerData = exposureData[playerName];

            
            if (!playersInInfluenceZones.includes(playerName)) {
                const timePassed = Math.floor((currentTime - playerData.lastUpdate) / 50);
                playerData.exposure = Math.max(0, playerData.exposure - (timePassed / 2));
                playerData.lastUpdate = currentTime;

                

                
                if (playerData.exposure <= 0) {
                    delete exposureData[playerName];
                }
            }
        }

        saveExposureData(exposureData);
    }


    

    system.runTimeout(() => { playForedepth() }, 97)

    let audioStringOffset = 0;

    function playForedepth() {
        const
            forebodingScore = fo.forebodingScore;

        if (forebodingScore < forebodingScoreCheck3 && forebodingScore > forebodingScoreCheck7) {
            system.runTimeout(() => { playForedepth() }, 2097)
            return
        };
        const
            overworld = fo.overworld,
            overworldPlayers = overworld.getPlayers(),
            players = [...overworldPlayers].filter(player => player.location.y <= 30);

        if (players.length === 0) {
            system.runTimeout(() => { playForedepth() }, 97)
            return
        };


        const
            audioString = [
                { name: "foredepths_01", length: 200 },
                { name: "foredepths_02", length: 200 },
                { name: "foredepths_03", length: 200 },
                { name: "foredepths_04", length: 200 },
                { name: "foredepths_05", length: 200 },
                { name: "foredepths_06", length: 197 },
            ],
            audioLength = audioString[audioStringOffset].length, 

            currentScore = forebodingScore < forebodingScoreCheck7 ? fo.scoreChapter5 : forebodingScore,
            minChapterVolume = 0.01,  
            maxChapterVolume = 0.15,  
            minChapterScore = 10, 
            maxChapterScore = forebodingScoreCheck5, 

            clampedScore = Math.max(minChapterScore, Math.min(currentScore, maxChapterScore)),
            chapterProgress = (clampedScore - minChapterScore) / (maxChapterScore - minChapterScore),
            chapterVolume = minChapterVolume + (chapterProgress * (maxChapterVolume - minChapterVolume));

        for (let player of players) {
            
            const
                shallowDepth = 30,  
                deepDepth = -60,    
                playerY = player.location.y,

                
                clampedY = Math.max(deepDepth, Math.min(playerY, shallowDepth)),
                
                depthFactor = (shallowDepth - clampedY) / (shallowDepth - deepDepth),
                
                depthMultiplier = 0.01 + (depthFactor * 0.99),

                
                finalVolume = Math.min(Math.floor((chapterVolume * depthMultiplier) * 100) / 100, maxChapterVolume);

            
            player.playSound(audioString[audioStringOffset].name, {
                location: player.location,
                volume: finalVolume,
            });
        }

        if (audioStringOffset === 5) {
            audioStringOffset = 0;
        } else audioStringOffset++

        system.runTimeout(() => { playForedepth() }, audioLength)
    }

}