import { MinecraftDimensionTypes, system, world } from "@minecraft/server";
import { getRandomInt, shuffleArray, validateBlockPlacementHeight } from "../utils/my_API";
import config from "nps_mot/config";
import { getRegionScoreboard, parameterNames, Settings } from "nps_mot/utils/scoreboard";
class Region {
    constructor(dimensionId, x, z) {
        this.dimensionId = dimensionId;
        this.x = x;
        this.z = z;
    }
    static get(data) {
        return new Region(data.dimensionId, data.x, data.z);
    }
    // Méthode pour générer des minerais dans la région
    static generateOres(region) {
        const regionValidOreList = OrePlacementCollection.filterOresByDimension(region.dimensionId);
        const shuffleOres = shuffleArray(regionValidOreList).slice(0, 25);
        for (const ore of shuffleOres) {
            const oreVeinNumber = ore.getVeinNumber();
            const veinNumber = getRandomInt(oreVeinNumber[0], oreVeinNumber[1]);
            for (let vein = 0; vein < veinNumber; vein++) {
                let placedOres = [];
                // Trouver un bloc non vide pour commencer la génération des minerais
                let solidBlock = findSolidBlock(region, ore);
                //console.warn(JSON.stringify(solidBlock))
                if (solidBlock === undefined) {
                    continue; // Passer à la veine suivante si aucun bloc non vide n'est trouvé dans la région
                }
                //console.warn('solidBlock', JSON.stringify(solidBlock))
                let [xStart, yStart, zStart] = [solidBlock.x, solidBlock.y, solidBlock.z];
                // Génération de la veine
                const veinSize = getRandomInt(ore.veinSize[0], ore.veinSize[1]);
                const dimension = world.getDimension(region.dimensionId);
                for (let i = 0; i < veinSize; i++) {
                    yStart = validateBlockPlacementHeight(region.dimensionId, yStart);
                    let oreId = ore.typeId;
                    if (region.dimensionId == MinecraftDimensionTypes.overworld) {
                        if (yStart < 0) {
                            oreId = addDeepslatePrefix(ore.typeId);
                        }
                    }
                    //console.warn(JSON.stringify(world.getDimension(region.dimensionId).runCommand(`setblock ${xStart} ${yStart} ${zStart} ${oreId}`)))
                    //dimension.runCommand(`setblock ${xStart} ${yStart} ${zStart} ${oreId}`);
                    dimension.getBlock({ x: xStart, y: yStart, z: zStart })?.setType(oreId);
                    //
                    const validAdjacentBlocks = getValidAdjacentBlocks([...placedOres, solidBlock]);
                    //validAdjacentBlocks = shuffleArray(validAdjacentBlocks);
                    if (validAdjacentBlocks.length != 0) {
                        const randomIndex = getRandomInt(0, validAdjacentBlocks.length - 1);
                        const randomBlock = validAdjacentBlocks[randomIndex];
                        xStart = randomBlock.x;
                        yStart = randomBlock.y;
                        zStart = randomBlock.z;
                        placedOres.push(randomBlock);
                    }
                }
            }
        }
    }
}
function getValidAdjacentBlocks(blocks) {
    const validAdjacentBlocks = new Set();
    for (const block of blocks) {
        const adjacentBlocks = [
            block.east(),
            block.west(),
            block.north(),
            block.south(),
            block.above(),
            block.below()
            /*,
            block.above()?.north(),
            block.above()?.south(),
            block.above()?.west(),
            block.above()?.east(),
            block.below()?.north(),
            block.below()?.south(),
            block.below()?.west(),
            block.below()?.east(),
            block.north()?.east(),
            block.north()?.west(),
            block.south()?.east(),
            block.south()?.west(),
            */
        ];
        // Filtrer et valider les blocs adjacents
        for (const adjacentBlock of adjacentBlocks) {
            if (!adjacentBlock)
                continue; // Si le bloc est indéfini
            const validHeight = validateBlockPlacementHeight(adjacentBlock.dimension.id, adjacentBlock.y);
            if (validHeight !== adjacentBlock.y)
                continue; // Vérifier la hauteur valide
            const replaceableBlocks = DimensionBlocks.getReplaceableBlocks(block.dimension.id);
            if (!replaceableBlocks.includes(adjacentBlock.typeId))
                continue; // Vérifier si remplaçable
            // Ajouter le bloc valide au Set
            validAdjacentBlocks.add(adjacentBlock);
        }
    }
    // Convertir le Set en tableau avant de le retourner
    return Array.from(validAdjacentBlocks);
}
class ResourcePlacementHandler {
    constructor() { }
    static tick() {
        //Identify regions where players are present & remove duplicates
        const uniquePlayerRegions = this.identifyUniquePlayerRegions();
        //console.warn('uniquePlayerRegions:', uniquePlayerRegions.length)
        //Exclude already generated areas
        const newPlayerRegions = this.excludeGeneratedAreas(uniquePlayerRegions);
        //console.warn('newPlayerRegions:', newPlayerRegions.length)
        //Generate undiscovered regions
        this.generateUndiscoveredRegions(newPlayerRegions);
    }
    static identifyUniquePlayerRegions() {
        const discoveredRegions = new Set();
        const regionLocations = [];
        const players = world.getAllPlayers();
        const regionSize = config.regionSize;
        for (const player of players) {
            // Ignorer la dimension "theEnd"
            if (player.dimension.id === MinecraftDimensionTypes.theEnd)
                continue;
            // Calculer la région du joueur
            const playerRegionX = Math.floor(player.location.x / regionSize);
            const playerRegionZ = Math.floor(player.location.z / regionSize);
            const regionKey = `${player.dimension.id}:${playerRegionX}:${playerRegionZ}`;
            // Ajouter la région si elle n'a pas été découverte
            if (!discoveredRegions.has(regionKey)) {
                regionLocations.push(new Region(player.dimension.id, playerRegionX, playerRegionZ));
                discoveredRegions.add(regionKey);
            }
            /*
            // Si les régions autour du joueur sont également souhaitées
            const regionDistance = Math.ceil(generationDistance * 16 / regionSize);
            this.addSurroundingRegions(player, playerRegionX, playerRegionZ, regionDistance, discoveredRegions, regionLocations);
            */
        }
        return regionLocations;
    }
    // Fonction helper pour ajouter les régions autour du joueur
    static addSurroundingRegions(player, playerRegionX, playerRegionZ, regionDistance, discoveredRegions, regionLocations) {
        for (let offsetX = -regionDistance; offsetX <= regionDistance; offsetX++) {
            for (let offsetZ = -regionDistance; offsetZ <= regionDistance; offsetZ++) {
                const regionX = playerRegionX + offsetX;
                const regionZ = playerRegionZ + offsetZ;
                const distance = Math.sqrt(offsetX * offsetX + offsetZ * offsetZ);
                // Ajouter si la région est dans la distance spécifiée et n'a pas été découverte
                if (distance <= regionDistance) {
                    const regionKey = `${player.dimension.id}:${regionX}:${regionZ}`;
                    if (!discoveredRegions.has(regionKey)) {
                        regionLocations.push(new Region(player.dimension.id, regionX, regionZ));
                        discoveredRegions.add(regionKey);
                    }
                }
            }
        }
    }
    static excludeGeneratedAreas(playerRegions) {
        const filteredRegions = playerRegions.filter(region => {
            const regionKey = `${region.x}:${region.z}`;
            const scoreboard = getRegionScoreboard(region.dimensionId);
            return !scoreboard.hasParticipant(regionKey);
        });
        return filteredRegions;
    }
    static generateUndiscoveredRegions(undiscoveredRegions) {
        for (const region of undiscoveredRegions) {
            const scoreboard = getRegionScoreboard(region.dimensionId);
            const regionGeneratedIdentity = 'regionGenerated';
            const regionGenerated = scoreboard.hasParticipant(regionGeneratedIdentity) ? scoreboard.getScore(regionGeneratedIdentity) : 0;
            const regionKey = `${region.x}:${region.z}`;
            scoreboard.setScore(regionKey, regionGenerated);
            scoreboard.setScore(regionGeneratedIdentity, regionGenerated + 1);
            // Appel de la génération des minerais
            Region.generateOres(region);
        }
    }
}
class DimensionBlocks {
    static getReplaceableBlocks(dimensionType) {
        return this.dimensionBlocksMap[dimensionType] || [];
    }
    static isBlockReplaceable(dimensionType, blockId) {
        const blocks = this.getReplaceableBlocks(dimensionType);
        return blocks.includes(blockId);
    }
}
DimensionBlocks.dimensionBlocksMap = {
    [MinecraftDimensionTypes.overworld]: [
        "minecraft:stone",
        "minecraft:gravel",
        "minecraft:deepslate"
    ],
    [MinecraftDimensionTypes.nether]: [
        "minecraft:netherrack",
        "minecraft:warped_nylium",
        "minecraft:crimson_nylium",
        "minecraft:magma",
        "minecraft:gravel"
    ],
    [MinecraftDimensionTypes.theEnd]: []
};
function findSolidBlock(region, ore) {
    const dimension = world.getDimension(region.dimensionId);
    const regionBaseX = region.x * config.regionSize;
    const regionBaseZ = region.z * config.regionSize;
    for (let test = 0; test < 20; test++) {
        const randomX = regionBaseX + getRandomInt(0, config.regionSize - 1);
        const randomY = validateBlockPlacementHeight(region.dimensionId, ore.density.getRandomY());
        const randomZ = regionBaseZ + getRandomInt(0, config.regionSize - 1);
        const block = dimension.getBlock({ x: randomX, y: randomY, z: randomZ });
        if (block && DimensionBlocks.isBlockReplaceable(region.dimensionId, block.typeId)) {
            return block;
        }
    }
    return undefined;
}
function addDeepslatePrefix(typeId) {
    return typeId.replace(/^nps_mot:/, "nps_mot:deepslate_");
}
export var OreRarity;
(function (OreRarity) {
    OreRarity["VERY_COMMON"] = "VERY_COMMON";
    OreRarity["COMMON"] = "COMMON";
    OreRarity["UNCOMMON"] = "UNCOMMON";
    OreRarity["RARE"] = "RARE";
    OreRarity["VERY_RARE"] = "VERY_RARE";
})(OreRarity || (OreRarity = {}));
class ProbabilityDensity {
    constructor(depthInterval) {
        this.depthInterval = depthInterval;
    }
    getDepthInterval() {
        return this.depthInterval;
    }
}
class TriangularOreDensity extends ProbabilityDensity {
    constructor(depthInterval, peak) {
        super(depthInterval);
        if (peak === undefined) {
            this.peak = (depthInterval[1] + depthInterval[0]) / 2; // Par défaut, définir 'peak' au milieu de l'intervalle
        }
        else {
            this.peak = peak;
        }
    }
    getOptimalY() {
        return [this.peak];
    }
    getRandomY() {
        const [min, max] = this.depthInterval;
        const F = (this.peak - min) / (max - min); // Calcul de la valeur du point où la distribution change de pente
        const rand = Math.random();
        let randomDepth;
        if (rand < F) {
            // Partie montante du triangle
            randomDepth = min + Math.sqrt(rand * (max - min) * (this.peak - min));
        }
        else {
            // Partie descendante du triangle
            randomDepth = max - Math.sqrt((1 - rand) * (max - min) * (max - this.peak));
        }
        // Arrondi à l'entier le plus proche
        return Math.round(randomDepth);
    }
}
class CustomOreDensity extends ProbabilityDensity {
    constructor(depthInterval, points) {
        super(depthInterval);
        this.points = points.sort((a, b) => a.depth - b.depth); // Tri des points par profondeur
    }
    interpolateDensity(depth) {
        if (depth < this.depthInterval[0] || depth > this.depthInterval[1]) {
            return 0;
        }
        let lowerPoint;
        let upperPoint;
        for (let i = 0; i < this.points.length - 1; i++) {
            if (depth >= this.points[i].depth && depth <= this.points[i + 1].depth) {
                lowerPoint = this.points[i];
                upperPoint = this.points[i + 1];
                break;
            }
        }
        if (!lowerPoint || !upperPoint) {
            return 0;
        }
        const ratio = (depth - lowerPoint.depth) / (upperPoint.depth - lowerPoint.depth);
        return lowerPoint.density + ratio * (upperPoint.density - lowerPoint.density);
    }
    // Méthode modifiée pour retourner des profondeurs entières
    getRandomY() {
        const maxDensity = Math.max(...this.points.map(point => point.density));
        while (true) {
            const randomDepth = this.depthInterval[0] + Math.random() * (this.depthInterval[1] - this.depthInterval[0]);
            const randomDensity = Math.random() * maxDensity;
            if (randomDensity <= this.interpolateDensity(randomDepth)) {
                return Math.round(randomDepth); // Arrondir à l'entier le plus proche
            }
        }
    }
    // Méthode modifiée pour retourner des profondeurs entières avec la densité maximale
    getOptimalY() {
        const optimalDensity = Math.max(...this.points.map(point => point.density));
        return this.points
            .filter(point => point.density === optimalDensity)
            .map(point => Math.round(point.depth)); // Arrondir à l'entier le plus proche
    }
}
class UniformOreDensity extends ProbabilityDensity {
    constructor(depthInterval) {
        super(depthInterval);
    }
    getOptimalY() {
        // Pour une distribution uniforme, il n'y a pas de "pic" unique
        return [];
    }
    getRandomY() {
        const [min, max] = this.depthInterval;
        return Math.floor(min + (max - min + 1) * Math.random());
    }
}
class OrePlacement {
    constructor(typeId, allowedDimension, rarity, density, veinSize) {
        this.typeId = typeId;
        this.allowedDimension = allowedDimension;
        this.rarity = rarity;
        this.veinSize = veinSize;
        this.density = density;
        this.density = density;
    }
    getVeinNumber() {
        let veinNumber = [1, 5];
        switch (this.rarity) {
            case OreRarity.VERY_COMMON: {
                veinNumber = [25, 35];
                break;
            }
            case OreRarity.COMMON: {
                veinNumber = [15, 20];
                break;
            }
            case OreRarity.UNCOMMON: {
                veinNumber = [10, 15];
                break;
            }
            case OreRarity.RARE: {
                veinNumber = [5, 10];
                break;
            }
            case OreRarity.VERY_RARE: {
                veinNumber = [1, 5];
                break;
            }
        }
        return veinNumber;
    }
}
system.runInterval(() => {
    if (Settings.getValue(parameterNames.oreGeneration) == 1) {
        ResourcePlacementHandler.tick();
    }
}, 50);
export class OrePlacementCollection {
    static findIndexByOreId(oreId) {
        return this.ores.findIndex((ore) => ore.typeId === oreId);
    }
    static filterOresByDimension(dimensionName) {
        return this.ores.filter(ore => dimensionName == ore.allowedDimension);
    }
}
OrePlacementCollection.ores = [
    //
    new OrePlacement(`nps_mot:sulfur_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new UniformOreDensity([5, 123]), [6, 10]),
    new OrePlacement(`nps_mot:nether_orichalc_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new UniformOreDensity([5, 123]), [5, 8]),
    //
    new OrePlacement(`nps_mot:adamantite_ore`, MinecraftDimensionTypes.nether, OreRarity.UNCOMMON, new CustomOreDensity([5, 123], [
        { depth: 0, density: 0 },
        { depth: 16, density: 0.5 },
        { depth: 32, density: 0.0 },
        { depth: 96, density: 0.0 },
        { depth: 112, density: 0.5 },
        { depth: 128, density: 0 }
    ]), [2, 5]),
    //
    new OrePlacement(`nps_mot:bismuthinite_ore`, MinecraftDimensionTypes.nether, OreRarity.UNCOMMON, new TriangularOreDensity([5, 123], 114), [2, 5]),
    new OrePlacement(`nps_mot:peridot_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new TriangularOreDensity([5, 123], 107), [4, 5]),
    new OrePlacement(`nps_mot:benitoite_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new TriangularOreDensity([5, 123], 92), [2, 5]),
    new OrePlacement(`nps_mot:fire_opal_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new TriangularOreDensity([5, 123], 63), [3, 4]),
    new OrePlacement(`nps_mot:garnet_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new TriangularOreDensity([5, 123], 34), [3, 5]),
    new OrePlacement(`nps_mot:spinel_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new TriangularOreDensity([5, 123], 19), [2, 3]),
    new OrePlacement(`nps_mot:lazurite_ore`, MinecraftDimensionTypes.nether, OreRarity.VERY_COMMON, new TriangularOreDensity([5, 123], 12), [3, 5]),
    //
    new OrePlacement(`nps_mot:cassiterite_ore`, MinecraftDimensionTypes.overworld, OreRarity.COMMON, new TriangularOreDensity([32, 104], 48), [6, 12]),
    new OrePlacement(`nps_mot:aluminium_ore`, MinecraftDimensionTypes.overworld, OreRarity.COMMON, new TriangularOreDensity([0, 128], 48), [4, 8]),
    new OrePlacement(`nps_mot:amber_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-8, 88]), [2, 4]),
    new OrePlacement(`nps_mot:black_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 16], 8), [2, 4]),
    new OrePlacement(`nps_mot:black_opal_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 24]), [2, 4]),
    new OrePlacement(`nps_mot:blue_garnet_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-8, 80]), [1, 2]),
    new OrePlacement(`nps_mot:chrome_ore`, MinecraftDimensionTypes.overworld, OreRarity.COMMON, new TriangularOreDensity([16, 96]), [4, 6]),
    new OrePlacement(`nps_mot:citrine_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([0, 72]), [2, 4]),
    new OrePlacement(`nps_mot:cobalt_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-16, 80]), [2, 4]),
    new OrePlacement(`nps_mot:green_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 16], 0), [2, 4]),
    new OrePlacement(`nps_mot:iridium_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-24, 80]), [2, 6]),
    new OrePlacement(`nps_mot:lead_ore`, MinecraftDimensionTypes.overworld, OreRarity.COMMON, new TriangularOreDensity([8, 112]), [6, 8]),
    new OrePlacement(`nps_mot:nickel_ore`, MinecraftDimensionTypes.overworld, OreRarity.COMMON, new TriangularOreDensity([24, 80]), [4, 8]),
    new OrePlacement(`nps_mot:opal_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-12, 64]), [1, 2]),
    new OrePlacement(`nps_mot:osmium_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-64, 0]), [1, 4]),
    new OrePlacement(`nps_mot:orange_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 16], -8), [2, 4]),
    new OrePlacement(`nps_mot:orichalc_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-48, 48]), [5, 10]),
    new OrePlacement(`nps_mot:palladium_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-16, 72]), [2, 6]),
    new OrePlacement(`nps_mot:platinum_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-32, 72]), [3, 5]),
    new OrePlacement(`nps_mot:pink_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 16], -16), [2, 4]),
    new OrePlacement(`nps_mot:purple_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 16], -24), [2, 4]),
    new OrePlacement(`nps_mot:rhodium_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-40, 64]), [3, 5]),
    new OrePlacement(`nps_mot:ruby_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-64, 24]), [1, 2]),
    new OrePlacement(`nps_mot:ruthenium_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-48, 48]), [2, 6]),
    new OrePlacement(`nps_mot:red_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-64, 16], -32), [2, 4]),
    new OrePlacement(`nps_mot:sapphire_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-64, 24]), [1, 4]),
    new OrePlacement(`nps_mot:titanium_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-64, 32]), [2, 6]),
    new OrePlacement(`nps_mot:topaz_ore`, MinecraftDimensionTypes.overworld, OreRarity.VERY_RARE, new TriangularOreDensity([-4, 64]), [1, 4]),
    new OrePlacement(`nps_mot:tungsten_ore`, MinecraftDimensionTypes.overworld, OreRarity.UNCOMMON, new TriangularOreDensity([-64, 16]), [3, 6]),
    new OrePlacement(`nps_mot:yellow_diamond_ore`, MinecraftDimensionTypes.overworld, OreRarity.RARE, new TriangularOreDensity([-64, 16], -40), [2, 4]),
];
