import { Block, system, ItemStack, EquipmentSlot, Player, Direction, EntityEquippableComponent} from "@minecraft/server"
import { multiClicking, testing } from "../../utils/systemHelper";
import { CustomBlockTags } from "../utils/blockTags";
import { AnimationIds, CommandEquipmentSlots, Piki, SoundsIds } from "../../utils/typingConstants";
import { BlockStateNames } from "../utils/blockConstants";
import { BlockHelper as BHelper } from "../utils/blockHelpers";
import { CommandHelper as CHelper } from "../../utils/commandHelper";
import { ItemHelper } from "../../items/utils/itemHelper";

export const CUSTOM_HEADS = {
    _typeIdSuffix: [
        "_head",
        "_skull"
    ],

    /**
     * Checks if the item using is a custom head.
     * This method prevents user from multicliking.
     * @param {string} itemStackId 
     * @param {Player} source 
     * @returns 
     */
    isUsing: function(itemStackId) {
        const expectedHeadID = BHelper.getPlacerRealBlockId(itemStackId)
        if (!expectedHeadID.startsWith(Piki.namespace) || !this._typeIdSuffix.some(s => expectedHeadID.endsWith(s))) return false
        return true
    },

    /**
     * 
     * @param {Block} block 
     * @param {Player} source 
     * @param {Direction} blockFace 
     * @param {ItemStack} itemStack 
     * @param {EntityEquippableComponent} equippable
     */
    useHead: function(block, source, blockFace, itemStack, equippable) {
        const targetBlock = BHelper.getBlockFromFace(block, blockFace)
        const blockNoHead = CustomBlockTags.notHeadAttachable(block)
        const blockNoHeadOnSide = CustomBlockTags.notHeadAttachableOnSide(block)
        const blockRequiresSneak = CustomBlockTags.isSneakRequired(block)
        const blockRequiresSneakOnSide = CustomBlockTags.isSneakRequiredOnSide(block)
        const usedBlockWillDestroy = CustomBlockTags.replaceAsSelf(block) 
        const placedBlock = usedBlockWillDestroy ? block : targetBlock;
        const targetBlockWillDestroy = CustomBlockTags.replaceAsTarget(placedBlock)
        const placeHeadAsUp = CustomBlockTags.placeHeadAsUp(placedBlock)
        const isPlacingOnSide = blockFace != Direction.Down && blockFace != Direction.Up
        
        if (placeHeadAsUp) blockFace = Direction.Up;

        const unplaceable = 
            (blockNoHeadOnSide && isPlacingOnSide) || 
            blockNoHead || 
            (blockRequiresSneak && !source.isSneaking) || (blockRequiresSneakOnSide && isPlacingOnSide) ||
            !targetBlockWillDestroy ||
            blockFace == Direction.Down

        if (!unplaceable) this.placeBlock(blockFace, itemStack, source, placedBlock)
        else this.wearAsHead(itemStack, source, equippable)
    },

    /**
     * @param {ItemStack} itemStack
     * @param {Player} source
     * @param {EntityEquippableComponent} equippable
     */
    wearAsHead(itemStack, source, equippable) {
        const armorHead = equippable.getEquipment(EquipmentSlot.Head)
        if (armorHead) return;
        system.run(() => {
            if (multiClicking(source)) return;
            // testing.success(source.name, "successfully set as helmet")
            source.playSound(SoundsIds.armorEquipGeneric, {location: source.location, volume: 1, pitch: 1})
            const command = CHelper.generateReplaceItemCommand(itemStack.typeId, CommandEquipmentSlots.slotArmorHead);
            source.runCommand(command)
            ItemHelper.clearPlayerItem(source, EquipmentSlot.Mainhand)
        })
    },

    /**
     * 
     * @param {Direction} blockFace 
     * @param {ItemStack} itemStack 
     * @param {Player} source 
     * @param {Block} placedBlock 
     */
    placeBlock(blockFace, itemStack, source, placedBlock) {
        if (multiClicking(source)) return;
        // testing.success(source.name, "successfully placed head")
        const blockFaceLower = blockFace.toLowerCase();
        const expectedHeadId = BHelper.getPlacerRealBlockId(itemStack.typeId)

        const playerRotation = source.getRotation().y;
        const positive_head_rot = playerRotation + 360 * (playerRotation != Math.abs(playerRotation))
        const blockRotation = Math.round(positive_head_rot / 22.5);
        const rotationValue = blockRotation != 16 ? blockRotation : 0;

        system.run(() => {
            source.playAnimation(AnimationIds.customSwing) 
            source.playSound(SoundsIds.digStone, {location: source.location, volume: 1, pitch: 0.8})
            placedBlock.setType(expectedHeadId)

            const permutationObject = {}
            if (blockFace == Direction.Up) {
                permutationObject[BlockStateNames.customRotation] = rotationValue
                permutationObject[BlockStateNames.blockFace] = blockFaceLower 
            } else {
                permutationObject[BlockStateNames.blockFace] = blockFaceLower 
            }                
            const permutation = placedBlock.permutation.constructor.resolve(placedBlock.typeId, permutationObject)
            placedBlock.setPermutation(permutation)
        
            ItemHelper.clearPlayerItem(source, EquipmentSlot.Mainhand)
        })
    }
}