import { ItemStack, BlockComponentTypes } from '@minecraft/server';
import { getBlocksInRange, spawnItemAtBlock, playSoundAtBlock, removeVec3, isBlockContainer, insertLootToBlock, shrinkItemStack, spawnLootAtBlock, addVec3, getBlockState, updateBlockState } from '../../utils/utils.js';
import { getEntityHeldItem, setEntityHeldItem } from '../../utils/players.js';
import { FSItemTags, MCSounds, ADDON_ID } from '../../constants.js';
import { SieveRecipes } from '../../recipes/recipes.js';

class BasicSieveBlockComponent {
    onPlayerInteract(event) {
        const blocksInRange = getBlocksInRange(event.block, 2, false);
        for (const blocksInRangeElement of blocksInRange) {
            const blockInRange = event.block.dimension.getBlock(blocksInRangeElement);
            if (blockInRange.hasTag(FSItemTags.SIEVE)) {
                onSieveInteract(blockInRange, event.player, event.block);
            }
        }
    }
    onPlayerBreak(event) {
        const block = event.block;
        block.dimension;
        const activeBlockType = getBlockType(event.brokenBlockPermutation);
        const activeMeshType = getMeshType(event.brokenBlockPermutation);
        if (activeBlockType !== SieveRecipes.EMPTY_BLOCK) {
            spawnItemAtBlock(block, new ItemStack(activeBlockType.itemId, 1));
        }
        if (activeMeshType !== SieveRecipes.EMPTY_MESH) {
            spawnItemAtBlock(block, new ItemStack(activeMeshType.itemId, 1));
        }
    }
}
class AutoSieveBlockComponent extends BasicSieveBlockComponent {
    onPlayerInteract(event) {
        const blocksInRange = getBlocksInRange(event.block, 2, false);
        for (const blocksInRangeElement of blocksInRange) {
            const blockInRange = event.block.dimension.getBlock(blocksInRangeElement);
            if (blockInRange.hasTag(FSItemTags.AUTO_SIEVE)) {
                onAutoSieveInteract(blockInRange, event.player);
            }
        }
    }
    onTick(event) {
        const block = event.block;
        const meshType = getMeshType(block.permutation);
        const blockType = getBlockType(block.permutation);
        if (meshType === SieveRecipes.EMPTY_MESH) {
            setInputLocked(block, false);
            return;
        }
        if (blockType === SieveRecipes.EMPTY_BLOCK) {
            processInventoryInteraction(block);
            return;
        }
        let newShiftStage = getSiftStage(block.permutation);
        if (!isInputLocked(block.permutation)) {
            const shiftStage = getSiftStage(block.permutation);
            newShiftStage = getNextSiftStage(shiftStage);
            setSiftStage(block, newShiftStage);
            playSoundAtBlock(block, MCSounds.BRUSH_GENERIC);
        }
        if (newShiftStage === SiftStage.EMPTY) {
            const blockBelow = block.dimension.getBlock(removeVec3(block.location, 0, 1, 0));
            if (blockBelow && isBlockContainer(blockBelow)) {
                const lootTable = getLootTable(block);
                const added = insertLootToBlock(blockBelow, lootTable);
                if (!added) {
                    console.warn("Issue with adding loot table: " + lootTable);
                    setInputLocked(block, true);
                    return;
                }
                setBlockType(block, SieveRecipes.EMPTY_BLOCK);
                return;
            }
            else {
                setInputLocked(block, true);
            }
            return;
        }
        return;
    }
}
function onAutoSieveInteract(block, player) {
    if (isInputLocked(block.permutation)) {
        handleSiftingFished(block, block);
        return;
    }
    onSieveInteract(block, player, block);
}
function onSieveInteract(block, player, lootSpawnBlock) {
    block.dimension;
    const activeBlockType = getBlockType(block.permutation);
    const activeMeshType = getMeshType(block.permutation);
    // Edge case for something breaking
    if (activeBlockType !== SieveRecipes.EMPTY_BLOCK && activeMeshType === SieveRecipes.EMPTY_MESH) {
        spawnItemAtBlock(block, new ItemStack(activeBlockType.itemId, 1));
        setBlockType(block, SieveRecipes.EMPTY_BLOCK);
    }
    if (activeBlockType !== SieveRecipes.EMPTY_BLOCK) {
        const shiftStage = getSiftStage(block.permutation);
        const newShiftStage = getNextSiftStage(shiftStage);
        setSiftStage(block, newShiftStage);
        playSoundAtBlock(block, MCSounds.BRUSH_GENERIC);
        if (newShiftStage === SiftStage.EMPTY) {
            handleSiftingFished(block, lootSpawnBlock);
            return;
        }
        return;
    }
    const playerHeldItem = getEntityHeldItem(player);
    // Check to see if they are trying to remove the mesh
    if (player.isSneaking && !playerHeldItem && activeMeshType !== SieveRecipes.EMPTY_MESH) {
        setMeshType(block, SieveRecipes.EMPTY_MESH);
        setSiftStage(block, SiftStage.EMPTY);
        spawnItemAtBlock(block, new ItemStack(activeMeshType.itemId, 1));
        return;
    }
    if (!playerHeldItem) {
        return;
    }
    const meshType = SieveRecipes.getMeshTypeByItemId(playerHeldItem.typeId);
    // If held item is not a mesh try and add block
    if (activeMeshType != SieveRecipes.EMPTY_MESH && !meshType) {
        const blockType = SieveRecipes.getBlockTypeByItemId(playerHeldItem.typeId);
        if (!blockType) {
            return;
        }
        playSoundAtBlock(block, MCSounds.BRUSH_GENERIC);
        setBlockType(block, blockType);
        setSiftStage(block, SiftStage.STAGE_1);
        setEntityHeldItem(player, shrinkItemStack(playerHeldItem));
        return;
    }
    if (activeMeshType === SieveRecipes.EMPTY_MESH && meshType) {
        setMeshType(block, meshType);
        playSoundAtBlock(block, MCSounds.STEP_CLOTH);
        const newHeldItem = shrinkItemStack(playerHeldItem);
        setEntityHeldItem(player, newHeldItem);
    }
}
function handleSiftingFished(block, lootSpawnBlock) {
    const lootTable = getLootTable(block);
    const added = spawnLootAtBlock(lootSpawnBlock, lootTable);
    if (!added) {
        console.warn("Can't find loot table for: " + lootTable);
    }
    setBlockType(block, SieveRecipes.EMPTY_BLOCK);
}
function processInventoryInteraction(block) {
    setInputLocked(block, false);
    const blockAbove = block.dimension.getBlock(addVec3(block.location, 0, 1, 0));
    if (!blockAbove) {
        return;
    }
    const blockInventoryComponent = blockAbove.getComponent(BlockComponentTypes.Inventory);
    if (!blockInventoryComponent) {
        return;
    }
    const container = blockInventoryComponent.container;
    if (!container) {
        return;
    }
    const size = container.size;
    for (let i = 0; i < size; i++) {
        const itemStack = container.getItem(i);
        if (!itemStack) {
            continue;
        }
        const blockTypeCandidate = SieveRecipes.getBlockTypeByItemId(itemStack.typeId);
        if (!blockTypeCandidate) {
            continue;
        }
        const newInventoryStack = shrinkItemStack(itemStack);
        container.setItem(i, newInventoryStack);
        setBlockType(block, blockTypeCandidate);
        setSiftStage(block, SiftStage.STAGE_1);
        return;
    }
}
function isInputLocked(blockPermutation) {
    return getBlockState(blockPermutation, SieveBlockStates.IS_BLOCKED);
}
function setInputLocked(block, isBlocked) {
    updateBlockState(block, SieveBlockStates.IS_BLOCKED, isBlocked);
}
function getLootTable(block) {
    const meshType = getMeshType(block.permutation);
    const blockType = getBlockType(block.permutation);
    if (!meshType || meshType === SieveRecipes.EMPTY_MESH) {
        return undefined;
    }
    if (!blockType || blockType === SieveRecipes.EMPTY_BLOCK) {
        return undefined;
    }
    return ADDON_ID + "/sieves/" + meshType.stateId + "/" + blockType.stateId;
}
function setMeshType(block, meshType) {
    updateBlockState(block, SieveBlockStates.MESH_TYPE, meshType.stateId);
}
function setBlockType(block, blockType) {
    updateBlockState(block, SieveBlockStates.BLOCK_TYPE, blockType.stateId);
}
function getBlockType(blockPermutation) {
    return SieveRecipes.getBlockTypeFromState(getBlockState(blockPermutation, SieveBlockStates.BLOCK_TYPE));
}
function getMeshType(blockPermutation) {
    return SieveRecipes.getMeshTypeFromState(getBlockState(blockPermutation, SieveBlockStates.MESH_TYPE));
}
function setSiftStage(block, shiftStage) {
    updateBlockState(block, SieveBlockStates.SIFT_STAGE, shiftStage);
}
function getSiftStage(blockPermutation) {
    return getBlockState(blockPermutation, SieveBlockStates.SIFT_STAGE);
}
var SiftStage;
(function (SiftStage) {
    SiftStage[SiftStage["EMPTY"] = 0] = "EMPTY";
    SiftStage[SiftStage["STAGE_1"] = 1] = "STAGE_1";
    SiftStage[SiftStage["STAGE_2"] = 2] = "STAGE_2";
    SiftStage[SiftStage["STAGE_3"] = 3] = "STAGE_3";
    SiftStage[SiftStage["STAGE_4"] = 4] = "STAGE_4";
})(SiftStage || (SiftStage = {}));
function getNextSiftStage(shiftStage) {
    if (shiftStage === SiftStage.STAGE_4) {
        return SiftStage.EMPTY;
    }
    return shiftStage + 1;
}
var SieveBlockStates;
(function (SieveBlockStates) {
    SieveBlockStates["BLOCK_TYPE"] = "ftb_sieves:block_type";
    SieveBlockStates["MESH_TYPE"] = "ftb_sieves:mesh_type";
    SieveBlockStates["SIFT_STAGE"] = "ftb_sieves:sift_stage";
    SieveBlockStates["IS_BLOCKED"] = "ftb_sieves:is_blocked";
})(SieveBlockStates || (SieveBlockStates = {}));

export { AutoSieveBlockComponent, BasicSieveBlockComponent, SieveBlockStates, SiftStage, getNextSiftStage };
