import { BlockPermutation, world } from "@minecraft/server";
import { namespace } from "../../../config/_config";
export class CropSpawnBlock {
    // Constructor to initialize a CropSpawnBlock with its properties
    constructor(config) {
        this.config = config;
    }
    // Method to register a CropSpawnBlock
    static registerBehavior(config) {
        const block = new CropSpawnBlock(config);
        CropSpawnBlock.blocks.push(block);
    }
    // Initialization method to register the custom block component and handle spawning logic on random ticks
    static initialize() {
        world.beforeEvents.worldInitialize.subscribe((event) => {
            event.blockComponentRegistry.registerCustomComponent(`${namespace}:spawn_block`, {
                onRandomTick: (event) => {
                    const spawnBlock = CropSpawnBlock.getSpawnBlockByIdentifier(event.block.typeId);
                    if (spawnBlock) {
                        CropSpawnBlock.handleRandomTick(event, spawnBlock); // Handle spawning on random tick
                    }
                },
            });
        });
    }
    // Method to handle the random tick event and spawn blocks if conditions are met
    static handleRandomTick(event, cropBlock) {
        // Check if any adjacent blocks match the type that should be spawned
        const adjacentSpawnBlocks = CropSpawnBlock.getAdjacentBlocks(event.block, cropBlock.config.directions).filter((block) => block?.typeId === cropBlock.config.block);
        // If no adjacent blocks match or the repeat condition is met, proceed to spawn the block
        if (adjacentSpawnBlocks.length === 0 || cropBlock.config.repeat) {
            CropSpawnBlock.spawnBlock(event, cropBlock);
        }
    }
    // Method to spawn a block adjacent to the source block
    static spawnBlock(event, cropBlock) {
        // Get adjacent blocks based on the allowed directions and check if they are replaceable
        const adjacentBlocks = CropSpawnBlock.getAdjacentBlocks(event.block, cropBlock.config.directions).filter((block) => {
            if (cropBlock.config.replaceableBlocks.includes(block.typeId)) {
                if (cropBlock.config.gravity) {
                    const blockBelow = block.below();
                    return blockBelow && !cropBlock.config.replaceableBlocks.includes(blockBelow.typeId); // Check for gravity conditions
                }
                return true;
            }
            return false;
        });
        // If any valid adjacent blocks are found, choose one randomly
        if (adjacentBlocks.length > 0) {
            const selectedBlock = adjacentBlocks[Math.floor(Math.random() * adjacentBlocks.length)];
            // Build the block permutation (its state configuration) from the withStates array
            const states = {};
            cropBlock.config.withStates?.forEach(({ state, value }) => {
                states[state] = value; // Add each state-value pair to the state object
            });
            // Automatically determine the facing direction based on the relative position of the selected block
            const facing = CropSpawnBlock.getFacingDirection(event.block, selectedBlock);
            if (facing) {
                states[`${namespace}:facing`] = facing;
            }
            // Set the block in the selected position with the defined states
            try {
                selectedBlock.setPermutation(BlockPermutation.resolve(cropBlock.config.block, states));
            }
            catch {
                selectedBlock.setPermutation(BlockPermutation.resolve(cropBlock.config.block));
            }
        }
    }
    // Method to determine the correct facing direction based on the relative position of two blocks
    static getFacingDirection(sourceBlock, targetBlock) {
        const deltaX = targetBlock.location.x - sourceBlock.location.x;
        const deltaY = targetBlock.location.y - sourceBlock.location.y;
        const deltaZ = targetBlock.location.z - sourceBlock.location.z;
        // Determine direction based on the relative position between the two blocks
        if (deltaY === 1)
            return "up";
        if (deltaY === -1)
            return "down";
        if (deltaZ === 1)
            return "south";
        if (deltaZ === -1)
            return "north";
        if (deltaX === 1)
            return "east";
        if (deltaX === -1)
            return "west";
        return undefined; // Return undefined if the position doesn't indicate a direction
    }
    // Method to get adjacent blocks based on the directions specified
    static getAdjacentBlocks(block, directions) {
        // Map each direction to the corresponding block position (above, below, etc.)
        return directions
            .map((direction) => {
            switch (direction) {
                case "up":
                    return block.above();
                case "down":
                    return block.below();
                case "north":
                    return block.north();
                case "south":
                    return block.south();
                case "west":
                    return block.west();
                case "east":
                    return block.east();
            }
        })
            .filter((block) => block !== undefined); // Ensure non-null blocks are returned
    }
    // Method to find a CropSpawnBlock by its ID
    static getSpawnBlockByIdentifier(id) {
        return CropSpawnBlock.blocks.find((block) => block.config.id === id); // Return the block if found
    }
}
CropSpawnBlock.blocks = [];
