import { world, system } from "@minecraft/server";
import { Random } from "../util/random";
import { FXSequencer } from "../util/fx";
import BIOMES from "../util/biomes";
import { clamp } from "../util/math";
import { settingEnabled } from "../settings";

// Constants
// Main loop interval -- 1 second
const customParticlesDelayTicks = 20;
// This is accounting for player direction and also randomizing location a little
const motionPredictionInfluence = 80;
const motionPredictionMaxDistance = 25;
const regionRandomSpreadXZ = 5;
const regionRandomSpreadYMin = 1;
const regionRandomSpreadYMax = 5;
// All probabilities for the particles to spawn
const baseChanceDust = 0.75;
const baseChanceSandstorm = 0.8;
const baseChanceFireflies = 0.75;
const baseChanceFirefliesGroup = 0.4;
const baseChanceFirefliesRandom = 0.3;
const baseChanceButterfliesRandom = 0.025;
const baseChanceFog = 0.1;
const baseChanceBirds = 0.025;
const baseChanceSnow = 0.8;
const baseChanceSnowstorm = 0.6;


// Adding all categories here
const ParticleCategory = {
    DUST: "dust",
    SANDSTORM: "sandstorm",
    FIREFLIES_SINGLE: "firefliesSingle",
    FIREFLIES_GROUP: "firefliesGroup",
    FIREFLIES_RANDOM: "firefliesRandom",
    BUTTERFLIES_RANDOM: "butterfliesRandom",
    FOG: "fog",
    BIRDS: "birds",
    SNOW: "snow",
    SNOWSTORM: "snowstorm"
};

// Initialize particle sequences for different biomes
// If a particle has a biome variant, add it in here
const particleSequences = {
    [ParticleCategory.DUST]: {
        beach: FXSequencer.register("dust_beach"),
        forest: FXSequencer.register("dust_forest"),
        plains: FXSequencer.register("dust_plains"),
        desert: FXSequencer.register("dust_desert"),
        jungle: FXSequencer.register("dust_jungle"),
        birch: FXSequencer.register("dust_birch"),
        frozen: FXSequencer.register("dust_frozen"),
        savanna: FXSequencer.register("dust_savanna"),
        mesa: FXSequencer.register("dust_mesa"),
        swamp: FXSequencer.register("dust_swamp"),
        taiga: FXSequencer.register("dust_taiga"),
        lush_caves: FXSequencer.register("dust_lush_caves"),
        dripstone_caves: FXSequencer.register("dust_dripstone_caves"),
        mangrove: FXSequencer.register("dust_mangrove"),
        deep_dark: FXSequencer.register("dust_deep_dark"),
        cherry_grove: FXSequencer.register("dust_cherry_grove")
    },
    [ParticleCategory.SANDSTORM]: {
        desert: FXSequencer.register("sandstorm_desert")
    },
    [ParticleCategory.FIREFLIES_SINGLE]: {
        single: FXSequencer.register("firefliesSingle")
    },
    [ParticleCategory.FIREFLIES_GROUP]: {
        group: FXSequencer.register("firefliesGroup")
    },
    [ParticleCategory.FIREFLIES_RANDOM]: {
        fireflies_2: FXSequencer.register("fireflies_2"),
        fireflies_3: FXSequencer.register("fireflies_3"),
        fireflies_4: FXSequencer.register("fireflies_4"),
        fireflies_5: FXSequencer.register("fireflies_5"),
        fireflies_6: FXSequencer.register("fireflies_6"),
        fireflies_7: FXSequencer.register("fireflies_7"),
        fireflies_8: FXSequencer.register("fireflies_8")
    },
    [ParticleCategory.BUTTERFLIES_RANDOM]: {
        butterflies_1: FXSequencer.register("butterflies_1"),
        butterflies_2: FXSequencer.register("butterflies_2")
    },
    [ParticleCategory.FOG]: {
        swamp: FXSequencer.register("fog_swamp"),
        forest: FXSequencer.register("fog_forest"),
    },
    [ParticleCategory.BIRDS]: {
        forest: FXSequencer.register("birds_forest"),
        plains: FXSequencer.register("birds_plains")
    },
    [ParticleCategory.SNOW]: {
        frozen: FXSequencer.register("snow_particle")
    },
    [ParticleCategory.SNOWSTORM]: {
        frozen: FXSequencer.register("snowstorm")
    }
};

