import { world, system } from "@minecraft/server"

export class ConveyorComponent {
    constructor() {
        this.onStepOn = this.onStepOn.bind(this);
        this.onStepOff = this.onStepOff.bind(this);

        this.onPlayerInteract = this.onPlayerInteract.bind(this);
        this.beforeOnPlayerPlace = this.beforeOnPlayerPlace.bind(this);
        this.onPlayerDestroy = this.onPlayerDestroy.bind(this);

        this.impulseVector = { x: 0, y: 0, z: 0 };
        this.conveyorId = "pb_red:conveyor"
    }

    connection(block, frontBlock, backBlock, direction) {
        try {
            const isDiagonal = block => block.permutation.getState('pb_red:conveyor_diagonal_up') || block.permutation.getState('pb_red:conveyor_diagonal_down');
            const isConveyor = (block, direction) => block.permutation.matches("pb_red:conveyor") && block.permutation.getState('minecraft:cardinal_direction') === direction;

            const setConnectedState = (block, state, value) => {
                block.setPermutation(block.permutation.withState(state, value));
            };

            const checkAndSetConnection = (block, checkBlock, state) => {
                if (isConveyor(checkBlock, direction) && !isDiagonal(block) && !isDiagonal(checkBlock)) {
                    setConnectedState(block, state, true);
                } else {
                    if (isConveyor(checkBlock, direction) && !isDiagonal(block) && isDiagonal(checkBlock)) {
                        setConnectedState(block, state, true);
                    } else {
                        setConnectedState(block, state, false);
                    }
                }
            };

            checkAndSetConnection(block, frontBlock, 'pb_red:conveyor_front_connected');
            checkAndSetConnection(block, backBlock, 'pb_red:conveyor_back_connected');

            const checkAndSetDiagonalConnection = (block, checkBlock, state, diagonalState) => {
                if (isConveyor(checkBlock.above(), direction)) {
                    setConnectedState(block, diagonalState, true);
                    if (checkBlock.above().permutation.getState(diagonalState)) {
                        setConnectedState(block, state, true);
                    } else {
                        setConnectedState(block, state, false);
                    }
                } else {
                    setConnectedState(block, diagonalState, false);
                }
            };

            checkAndSetDiagonalConnection(block, frontBlock, 'pb_red:conveyor_front_connected', 'pb_red:conveyor_diagonal_up');
            checkAndSetDiagonalConnection(block, backBlock, 'pb_red:conveyor_back_connected', 'pb_red:conveyor_diagonal_down');

            const checkBelowConnection = (block, checkBlock, diagonalState, state) => {
                if (isConveyor(checkBlock.below(), direction) && checkBlock.below().permutation.getState(diagonalState)) {
                    setConnectedState(block, state, true);
                }
            };

            checkBelowConnection(block, backBlock, 'pb_red:conveyor_diagonal_up', 'pb_red:conveyor_back_connected');
            checkBelowConnection(block, frontBlock, 'pb_red:conveyor_diagonal_down', 'pb_red:conveyor_front_connected');

            if (block.above().permutation.matches("pb_red:conveyor")) {
                setConnectedState(block, 'pb_red:conveyor_diagonal_down', false);
                setConnectedState(block, 'pb_red:conveyor_diagonal_up', false);
            }

        } catch (e) {
            //   console.warn(e, " (Block beeing break while functioning)")
        }
    }

    onPlayerDestroy(e) {
        const block = e.block;
        const destroyedBlockPermutation = e.destroyedBlockPermutation
        const direction = destroyedBlockPermutation.getState('minecraft:cardinal_direction');

        system.runTimeout(() => {
            try {
                if (block)
                    this.updateSurroundingConveyors(block, direction)
            } catch (e) {
                //console.warn(e)
            }

        }, 1)
    }

    beforeOnPlayerPlace(e) {
        const block = e.block;
        const permutationToPlace = e.permutationToPlace;
        const direction = permutationToPlace.getState('minecraft:cardinal_direction');

        system.runTimeout(() => {
            try {
                if (block)
                    this.updateSurroundingConveyors(block, direction)
            } catch (e) {
                //console.warn(e)
            }

        }, 1)

    }

    onPlayerInteract(e) {
        const block = e.block;
        const direction = this.getDirection(block)

        const player = e.player;
        let selectedSlot = player.selectedSlotIndex;
        let handItem = player.getComponent("inventory").container.getItem(selectedSlot);

        if (handItem) {
            if (handItem.typeId == "pb_red:redstone_wrench") {
                // switch conveyor direction
                block.dimension.runCommand(`playsound vr.stutterturn @a ${block.location.x} ${block.location.y} ${block.location.z}`)
                block.setPermutation(block.permutation.withState("minecraft:cardinal_direction", this.getOppositeDirection(direction)))
                this.updateSurroundingConveyors(block, direction)
                return;
            }
        }
    }

