import { Direction, EquipmentSlot, GameMode, system, world } from "@minecraft/server";
import { decrementStack, getBlockState, getCardinalDirection, getPlayerEquipment, getRelativeBlock, setBlockState, setCardinalDirection } from "../others/my_API";
class TallPlant {
    beforeOnPlayerPlace(arg) {
        this.construct(arg);
        system.runTimeout(() => {
            decrementStack(arg.player);
        }, 1);
    }
    ;
    onPlayerDestroy(arg) {
        this.destroy(arg);
    }
    ;
    constructor() {
        this.beforeOnPlayerPlace = this.beforeOnPlayerPlace.bind(this);
        this.onPlayerDestroy = this.onPlayerDestroy.bind(this);
        if (!TallPlant.tallPlants.includes(this)) {
            TallPlant.tallPlants.push(this);
        }
    }
    getAllBlocks(block, cardinalDirection) {
        const sizeVector = this.size; // Utiliser la taille d'origine ici
        const absSizeVector = [
            Math.abs(sizeVector[0]),
            Math.abs(sizeVector[1]),
            Math.abs(sizeVector[2])
        ];
        const neighbours = [];
        // Récupérer la matrice de rotation
        const rotationMatrix = this.getRotationMatrix(cardinalDirection);
        // Boucle pour placer chaque bloc dans la zone
        for (let dz = 0; dz < absSizeVector[2]; dz++) {
            for (let dy = 0; dy < absSizeVector[1]; dy++) {
                for (let dx = 0; dx < absSizeVector[0]; dx++) {
                    // Appliquer la matrice de rotation pour déterminer les nouveaux offsets
                    const rotatedX = rotationMatrix[0][0] * dx + rotationMatrix[0][1] * dz; // X
                    const rotatedZ = rotationMatrix[1][0] * dx + rotationMatrix[1][1] * dz; // Z
                    const neighbour = block?.east(rotatedX)?.above(dy)?.north(rotatedZ);
                    neighbours.push(neighbour);
                }
            }
        }
        return neighbours;
    }
    destroy(arg) {
        const mainBlock = this.getMainBlock(arg.block, arg.destroyedBlockPermutation);
        const sizeVector = this.size; // Utiliser la taille d'origine ici
        const absSizeVector = [
            Math.abs(sizeVector[0]),
            Math.abs(sizeVector[1]),
            Math.abs(sizeVector[2])
        ];
        // Récupérer la matrice de rotation
        const rotationMatrix = this.getRotationMatrix(arg.destroyedBlockPermutation.getState("minecraft:cardinal_direction"));
        // Boucle pour placer chaque bloc dans la zone
        for (let dz = 0; dz < absSizeVector[2]; dz++) {
            for (let dy = 0; dy < absSizeVector[1]; dy++) {
                for (let dx = 0; dx < absSizeVector[0]; dx++) {
                    // Appliquer la matrice de rotation pour déterminer les nouveaux offsets
                    const rotatedX = rotationMatrix[0][0] * dx + rotationMatrix[0][1] * dz; // X
                    const rotatedZ = rotationMatrix[1][0] * dx + rotationMatrix[1][1] * dz; // Z
                    const neighbour = mainBlock?.east(rotatedX)?.above(dy)?.north(rotatedZ);
                    neighbour?.setType("minecraft:air");
                }
            }
        }
    }
    getMainBlock(destroyedBlock, destroyedBlockPermutation) {
        const cardinalDirection = destroyedBlockPermutation.getState("minecraft:cardinal_direction");
        const furniturePart = destroyedBlockPermutation.getState(this.stateName);
        const deltas = this.getOffsetsFromFurniturePart(furniturePart);
        const offsets = this.rotateOffsetsByCardinalDirection(deltas, cardinalDirection);
        const mainBlock = destroyedBlock.west(offsets[0]).below(offsets[1]).south(offsets[2]);
        return mainBlock;
    }
    getOffsetsFromFurniturePart(furniturePart) {
        // Calcul des indices inverses pour obtenir dx, dy et dz
        const sizeVector = this.size; // Utiliser la taille d'origine ici
        const absSizeVector = [
            Math.abs(sizeVector[0]),
            Math.abs(sizeVector[1]),
            Math.abs(sizeVector[2])
        ];
        const dx = (furniturePart - 1) % absSizeVector[0];
        const dy = Math.floor((furniturePart - 1) / absSizeVector[0]) % absSizeVector[1];
        const dz = Math.floor((furniturePart - 1) / (absSizeVector[0] * absSizeVector[1]));
        return [dx, dy, dz];
    }
    rotateOffsetsByCardinalDirection(deltas, cardinalDirection) {
        const dx = deltas[0];
        const dz = deltas[2];
        // Obtenir la matrice de rotation pour la direction donnée
        const rotationMatrix = this.getRotationMatrix(cardinalDirection);
        // Appliquer la rotation pour ajuster dx et dz
        const rotatedX = rotationMatrix[0][0] * dx + rotationMatrix[0][1] * dz;
        const rotatedZ = rotationMatrix[1][0] * dx + rotationMatrix[1][1] * dz;
        return [rotatedX, deltas[1], rotatedZ];
    }
    checkSpace(block, cardinalDirection) {
        const sizeVector = this.size; // Utiliser la taille d'origine ici
        const absSizeVector = [
            Math.abs(sizeVector[0]),
            Math.abs(sizeVector[1]),
            Math.abs(sizeVector[2])
        ];
        // Récupérer la matrice de rotation
        const rotationMatrix = this.getRotationMatrix(cardinalDirection);
        // Boucle pour placer chaque bloc dans la zone
        for (let dz = 0; dz < absSizeVector[2]; dz++) {
            for (let dy = 0; dy < absSizeVector[1]; dy++) {
                for (let dx = 0; dx < absSizeVector[0]; dx++) {
                    // Appliquer la matrice de rotation pour déterminer les nouveaux offsets
                    const rotatedX = rotationMatrix[0][0] * dx + rotationMatrix[0][1] * dz; // X
                    const rotatedZ = rotationMatrix[1][0] * dx + rotationMatrix[1][1] * dz; // Z
                    const neighbour = block?.east(rotatedX)?.above(dy)?.north(rotatedZ);
                    // Vérifie si le bloc est de l'air et non liquide
                    if (neighbour == undefined || !neighbour.isAir || neighbour.isLiquid) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    cancel(arg) {
        const player = arg.player;
        const block = arg.block;
        if (player != undefined) {
            player.onScreenDisplay.setActionBar({
                translate: 'Not Enough Space'
            });
            arg.cancel = true;
        }
        block.setType("minecraft:air");
    }
    construct(arg) {
        const isTallPlant = this.isTallPlant(arg.permutationToPlace.getTags());
        if (!isTallPlant) {
            return;
        }
        const block = arg.block;
        const cardinalDirection = arg.permutationToPlace.getState("minecraft:cardinal_direction");
        const canBePlaced = this.checkSpace(block, cardinalDirection);
        if (!canBePlaced) {
            this.cancel(arg);
            return;
        }
        else {
            this.placeBlocks(arg);
        }
    }
    getRotationMatrix(direction) {
        switch (direction) {
            case 'east':
                return [[0, 1], [-1, 0]]; // Rotation de 90 degrés dans le sens inverse
            case 'west':
                return [[0, -1], [1, 0]]; // Rotation de 90 degrés dans le sens des aiguilles d'une montre
            case 'north':
                return [[1, 0], [0, 1]]; // Pas de rotation
            case 'south':
                return [[-1, 0], [0, -1]]; // Rotation de 180 degrés
            default:
                return [[1, 0], [0, 1]]; // Valeur par défaut (pas de rotation)
        }
    }
    placeBlocks(arg) {
        arg.permutationToPlace = arg.permutationToPlace.withState(this.stateName, 1);
        const cardinalDirection = arg.permutationToPlace.getState("minecraft:cardinal_direction") ?? Direction.North;
        const sizeVector = this.size; // Utiliser la taille d'origine ici
        const absSizeVector = [
            Math.abs(sizeVector[0]),
            Math.abs(sizeVector[1]),
            Math.abs(sizeVector[2])
        ];
        const block = arg.block;
        // Récupérer la matrice de rotation
        const rotationMatrix = this.getRotationMatrix(cardinalDirection);
        // Boucle pour placer chaque bloc dans la zone
        for (let dz = 0; dz < absSizeVector[2]; dz++) {
            for (let dy = 0; dy < absSizeVector[1]; dy++) {
                for (let dx = 0; dx < absSizeVector[0]; dx++) {
                    // Appliquer la matrice de rotation pour déterminer les nouveaux offsets
                    const rotatedX = rotationMatrix[0][0] * dx + rotationMatrix[0][1] * dz; // X
                    const rotatedZ = rotationMatrix[1][0] * dx + rotationMatrix[1][1] * dz; // Z
                    const neighbour = block?.east(rotatedX)?.above(dy)?.north(rotatedZ);
                    const furniturePart = 1 + dx + (dy * absSizeVector[0]) + (dz * absSizeVector[0] * absSizeVector[1]);
                    //console.warn(`bone: ${dx}_${dy}_${dz} : ${furniturePart}`)
                    // Vérification si le voisin est défini
                    if (neighbour) {
                        neighbour.setType(arg.permutationToPlace.type.id);
                        setCardinalDirection(neighbour, cardinalDirection);
                        setBlockState(neighbour, this.stateName, furniturePart);
                    }
                }
            }
        }
    }
    isTallPlant(tags) {
        return tags.includes(this.tagName);
    }
}
TallPlant.tallPlants = [];
class Doubleplant extends TallPlant {
    constructor() {
        super();
        this.componentName = "nps_tbe:c9449d58";
        this.tagName = "nps_tbe:c9449d58";
        this.stateName = "nps_tbe:height";
        this.size = [1, 2, 1];
    }
}
class Triplant extends TallPlant {
    constructor() {
        super();
        this.componentName = "nps_tbe:a30bd121";
        this.tagName = "nps_tbe:a30bd121";
        this.stateName = "nps_tbe:height";
        this.size = [1, 3, 1];
    }
}
class Tetraplant extends TallPlant {
    constructor() {
        super();
        this.componentName = "nps_tbe:7733d8f3";
        this.tagName = "nps_tbe:7733d8f3";
        this.stateName = "nps_tbe:height";
        this.size = [1, 4, 1];
    }
}
class Pentaplant extends TallPlant {
    constructor() {
        super();
        this.componentName = "nps_tbe:0e439cf9";
        this.tagName = "nps_tbe:0e439cf9";
        this.stateName = "nps_tbe:height";
        this.size = [1, 5, 1];
    }
}
class Hexaplant extends TallPlant {
    constructor() {
        super();
        this.componentName = "nps_tbe:f3604b13";
        this.tagName = "nps_tbe:f3604b13";
        this.stateName = "nps_tbe:height";
        this.size = [1, 6, 1];
    }
}
class ChorusPlant {
    static initialize(event) { event.blockComponentRegistry.registerCustomComponent(this.componentName, new this()); }
    onTick(arg) {
        ChorusPlant.updateBlock(arg.block, 2);
        setBlockState(arg.block, 'nps_tbe:initial_tick', false);
    }
    ;
    static getConnectableBlockIds() {
        return [
            "minecraft:end_stone",
            "nps_tbe:enderglow_plant",
            "nps_tbe:enderglow_flower",
            "nps_tbe:amanita_soil",
            "nps_tbe:chlorophos_soil",
            "nps_tbe:cortinarius_soil",
            "nps_tbe:lactarius_soil",
            "nps_tbe:russula_soil",
            "nps_tbe:umbromycelia_soil"
        ];
    }
    static isPlant(block) {
        if (block == undefined) {
            return false;
        }
        if (block.getTags().includes(this.componentName)) {
            return true;
        }
        return false;
    }
    static shouldConnectWith(neighbor) {
        return this.isPlant(neighbor) || ChorusFlower.isFlower(neighbor) || this.getConnectableBlockIds().includes(neighbor.typeId);
    }
    static updateBlock(plant, depth) {
        if (depth <= 0)
            return;
        const cardinalDirection = getCardinalDirection(plant);
        const directions = ['west', 'east', 'north', 'south', 'up', 'down'];
        for (const direction of directions) {
            const neighbor = getRelativeBlock(plant, cardinalDirection, direction);
            if (!neighbor) {
                continue;
            }
            if (this.shouldConnectWith(neighbor)) {
                plant.setPermutation(plant.permutation.withState(`nps_tbe:${direction}_connected`, true));
                if (depth > 0 && ChorusPlant.isPlant(neighbor)) {
                    this.updateBlock(neighbor, depth - 1);
                }
            }
            else {
                plant.setPermutation(plant.permutation.withState(`nps_tbe:${direction}_connected`, false));
            }
        }
    }
}
ChorusPlant.componentName = "nps_tbe:b29193ab";
class ChorusFlower {
    static initialize(event) { event.blockComponentRegistry.registerCustomComponent(this.componentName, new this()); }
    onTick(arg) {
        if (ChorusFlower.stopGrowChance < Math.random()) {
            ChorusFlower.grow(arg.block);
        }
        else {
            ChorusFlower.stopGrow(arg.block);
        }
    }
    ;
    static getConnectableBlockIds() {
        return [
            "minecraft:end_stone"
        ];
    }
    static isFlower(block) {
        if (block == undefined) {
            return false;
        }
        if (block.getTags().includes(this.componentName)) {
            return true;
        }
        return false;
    }
    static shouldConnectWith(neighbor) {
        return ChorusPlant.isPlant(neighbor) || this.isFlower(neighbor) || this.getConnectableBlockIds().includes(neighbor.typeId);
    }
    static updateBlock(plant, depth) {
        if (depth <= 0)
            return;
        const directions = ['west', 'east', 'north', 'south', 'up', 'down'];
        for (const direction of directions) {
            const neighbor = getRelativeBlock(plant, 'north', direction);
            if (!neighbor) {
                continue;
            }
            if (this.shouldConnectWith(neighbor)) {
                plant.setPermutation(plant.permutation.withState(`nps_tbe:${direction}_connected`, true));
                if (depth > 0 && ChorusPlant.isPlant(neighbor)) {
                    this.updateBlock(neighbor, depth - 1);
                }
            }
        }
    }
    static getTargetBlock(flower, direction) {
        switch (direction) {
            case 'up':
                return flower.above();
            case 'east':
                return flower.east();
            case 'west':
                return flower.west();
            case 'north':
                return flower.north();
            case 'south':
                return flower.south();
            default:
                return flower.above();
        }
    }
    static grow(flower) {
        const neighbors = [
            flower.west(),
            flower.east(),
            flower.north(),
            flower.south()
        ];
        // Si une plante adjacente existe, on pousse vers le haut
        if (neighbors.some(neighbor => neighbor?.typeId === this.plantIdentifier)) {
            const flowerAbove = flower.above();
            if (flowerAbove?.isAir) {
                flowerAbove.setType(flower.typeId);
                flower.setType(this.plantIdentifier);
                ChorusFlower.updateBlock(flower, 2);
            }
        }
        else {
            if (ChorusFlower.branchingChance > Math.random()) {
                // Tentative d'embranchement - faire apparaître deux fleurs dans des directions opposées
                // Choisir aléatoirement entre l'axe est-ouest ou nord-sud
                const isEastWestBranch = Math.random() > 0.5;
                let direction1;
                let direction2;
                if (isEastWestBranch) {
                    // Axe est-ouest
                    direction1 = flower.east();
                    direction2 = flower.west();
                }
                else {
                    // Axe nord-sud
                    direction1 = flower.north();
                    direction2 = flower.south();
                }
                // Vérifier si les deux directions sont de l'air et disponibles pour planter une fleur
                if (direction1?.isAir && direction2?.isAir) {
                    // Placer la première fleur dans la première direction
                    direction1.setType(flower.typeId);
                    // Placer la deuxième fleur dans la direction opposée
                    direction2.setType(flower.typeId);
                    // Mettre à jour le bloc d'origine et les fleurs créées pour connecter les embranchements
                    flower.setType(this.plantIdentifier);
                    ChorusFlower.updateBlock(flower, 2);
                }
            }
            else {
                // Aucune plante adjacente - pousser dans une direction aléatoire configurée
                const direction = this.chooseDirection();
                const targetBlock = this.getTargetBlock(flower, direction);
                if (targetBlock && targetBlock.isAir) {
                    targetBlock.setType(flower.typeId);
                    flower.setType(this.plantIdentifier);
                    ChorusFlower.updateBlock(flower, 2);
                }
            }
        }
    }
    static stopGrow(flower) {
        if (ChorusFlower.isFlower(flower)) {
            flower.setPermutation(flower.permutation.withState("nps_tbe:can_grow", false));
        }
    }
    static chooseDirection() {
        const rand = Math.random();
        let cumulativeProbability = 0;
        for (const [direction, probability] of Object.entries(this.growthProbabilities)) {
            cumulativeProbability += probability;
            if (rand < cumulativeProbability) {
                return direction;
            }
        }
        return 'up';
    }
}
ChorusFlower.componentName = "nps_tbe:42aff020";
ChorusFlower.plantIdentifier = "nps_tbe:enderglow_plant"; //..
ChorusFlower.stopGrowChance = 0.25;
ChorusFlower.branchingChance = 0.025;
ChorusFlower.growthProbabilities = {
    up: 0.75,
    east: 0.06275,
    west: 0.06275,
    north: 0.06275,
    south: 0.06275
};
class BerryBush {
    static initialize(event) { event.blockComponentRegistry.registerCustomComponent(this.componentName, new this()); }
    onRandomTick(arg) {
        const block = arg.block;
        const growthChance = 1 / 3;
        if (Math.random() > growthChance) {
            return;
        }
        const aboveBlock = arg.block.above();
        if (aboveBlock == undefined) {
            return;
        }
        if (!aboveBlock.isAir) {
            return;
        }
        const growth = getBlockState(block, "nps_tbe:growth_stage");
        if (growth < 3) {
            setBlockState(block, "nps_tbe:growth_stage", growth + 1);
        }
    }
    ;
    onPlayerInteract(arg) {
        const player = arg.player;
        if (player == undefined) {
            return;
        }
        const itemStack = getPlayerEquipment(player).getEquipment(EquipmentSlot.Mainhand);
        if (itemStack == undefined) {
            BerryBush.harvest(arg);
        }
        else if (itemStack.typeId == "minecraft:bone_meal") {
            BerryBush.onBoneMealUse(arg);
        }
    }
    static onBoneMealUse(arg) {
        const block = arg.block;
        const growth = getBlockState(block, "nps_tbe:growth_stage");
        if (growth < 3) {
            const player = arg.player;
            if (player == undefined) {
                return;
            }
            if (player.getGameMode() == GameMode.creative) {
                setBlockState(block, "nps_tbe:growth_stage", 3);
                arg.dimension.playSound("item.bone_meal.use", block.center());
                arg.dimension.spawnParticle("minecraft:crop_growth_emitter", block.center());
            }
            else {
                decrementStack(player, 1);
                arg.dimension.playSound("item.bone_meal.use", block.center());
                const growthChance = 1 / 3;
                if (Math.random() > growthChance) {
                    return;
                }
                setBlockState(block, "nps_tbe:growth_stage", growth + 1);
                arg.dimension.spawnParticle("minecraft:crop_growth_emitter", block.center());
            }
        }
    }
    static harvest(arg) {
        const block = arg.block;
        const growth = getBlockState(block, "nps_tbe:growth_stage");
        if (growth == 3) {
            setBlockState(block, "nps_tbe:growth_stage", 1);
            //loot
            //sound
        }
    }
}
BerryBush.componentName = "nps_tbe:a0929ac1";
world.beforeEvents.worldInitialize.subscribe(event => {
    const doubleplant = new Doubleplant();
    const triplant = new Triplant();
    const tetraplant = new Tetraplant();
    const pentaplant = new Pentaplant();
    const hexaplant = new Hexaplant();
    for (const complexFurniture of TallPlant.tallPlants) {
        event.blockComponentRegistry.registerCustomComponent(complexFurniture.componentName, complexFurniture);
    }
});