// Add particles to respective sequences
// Dust Particles
particleSequences.dust.beach.addParticle(0, "squaredreams_realism:dust_beach");
particleSequences.dust.forest.addParticle(0, "squaredreams_realism:dust_forest");
particleSequences.dust.plains.addParticle(0, "squaredreams_realism:dust_plains");
particleSequences.dust.desert.addParticle(0, "squaredreams_realism:dust_desert");
particleSequences.dust.jungle.addParticle(0, "squaredreams_realism:dust_jungle");
particleSequences.dust.birch.addParticle(0, "squaredreams_realism:dust_birch");
particleSequences.dust.frozen.addParticle(0, "squaredreams_realism:dust_frozen");
particleSequences.dust.savanna.addParticle(0, "squaredreams_realism:dust_savanna");
particleSequences.dust.mesa.addParticle(0, "squaredreams_realism:dust_mesa");
particleSequences.dust.swamp.addParticle(0, "squaredreams_realism:dust_swamp");
particleSequences.dust.taiga.addParticle(0, "squaredreams_realism:dust_taiga");
particleSequences.dust.lush_caves.addParticle(0, "squaredreams_realism:dust_lush_caves");
particleSequences.dust.dripstone_caves.addParticle(0, "squaredreams_realism:dust_dripstone_caves");
particleSequences.dust.mangrove.addParticle(0, "squaredreams_realism:dust_mangrove");
particleSequences.dust.deep_dark.addParticle(0, "squaredreams_realism:dust_deep_dark");
particleSequences.dust.cherry_grove.addParticle(0, "squaredreams_realism:dust_cherry_grove");
// Sandstorm Particles
particleSequences.sandstorm.desert.addParticle(0, "squaredreams_realism:sandstorm_desert");
// Fireflies Particles
particleSequences.firefliesSingle.single.addParticle(0, "squaredreams_realism:fireflies_1");
particleSequences.firefliesGroup.group.addParticle(0, "squaredreams_realism:fireflies_group");
particleSequences.firefliesRandom.fireflies_2.addParticle(0, "squaredreams_realism:fireflies_2");
particleSequences.firefliesRandom.fireflies_3.addParticle(0, "squaredreams_realism:fireflies_3");
particleSequences.firefliesRandom.fireflies_4.addParticle(0, "squaredreams_realism:fireflies_4");
particleSequences.firefliesRandom.fireflies_5.addParticle(0, "squaredreams_realism:fireflies_5");
particleSequences.firefliesRandom.fireflies_6.addParticle(0, "squaredreams_realism:fireflies_6");
particleSequences.firefliesRandom.fireflies_7.addParticle(0, "squaredreams_realism:fireflies_7");
particleSequences.firefliesRandom.fireflies_8.addParticle(0, "squaredreams_realism:fireflies_8");
// Butterflies Particles
particleSequences.butterfliesRandom.butterflies_1.addParticle(0, "squaredreams_realism:butterflies_1");
particleSequences.butterfliesRandom.butterflies_2.addParticle(0, "squaredreams_realism:butterflies_2");
// Fog Particles
particleSequences.fog.swamp.addParticle(0, "squaredreams_realism:fog_swamp");
particleSequences.fog.forest.addParticle(0, "squaredreams_realism:fog_forest");
// Birds particles
particleSequences.birds.forest.addParticle(0, "squaredreams_realism:birds");
particleSequences.birds.plains.addParticle(0, "squaredreams_realism:birds");
// Snow Particles
particleSequences.snow.frozen.addParticle(0, "squaredreams_realism:snow");
particleSequences.snowstorm.frozen.addParticle(0, "squaredreams_realism:snowstorm");

