import { world, system, MolangVariableMap } from "@minecraft/server";
import { settingEnabled } from "../settings";
import { Random } from "../util/random";
import { clamp, randomAB } from "../util/math";
import SimplexNoise from "../util/simplexNoise";

// Constants
const volumetricCloudsDelayTicks = 20;
const CloudNoise = new SimplexNoise();
const densityMultiplier = 0.005;
const typeMultiplier = 0.00012;
const textureMultiplier = 0.00015;

// Caching noise values for optimization
let cachedNoiseValues = {};

function getCachedNoiseValue(key, x, y) {
    const cacheKey = `${key}_${x}_${y}`;
    if (!(cacheKey in cachedNoiseValues)) {
        cachedNoiseValues[cacheKey] = CloudNoise.noise(x, y);
    }
    return cachedNoiseValues[cacheKey];
}

system.runInterval(function() {
    if (!settingEnabled("volumetric_clouds")) return;

    const allPlayers = world.getAllPlayers();
    if (allPlayers.length === 0) return;

    const chosenPlayer = Random.chooseFromArray(allPlayers);
    let overworld = world.getDimension("overworld");
    const absoluteTime = world.getAbsoluteTime();
    const timeOfDay = absoluteTime % 24000;

    let densityNoise = getCachedNoiseValue('density', (timeOfDay * densityMultiplier) % 1, 300);
    let typeNoise = getCachedNoiseValue('type', (timeOfDay * typeMultiplier + 1000) % 1, -500);
    let textureNoise = getCachedNoiseValue('texture', (timeOfDay * textureMultiplier + 2000) % 1, -1000);

    let cloudDensity = Math.max(0, Math.min(1, (densityNoise * 0.6) + 0.4));
    let cloudScale = Math.max(0, Math.min(1, (typeNoise * 0.7) + 0.3));
    let cloudVariant = Math.max(0, Math.min(1, (textureNoise * 0.8) + 0.2));

    let baseIterations = Math.max(0, Math.floor((cloudDensity * 8) + 4));
    let shapeFactor = (cloudScale > 0.6) ? 0.7 : (cloudScale < 0.2) ? 1.3 : 1;

    // Only use basic cloud type
    let cloudType = "squaredreams_realism:clouds_basic";
    let radiusRange = [30, 100];

    if (timeOfDay > 14000) baseIterations = Math.max(0, baseIterations - 2);

    let velocityFactor = Math.min(6, Math.sqrt(
        Math.pow(chosenPlayer.getVelocity().x, 2) +
        Math.pow(chosenPlayer.getVelocity().z, 2)
    ));
    let finalIterations = Math.floor(baseIterations * velocityFactor * 1.2 * 0.65);

    for (let i = 0; i < finalIterations; i++) {
        let radius = Math.random() * (radiusRange[1] - radiusRange[0]) + radiusRange[0];
        if (shapeFactor !== 1) radius *= shapeFactor;

        let angle = Math.random() * 2 * Math.PI;
        let offset = [
            chosenPlayer.location.x + Math.cos(angle) * radius,
            chosenPlayer.location.z + Math.sin(angle) * radius,
        ];

        let timeShift = (timeOfDay / 1200) * 1.5;
        let noiseValue = getCachedNoiseValue('cloud', offset[0] / 100 + timeShift, offset[1] / 100 + 0.6 * timeShift);

        if (noiseValue > randomAB(-0.1, 0.2)) {
            let variables = new MolangVariableMap();
            variables.setVector3("offset", {
                x: offset[0] - chosenPlayer.location.x,
                y: 160 + noiseValue * 60 - chosenPlayer.location.y,
                z: offset[1] - chosenPlayer.location.z,
            });
            variables.setFloat("shape", shapeFactor);
            overworld.spawnParticle(cloudType, chosenPlayer.location, variables);
        }
    }

}, volumetricCloudsDelayTicks);