    updateSurroundingConveyors(block, direction, updatedconveyorBlocks = new Set()) {

        let suroundBlocks = this.getFrontandBack(direction, block)
        let suroundBlocksAbove = this.getFrontandBack(direction, block.above())
        let suroundBlocksBelow = this.getFrontandBack(direction, block.below())

        const isValid = block => block && block.typeId == this.conveyorId;

        const allconveyors = [];
        if (isValid(suroundBlocks.frontBlock)) allconveyors.push(suroundBlocks.frontBlock)
        if (isValid(suroundBlocks.backBlock)) allconveyors.push(suroundBlocks.backBlock)
        if (isValid(suroundBlocksBelow.frontBlock)) allconveyors.push(suroundBlocksBelow.frontBlock)
        if (isValid(suroundBlocksBelow.backBlock)) allconveyors.push(suroundBlocksBelow.backBlock)
        if (isValid(suroundBlocksAbove.frontBlock)) allconveyors.push(suroundBlocksAbove.frontBlock)
        if (isValid(suroundBlocksAbove.backBlock)) allconveyors.push(suroundBlocksAbove.backBlock)

        if (updatedconveyorBlocks.size == 0) {
            if (block && block.typeId == this.conveyorId) allconveyors.push(block)
        }

        for (const expendedconveyor of allconveyors) {

            const expDir = this.getDirection(expendedconveyor);
            let expBlocks = this.getFrontandBack(expDir, expendedconveyor)

            this.connection(expendedconveyor, expBlocks.frontBlock, expBlocks.backBlock, expDir)

            system.runTimeout(() => {
                try {
                    if (updatedconveyorBlocks.size < 3) {
                        const blockLocationKey = `${expendedconveyor.location.x},${expendedconveyor.location.y},${expendedconveyor.location.z}`;
                        if (updatedconveyorBlocks.has(blockLocationKey)) {
                            return;
                        }

                        updatedconveyorBlocks.add(blockLocationKey);
                        this.updateSurroundingConveyors(expendedconveyor, expDir, updatedconveyorBlocks);
                    }

                } catch (e) {
                    // console.warn(e)
                }

            }, 1);
        }
    }

    getDirection(block) {
        return block.permutation.getState("minecraft:cardinal_direction")
    }

    getFrontandBack(direction, block) {
        let frontBlock, backBlock, frontOffset, backOffset, rotation;
        switch (direction) {
            case 'north':
                frontBlock = block.north();
                backBlock = block.south();
                frontOffset = { x: 0, y: 0, z: -1 }
                backOffset = { x: 0, y: 0, z: 1 }
                rotation = 180
                break;
            case 'south':
                frontBlock = block.south();
                backBlock = block.north();
                frontOffset = { x: 0, y: 0, z: 1 }
                backOffset = { x: 0, y: 0, z: -1 }
                rotation = 0
                break;
            case 'east':
                frontBlock = block.east();
                backBlock = block.west();
                frontOffset = { x: 1, y: 0, z: 0 }
                backOffset = { x: -1, y: 0, z: 0 }
                rotation = 270
                break;
            case 'west':
                frontBlock = block.west();
                backBlock = block.east();
                frontOffset = { x: -1, y: 0, z: 0 }
                backOffset = { x: 1, y: 0, z: 0 }
                rotation = 90

                break;
        }
        return { frontBlock, backBlock, frontOffset, backOffset, rotation }
    }

    getOppositeDirection(direction) {
        switch (direction) {
            case 'north':
                return 'south';
            case 'south':
                return 'north';
            case 'east':
                return 'west';
            case 'west':
                return 'east';
            case 'up':
                return 'down';
            case 'down':
                return 'up';
            default:
                return null;  // If the direction is invalid
        }
    }