// Map biome IDs to sequence names for different particle categories
const conditionToSequenceName = {
    [ParticleCategory.DUST]: {
        [BIOMES.beach]: "dust_beach",
        [BIOMES.forest]: "dust_forest",
        [BIOMES.plains]: "dust_plains",
        [BIOMES.desert]: "dust_desert",
        [BIOMES.jungle]: "dust_jungle",
        [BIOMES.birch]: "dust_birch",
        [BIOMES.frozen]: "dust_frozen",
        [BIOMES.savanna]: "dust_savanna",
        [BIOMES.mesa]: "dust_mesa",
        [BIOMES.swamp]: "dust_swamp",
        [BIOMES.taiga]: "dust_taiga",
        [BIOMES.lush_caves]: "dust_lush_caves",
        [BIOMES.dripstone_caves]: "dust_dripstone_caves",
        [BIOMES.mangrove]: "dust_mangrove",
        [BIOMES.deep_dark]: "dust_deep_dark",
        [BIOMES.cherry_grove]: "dust_cherry_grove"
    },
    [ParticleCategory.SANDSTORM]: {
        [BIOMES.desert]: "sandstorm_desert"
    },
    [ParticleCategory.FIREFLIES_SINGLE]: {
        single: "firefliesSingle"
    },
    [ParticleCategory.FIREFLIES_GROUP]: {
        group: "firefliesGroup"
    },
    [ParticleCategory.FIREFLIES_RANDOM]: {
        fireflies_2: "fireflies_2",
        fireflies_3: "fireflies_3",
        fireflies_4: "fireflies_4",
        fireflies_5: "fireflies_5",
        fireflies_6: "fireflies_6",
        fireflies_7: "fireflies_7",
        fireflies_8: "fireflies_8"
    },
    [ParticleCategory.BUTTERFLIES_RANDOM]: {
        butterflies_1: "butterflies_1",
        butterflies_2: "butterflies_2"
    },
    [ParticleCategory.FOG]: {
        [BIOMES.swamp]: "fog_swamp",
        [BIOMES.forest]: "fog_forest",
    },
    [ParticleCategory.BIRDS]: {
        [BIOMES.forest]: "birds_forest",
        [BIOMES.plains]: "birds_plains"
    },
    [ParticleCategory.SNOW]: {
        [BIOMES.frozen_ocean]: "snow_particle",
    },
    [ParticleCategory.SNOWSTORM]: {
        [BIOMES.frozen_ocean]: "snowstorm",
    }
};

// Function to determine if particles should spawn based on biome and category
function shouldShowParticle(biomeId, category) {
    switch (category) {
        case ParticleCategory.DUST:
            return shouldShowDust(biomeId);
        case ParticleCategory.SANDSTORM:
            return shouldShowSandstorm(biomeId);
        case ParticleCategory.FIREFLIES_SINGLE:
            return shouldShowFirefliesSingle(biomeId);
        case ParticleCategory.FIREFLIES_GROUP:
            return shouldShowFirefliesGroup(biomeId);
        case ParticleCategory.FIREFLIES_RANDOM:
            return shouldShowFirefliesRandom();
        case ParticleCategory.BUTTERFLIES_RANDOM:
            return shouldShowButterfliesRandom(biomeId);
        case ParticleCategory.FOG:
            return shouldShowFog(biomeId);
        case ParticleCategory.BIRDS:
            return shouldShowBirds(biomeId);
        case ParticleCategory.SNOW:
            return shouldShowSnow(biomeId);
        case ParticleCategory.SNOWSTORM:
            return shouldShowSnowstorm(biomeId);
        default:
            return false;
    }
}

// Function to determine if dust particles should spawn
function shouldShowDust(biomeId) {
    // Return if not enabled
    if (!settingEnabled("dust")) return;
    switch (biomeId) {
        case BIOMES.beach:
        case BIOMES.forest:
        case BIOMES.plains:
        case BIOMES.desert:
        case BIOMES.jungle:
        case BIOMES.birch:
        case BIOMES.frozen:
        case BIOMES.savanna:
        case BIOMES.mesa:
        case BIOMES.swamp:
        case BIOMES.taiga:
        case BIOMES.lush_caves:
        case BIOMES.dripstone_caves:
        case BIOMES.mangrove:
        case BIOMES.deep_dark:
        case BIOMES.cherry_grove:
            return Math.random() < baseChanceDust;
        default:
            return false;
    }
}

