import { BlockComponentTypes, Direction, MolangVariableMap, system, world } from "@minecraft/server";
import { directionToVector, flipDir, getAttachFace, getCardinalDirection, isAxisVertical, isSameAxis, offsetPos, setAttachFace, setCardinalDirection, vectorToDirection, } from "../util/dirUtils";
import { isBlockSolid } from "../util/miscUtils";
import { blockEntityManager } from "../blockEntitiesManager";
import { isUnPushable, needsSolidBlockBehind } from "../util/blockProperties";
import { rotateVector, vecCross, vecNormalize } from "../util/mthUtils";
import { SuppItems } from "../reg/suppItems";
import { SuppParticles } from "../reg/suppParticles";
import { McBlocks } from "../reg/mcBlocks";
import { configs } from "../configs";
function shuffleInventory(block) {
    const inv = block.getComponent(BlockComponentTypes.Inventory);
    if (inv && inv.container) {
        //shuffle the inventory
        const size = inv.container.size;
        for (let i = 0; i < size; i++) {
            const j = Math.floor(Math.random() * size);
            inv.container.swapItems(i, j, inv.container);
        }
    }
}
function onRotated(block) {
    blockEntityManager.syncBlockEntityRotation(block);
    shuffleInventory(block);
}
// Main component – when used, it rotates the block's cardinal direction 90° around the face hit.
function rotateDirectional(block, face, dirGetter, dirSetter) {
    // Get the block’s current facing state.
    const currentDirection = dirGetter(block.permutation);
    if (currentDirection == undefined)
        return false;
    // If the hit face is on the same axis as the block's facing, do nothing.
    if (isSameAxis(currentDirection, face)) {
        return false;
    }
    const currentVec = directionToVector(currentDirection);
    const faceVec = directionToVector(face);
    // Rotate the current vector 90° around the face's axis.
    // When invert is true, we rotate by -90° (i.e. clockwise flag false).
    const newVec = rotateVector(currentVec, faceVec);
    const newDirection = vectorToDirection(newVec);
    if (newDirection) {
        if (needsSolidBlockBehind(block.permutation)) {
            const behindPos = offsetPos(block, flipDir(newDirection));
            if (!isBlockSolid(behindPos)) {
                return false;
            }
        }
        // Update the block's state with the new direction.
        const perm = dirSetter(block.permutation, newDirection);
        if (perm != block.permutation) {
            block.setPermutation(perm);
            onRotated(block);
            return true;
        }
    }
    return false;
}
function rotateSign(block, face) {
    const dir = block.permutation.getState("ground_sign_direction");
    if (dir == undefined)
        return false;
    const newDir = (dir + 16) % 16;
    block.setPermutation(block.permutation.withState("ground_sign_direction", newDir));
    return true;
}
function rotateRail(block, face) {
    //choose first or second depending on inverted
    //0->1 or 1->0
    //5->3->4->clean or clean->4->3->5
    //6->7->8->9 or 9->8->7->6
    const railShape = block.permutation.getState("rail_direction");
    if (railShape == undefined)
        return false;
    let newShape;
    if (railShape >= 6 && railShape <= 9) {
        // Looping cycle: 6 → 7 → 8 → 9 → 6 (or reversed if inverted)
        newShape = ((railShape - 6 + 4) % 4) + 6;
    }
    else if ([5, 3, 4, 2].includes(railShape)) {
        //get directio based off index
        //5->south 4->north
        //clean->east 3->west
        // Cycle: 5 → 3 → 4 → clean → 5 (or reversed if inverted)
        const cycle = [5, 3, 4, 2];
        const index = cycle.indexOf(railShape);
        newShape = cycle[(index + 4) % 4];
        let railDir;
        switch (newShape) {
            case 5:
                railDir = Direction.South;
                break;
            case 4:
                railDir = Direction.North;
                break;
            case 2:
                railDir = Direction.East;
                break;
            case 3:
                railDir = Direction.West;
                break;
        }
        const behindPos = offsetPos(block, railDir);
        if (!isBlockSolid(behindPos)) {
            return false;
        }
    }
    else {
        // Pair swapping: 0 ⇄ 1
        newShape = railShape ^ 1;
    }
    block.setPermutation(block.permutation.withState("rail_direction", newShape));
    return true;
}
function spawnParticle(block, face) {
    const map = new MolangVariableMap();
    const direction = directionToVector(face);
    // pick a world-up
    const up = isAxisVertical(face) ? { x: 1, y: 0, z: 0 } : { x: 0, y: 1, z: 1 };
    // compute raw cross
    const rawRight = vecCross(direction, up);
    // destructure length and normalized vector
    const [length, vec] = vecNormalize(rawRight);
    // full forward vector
    const forward = vecCross(direction, vec);
    map.setVector3("variable.right", vec);
    map.setVector3("variable.forward", forward);
    for (let i = 0; i < 4; i++) {
        map.setFloat("variable.angle_offset", i * 90);
        block.dimension.spawnParticle(SuppParticles.Rotation, block.center(), map);
    }
}
function isBlacklisted(block) {
    if (block.typeId == McBlocks.EnderChest || block.typeId == "razz_sup:ram_block" /* SuppBlocks.RamBlock */)
        return false;
    return isUnPushable(block);
}
export function performRotation(block, face, invert, canDefaultUp = false) {
    //TODO: stairs
    if (isBlacklisted(block))
        return false;
    face = invert ? flipDir(face) : face;
    let success = false;
    //fist facing dir
    success = rotateDirectional(block, face, getCardinalDirection, setCardinalDirection);
    if (!success) {
        success = rotateDirectional(block, face, getAttachFace, setAttachFace);
    }
    if (!success && canDefaultUp && face != Direction.Up) {
        //try again but with up
        success = rotateDirectional(block, Direction.Up, getCardinalDirection, setCardinalDirection);
        if (!success) {
            success = rotateDirectional(block, Direction.Up, getAttachFace, setAttachFace);
        }
        if (success)
            face = Direction.Up;
    }
    if (!success && (canDefaultUp || isAxisVertical(face))) {
        if (!success) {
            success = rotateRail(block, face);
        }
        if (!success) {
            success = rotateSign(block, face);
        }
    }
    if (success) {
        //rotate block entity. merely getting will fix
        block.dimension.playSound("block.itemframe.rotate_item", block.center());
        spawnParticle(block, face);
    }
    return success;
}
export const WrenchUseItemComponent = {
    onUseOn(event) {
        //just for anim. we need higher priority so using event
    }
};
world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
    const item = event.itemStack;
    if (item && item.typeId == SuppItems.Wrench && event.block.typeId != "razz_sup:pedestal" /* SuppBlocks.Pedestal */) {
        event.cancel = true;
        const block = event.block;
        const player = event.player;
        // Assume event.blockFace is one of our Direction values.
        const face = event.blockFace;
        const invert = player.isSneaking;
        system.run(() => {
            if (performRotation(block, face, invert, configs.wrenchUp.getValue())) {
                const soundOption = { pitch: 1.4, volume: 0.8 };
                player.playSound("item.spyglass.use", soundOption);
            }
            else {
                const soundOption = { pitch: 0.8, volume: 0.8 };
                player.playSound("item.spyglass.stop_using", soundOption);
            }
            //pulley ram, thatch, pumice cushion,
            //TODO: durability?
        });
    }
});