    onStepOn(e) {
        const block = e.block

        let direction = block.permutation.getState('minecraft:cardinal_direction')
        let { frontBlock, backBlock, frontOffset, backOffset, rotation } = this.getFrontandBack(direction, block)
        for (const aboveEntity of block.dimension.getEntitiesAtBlockLocation(block.location)) {

            if (
                aboveEntity.typeId == 'minecraft:player'
                || (this.loadIgnoredEntities("Conveyor").includes(aboveEntity.typeId.replace("minecraft:", "").replace("_v2", "")) && !this.loadItemsOnlyMode("Conveyor"))
                || (aboveEntity.typeId !== "minecraft:item" && this.loadItemsOnlyMode("Conveyor"))
            ) {
                return
            }

            let entityLoc = aboveEntity.location
            //aboveEntity.getComponent('minecraft:movement')?.setCurrentValue(0)
            const blockCenterX = block.location.x + 0.5;
            const blockCenterZ = block.location.z + 0.5;
            // If the direction is along the x-axis, center on the z-axis
            if (Math.abs(frontOffset.x) > Math.abs(frontOffset.z)) {
                if (entityLoc.z > blockCenterZ + 0.1)
                    aboveEntity.teleport({ x: entityLoc.x, y: entityLoc.y + 0.05, z: blockCenterZ })
                else if (entityLoc.z < blockCenterZ - 0.1)
                    aboveEntity.teleport({ x: entityLoc.x, y: entityLoc.y + 0.05, z: blockCenterZ })
            }
            // If the direction is along the z-axis, center on the x-axis
            else {
                if (entityLoc.x > blockCenterX + 0.1)
                    aboveEntity.teleport({ x: blockCenterX, y: entityLoc.y + 0.05, z: entityLoc.z })
                else if (entityLoc.x < blockCenterX - 0.1)
                    aboveEntity.teleport({ x: blockCenterX, y: entityLoc.y + 0.05, z: entityLoc.z })
            }
            // aboveEntity.setRotation({ x: 0, y: rotation })
            aboveEntity.clearVelocity()

            if (block.permutation.getState('pb_red:conveyor_diagonal_up') == false && block.permutation.getState('pb_red:conveyor_diagonal_down') == false)
                this.impulseVector = { x: frontOffset.x / 3, y: frontOffset.y + 0.05, z: frontOffset.z / 3 }
            if (block.permutation.getState('pb_red:conveyor_diagonal_up') == true)
                this.impulseVector = { x: frontOffset.x / 3, y: frontOffset.y + 0.3, z: frontOffset.z / 3 }
            if (block.permutation.getState('pb_red:conveyor_diagonal_down') == true) {
                if (aboveEntity.typeId == "minecraft:item")
                    this.impulseVector = { x: frontOffset.x / 3, y: frontOffset.y, z: frontOffset.z / 3 }
                else
                    this.impulseVector = { x: frontOffset.x / 3, y: frontOffset.y + 0.1, z: frontOffset.z / 3 }
            }
            aboveEntity.applyImpulse(this.impulseVector)
        }
    }

    onStepOff(e) {
        const block = e.block
        let direction = block.permutation.getState('minecraft:cardinal_direction')
        let { frontBlock, backBlock, frontOffset, backOffset, rotation } = this.getFrontandBack(direction, block)
        if (block.permutation.getState('pb_red:conveyor_diagonal_up') == true) {
            for (const aboveEntity of block.dimension.getEntitiesAtBlockLocation(block.location)) {
                if (aboveEntity.typeId == 'minecraft:player') {
                    return
                }
                let entityTpLoc = frontBlock.above().location
                let rectifiedTpLoc = { x: 0, y: 0, z: 0 }
                switch (direction) {
                    case 'north':
                        rectifiedTpLoc = { x: entityTpLoc.x + 0.5, y: entityTpLoc.y + 0.35, z: entityTpLoc.z + 0.8 }
                        break;
                    case 'south':
                        rectifiedTpLoc = { x: entityTpLoc.x + 0.5, y: entityTpLoc.y + 0.35, z: entityTpLoc.z + 0.1 }
                        break;
                    case 'east':
                        rectifiedTpLoc = { x: entityTpLoc.x + 0.1, y: entityTpLoc.y + 0.35, z: entityTpLoc.z + 0.5 }
                        break;
                    case 'west':
                        rectifiedTpLoc = { x: entityTpLoc.x + 0.8, y: entityTpLoc.y + 0.35, z: entityTpLoc.z + 0.5 }

                        break;
                }
                aboveEntity.teleport(rectifiedTpLoc)
            }
        }
    }

    loadIgnoredEntities(block) {
        const data = world.getDynamicProperty(`pb_red:IgnoredEntities:${block}`);
        if (data) {
            return JSON.parse(data);
        }
        return [];
    }

    loadItemsOnlyMode(block) {
        const data = world.getDynamicProperty(`pb_red:ItemsOnlyMode:${block}`);
        return data ? JSON.parse(data) : false;
    }

}