// Function to determine if sandstorm particles should spawn
function shouldShowSandstorm(biomeId) {
    // Return if not enabled
    if (!settingEnabled("sandstorm")) return;
    const timeOfDay = world.getTimeOfDay();
    const isDaytime = timeOfDay >= 1000 && timeOfDay <= 13000;
    switch (biomeId) {
        case BIOMES.desert:
            return isDaytime && Math.random() < baseChanceSandstorm;
        default:
            return false;
    }
}

// Function to determine if fireflies particles should spawn
function shouldShowFirefliesSingle(biomeId) {
    // Return if not enabled
    if (!settingEnabled("fireflies")) return;
    const isSwampOrMangrove = biomeId === BIOMES.swamp || biomeId === BIOMES.mangrove;
    const chance = isSwampOrMangrove ? baseChanceFireflies * 2 : baseChanceFireflies;

    // Check if it's swamp or mangrove biome
    if (isSwampOrMangrove) {
        return Math.random() < chance;
    } else {
        // Default behavior for other biomes (only at night)
        const timeOfDay = world.getTimeOfDay();
        const disallowedBiomes = [BIOMES.cold, BIOMES.taiga, BIOMES.mountains, BIOMES.ocean, BIOMES.cave, BIOMES.frozen_ocean];
        return (!disallowedBiomes.includes(biomeId)) && (timeOfDay > 13000 && timeOfDay < 21000) && Math.random() < chance;
    }
}

function shouldShowFirefliesGroup(biomeId) {
    // Return if not enabled
    if (!settingEnabled("fireflies")) return;
    const timeOfDay = world.getTimeOfDay();
    const isSwampOrMangrove = biomeId === BIOMES.swamp || biomeId === BIOMES.mangrove;
    const chance = isSwampOrMangrove ? baseChanceFirefliesGroup * 2 : baseChanceFirefliesGroup;
    const disallowedBiomes = [BIOMES.cold, BIOMES.taiga, BIOMES.mountains, BIOMES.ocean, BIOMES.cave, BIOMES.frozen_ocean];
    return (!disallowedBiomes.includes(biomeId)) && (timeOfDay > 13000 && timeOfDay < 21000) && Math.random() < chance;
}

function shouldShowFirefliesRandom(biomeId) {
    // Return if not enabled
    if (!settingEnabled("fireflies")) return;
    const timeOfDay = world.getTimeOfDay();
    const isSwampOrMangrove = biomeId === BIOMES.swamp || biomeId === BIOMES.mangrove;
    const chance = isSwampOrMangrove ? baseChanceFirefliesRandom * 2 : baseChanceFirefliesRandom;
    const disallowedBiomes = [BIOMES.cold, BIOMES.taiga, BIOMES.mountains, BIOMES.ocean, BIOMES.cave, BIOMES.frozen_ocean];
    return (!disallowedBiomes.includes(biomeId)) && (timeOfDay > 13000 && timeOfDay < 21000) && Math.random() < chance;
}

// Function to determine if butterfliesRandom particles should spawn
function shouldShowButterfliesRandom(biomeId) {
    // Return if not enabled
    if (!settingEnabled("butterflies")) return;
    const timeOfDay = world.getTimeOfDay();
    const isDaytime = timeOfDay >= 1000 && timeOfDay <= 13000;
    const allowedBiomes = [BIOMES.forest, BIOMES.jungle, BIOMES.plains, BIOMES.birch, BIOMES.mangrove, BIOMES.swamp];
    return isDaytime && allowedBiomes.includes(biomeId) && Math.random() < baseChanceButterfliesRandom;
}

// Function to determine if fog particles should spawn
function shouldShowFog(biomeId) {
    // Return if not enabled
    if (!settingEnabled("fog")) return;
    switch (biomeId) {
        case BIOMES.swamp:
        case BIOMES.forest:
            return Math.random() < baseChanceFog;
        default:
            return false;
    }
}

// Function to determine if birds particles should spawn
function shouldShowBirds(biomeId) {
    if (!settingEnabled("birds")) return;
    const allowedBiomes = [BIOMES.forest, BIOMES.plains];
    return allowedBiomes.includes(biomeId) && Math.random() < baseChanceBirds;
}

// Function to determine if snow particles should spawn
function shouldShowSnow(biomeId) {
    if (!settingEnabled("snow")) return;
    const allowedBiomes = [BIOMES.frozen_ocean];
    return allowedBiomes.includes(biomeId) && Math.random() < baseChanceSnow;
}

// Function to determine if snowstorm particles should spawn
function shouldShowSnowstorm(biomeId) {
    if (!settingEnabled("snow")) return;
    const allowedBiomes = [BIOMES.frozen_ocean];
    return allowedBiomes.includes(biomeId) && Math.random() < baseChanceSnowstorm;
}

// Main interval loop for spawning particles based on biome and random chance
system.runInterval(() => {
    // Get all players
    const allPlayers = world.getAllPlayers();
    // If no players, return
    if (allPlayers.length === 0) return;

    // Choose random player
    const chosenPlayer = Random.chooseFromArray(allPlayers);
    const { dimension, location } = chosenPlayer;
    const velocity = chosenPlayer.getVelocity();

    // Calculate velocity contribution for motion prediction
    const velocityContributionX = clamp(velocity.x * motionPredictionInfluence, -motionPredictionMaxDistance, motionPredictionMaxDistance);
    const velocityContributionZ = clamp(velocity.z * motionPredictionInfluence, -motionPredictionMaxDistance, motionPredictionMaxDistance);

    // Randomize location slightly
    const resultLocation = {
        x: Math.floor(location.x + velocityContributionX + Random.IntegerWithinRange(-1, 1) * regionRandomSpreadXZ) + 0.5,
        y: Math.floor(location.y + Random.IntegerWithinRange(regionRandomSpreadYMin, regionRandomSpreadYMax)),
        z: Math.floor(location.z + velocityContributionZ + Random.IntegerWithinRange(-1, 1) * regionRandomSpreadXZ) + 0.5,
    };

    try {
        // Summon biome_tester
        const biomeTester = dimension.spawnEntity("squaredreams_realism:biome_tester", resultLocation);

        // If biome tester is invalid, log and return
        if (!biomeTester || !biomeTester.isValid()) {
            return;
        }

        // Trigger event to query biome
        biomeTester.triggerEvent("squaredreams_realism:query_biome");

        // Handle biome query result after a short delay
        system.runTimeout(() => {
            try {
                const biomeId = biomeTester.getProperty("squaredreams_realism:biome_query");

                // Determine particle category based on biome
                Object.keys(particleSequences).forEach(category => {
                    const sequenceName = conditionToSequenceName[category][biomeId];
                    if (sequenceName && shouldShowParticle(biomeId, category)) {
                        FXSequencer.play(sequenceName, chosenPlayer.dimension, resultLocation);
                    }
                });

                // Check for fireflies particles
                if (shouldShowFirefliesSingle(biomeId)) {
                    FXSequencer.play(conditionToSequenceName[ParticleCategory.FIREFLIES_SINGLE].single, chosenPlayer.dimension, resultLocation);
                }
                if (shouldShowFirefliesGroup(biomeId)) {
                    FXSequencer.play(conditionToSequenceName[ParticleCategory.FIREFLIES_GROUP].group, chosenPlayer.dimension, resultLocation);
                }
                if (shouldShowFirefliesRandom(biomeId)) {
                    // Generates a number from 2 to 8
                    const sequenceNumber = Math.floor(Math.random() * 7) + 2;
                    const sequenceName = `fireflies_${sequenceNumber}`;
                    FXSequencer.play(sequenceName, chosenPlayer.dimension, resultLocation);
                }

                // Check for butterfliesRandom particles
                if (shouldShowButterfliesRandom(biomeId)) {
                    // Generates a number from 2 to 8
                    const sequenceNumber = Math.floor(Math.random() * 2) + 1;
                    const sequenceName = `butterflies_${sequenceNumber}`;
                    FXSequencer.play(sequenceName, chosenPlayer.dimension, resultLocation);
                }

            } catch (err) {
                // Handle any errors silently
            } finally {
                // Despawn the biome tester entity
                biomeTester.triggerEvent("squaredreams_realism:instant_despawn");
            }
        }, 20);

    } catch (err) {
        // Handle any errors silently
    }
}, customParticlesDelayTicks);