import "./storage_wireless";
import "./tool_belt";
import "./maisumcadin";
import { system, world, MolangVariableMap, TicksPerSecond } from "@minecraft/server";
import * as ui from "@minecraft/server-ui";
import { icons } from "./storageIcons";
import * as racoAPI from "../raco-API";

system.runInterval(() => {
    const players = world.getAllPlayers();
    players.forEach(player => {
        const armorHead = player.getComponent('minecraft:equippable').getEquipment('Head');
        const itemHand = player.getComponent('minecraft:equippable').getEquipment('Mainhand');
        if (armorHead?.typeId == 'vtng_rt:electricians_goggles') {
            const blockFromView = player.getBlockFromViewDirection({maxDistance: 8})?.block;
            if (!blockFromView) return;
            const redstonePower = blockFromView.getRedstonePower();
            if (redstonePower !== undefined) {
                player.runCommand(`titleraw @s actionbar {"rawtext": [{"text": "Redstone Power: §l§c${redstonePower}\n§r§9§oElectrician's Goggles"}]}`);
            };
        };
        if (armorHead?.typeId == 'vtng_rt:miners_hat') {
            const glowLoc = player.getHeadLocation();
            const glowBlock = player.dimension.getBlock(glowLoc);
            if (glowBlock.isAir) minersHat(glowBlock);
            if (glowBlock?.typeId == 'vtng_rt:glow_block') minersHat(glowBlock);
        };
    });
}, 5);

function minersHat(glowBlock) {
    system.run(() => {
        let lightLevel = 10;
        const light = system.runInterval(() => {
            glowBlock.setType(lightLevel > 0 ? 'vtng_rt:glow_block' : 'minecraft:air');
            if (lightLevel <= 0) system.clearRun(light);
            lightLevel -= 5;
        }, 5);
    });
};

const numberToColor = { 0: 'red', 1: 'green', 2: 'blue', 3: 'yellow'};
const colorToNumber = { 'red': 0, 'green': 1, 'blue': 2, 'yellow': 3};

function addStep(block, dimension, player) {
    const addStep = new ui.ModalFormData();
    addStep.title('Add config');
    addStep.dropdown('Choose a side:', ['Red (N)', 'Green (E)', 'Blue (S)', 'Yellow (W)']);
    addStep.slider('\nPulse duration: (In seconds)', 0, 60, 1, 0);
    addStep.slider('\nPulse interval: (In seconds)', 1, 300, 1, 1);
    addStep.submitButton('Add new config')
    addStep.show(player).then(r => {
        if (r.canceled) return;
        const pcSteps = world.getDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`);
        world.setDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`, `${pcSteps ? pcSteps + ':' : ''}${numberToColor[r.formValues[0]]}.${r.formValues[1]}.${r.formValues[2]}`);
    });
};

function editStep(block, dimension, player, stepIndex) {
    const pcSteps = world.getDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`)?.split(':');
    const step = pcSteps[stepIndex];
    const editStep = new ui.ModalFormData();
    editStep.title('Edit menu');
    editStep.dropdown('Choose a side:', ['Red (N)', 'Green (E)', 'Blue (S)', 'Yellow (W)'], colorToNumber[step.split('.')[0]]);
    editStep.slider('\nPulse duration: (In seconds)', 0, 60, 1, Number(step.split('.')[1]));
    editStep.slider('\nPulse interval: (In seconds)', 1, 300, 1, Number(step.split('.')[2]));
    editStep.submitButton('Edit step');
    editStep.show(player).then(r => {
        if (r.canceled) return;
        const editedSteps = pcSteps.map((item, index) => index === stepIndex ? `${numberToColor[r.formValues[0]]}.${r.formValues[1]}.${r.formValues[2]}` : item).join(':');
        world.setDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`, editedSteps);
    });
};

const computerTimer = {};
const computerTick = {};
const sequencialComputer = {};

world.beforeEvents.worldInitialize.subscribe(xZ => {
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:computer', {
        onPlayerDestroy: event => {
            const {block, dimension } = event; 
            if (computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]);
            if (computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
            if (computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]);
            if (computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
            for (let side of ['north', 'south', 'east', 'west']) {
                const newBlock = block[side]();
                if (newBlock.typeId == 'minecraft:powered_repeater') newBlock.dimension.runCommand(`setblock ${newBlock.x} ${newBlock.y} ${newBlock.z} unpowered_repeater ["minecraft:cardinal_direction"="${newBlock.permutation.getState("minecraft:cardinal_direction")}", "repeater_delay"=${newBlock.permutation.getState("repeater_delay") ?? 0}]`);
            };
            world.setDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`, undefined);
        },
        onTick: event => {
            const {block, dimension } = event;   
            const pcSteps = world.getDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`)?.split(':');
            if (pcSteps) for (let step of pcSteps) {
                const stepConfig = step.split('.');
                const redBlock = block.north();
                const greenBlock = block.east();
                const blueBlock = block.south();
                const yellowBlock = block.west();
                if (stepConfig[0] == 'red' && redBlock?.typeId.includes('_repeater') && redBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['north']) {
                    if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                        let counter = 0;
                        const repeater = dimension.getBlock(redBlock.location);
                        if (computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                        computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                            if (repeater.typeId.includes('_repeater')) block?.dimension?.runCommand(`setblock ${redBlock?.x} ${redBlock?.y} ${redBlock?.z} powered_repeater ["minecraft:cardinal_direction"="south", "repeater_delay"=${redBlock.permutation.getState("repeater_delay") ?? 0}]`);
                            if (counter >= stepConfig[1] * TicksPerSecond) { system.clearRun(computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]); };
                            counter++;
                        });
                    };
                };
                if (stepConfig[0] == 'green' && greenBlock?.typeId.includes('_repeater') && greenBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['east']) {
                    if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                        let counter = 0;
                        const repeater = dimension.getBlock(greenBlock.location);
                        if (computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                        computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                            if (repeater.typeId.includes('_repeater')) block.dimension.runCommand(`setblock ${greenBlock.x} ${greenBlock.y} ${greenBlock.z} powered_repeater ["minecraft:cardinal_direction"="west", "repeater_delay"=${greenBlock.permutation.getState("repeater_delay") ?? 0}]`);
                            if (counter >= stepConfig[1] * TicksPerSecond) { system.clearRun(computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]); };
                            counter++;
                        });
                    };
                };
                if (stepConfig[0] == 'blue' && blueBlock?.typeId.includes('_repeater') && blueBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['south']) {
                    if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                        let counter = 0;
                        const repeater = dimension.getBlock(blueBlock.location);
                        if (computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                        computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                            if (repeater.typeId.includes('_repeater')) block?.dimension?.runCommand(`setblock ${blueBlock?.x} ${blueBlock?.y} ${blueBlock?.z} powered_repeater ["minecraft:cardinal_direction"="north", "repeater_delay"=${blueBlock.permutation.getState("repeater_delay") ?? 0}]`);
                            if (counter >= stepConfig[1] * TicksPerSecond) { system.clearRun(computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]); };
                            counter++;
                        });
                    };
                };
                if (stepConfig[0] == 'yellow' && yellowBlock?.typeId.includes('_repeater') && yellowBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['west']) {
                    if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                        let counter = 0;
                        const repeater = dimension.getBlock(yellowBlock.location);
                        if (computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                        computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                            if (repeater.typeId.includes('_repeater')) block.dimension.runCommand(`setblock ${yellowBlock.x} ${yellowBlock.y} ${yellowBlock.z} powered_repeater ["minecraft:cardinal_direction"="east", "repeater_delay"=${yellowBlock.permutation.getState("repeater_delay") ?? 0}]`);
                            if (counter >= stepConfig[1] * TicksPerSecond) { system.clearRun(computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]); };
                            counter++;
                        });
                    };
                };
            };
            if (!computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] || computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] >= 10000) computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] = 1;
            computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`]++;
        },
        onPlayerInteract: event => {
            const {block, dimension, player} = event;
            const pcSteps = world.getDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`)?.split(':');
            const stepsNum = pcSteps != undefined ? pcSteps.length : 0;
            const configForm = new ui.ActionFormData();
            configForm.title('Computer Configuration');
            configForm.body('Configure each face to send redstone signals.');
            for (let i = 0; i < stepsNum; i++) {
                const color = pcSteps[i].split('.')[0].charAt(0).toUpperCase() + pcSteps[i].split('.')[0].slice(1);
                configForm.button(`Config ${i+1} - Side ${color}`);
            };
            configForm.button('Define a new config');
            configForm.show(player).then(r => {
                if (r.canceled) return;
                if (r.selection == stepsNum) addStep(block, dimension, player);
                else {
                    const configStep = new ui.ActionFormData();
                    configStep.title('Edit menu');
                    configStep.button('Edit config');
                    configStep.button('Delete config');
                    configStep.show(player).then(a => {
                        if (a.canceled) return;
                        if (a.selection == 0) {
                            editStep(block, dimension, player, r.selection);
                        };
                        if (a.selection == 1) {
                            world.setDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`, stepsNum > 1 ? pcSteps.filter(step => step !== pcSteps[r.selection]).join(':') : undefined);
                        };
                    });
                };
            });
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:sequencial_computer', {
        onPlayerDestroy: event => {
            const {block, dimension } = event; 
            if (computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]);
            if (computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
            if (computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]);
            if (computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
            for (let side of ['north', 'south', 'east', 'west']) {
                const newBlock = block[side]();
                if (newBlock.typeId.includes('_repeater')) newBlock.dimension.runCommand(`setblock ${newBlock.x} ${newBlock.y} ${newBlock.z} unpowered_repeater ["minecraft:cardinal_direction"="${newBlock.permutation.getState("minecraft:cardinal_direction")}", "repeater_delay"=${newBlock.permutation.getState("repeater_delay") ?? 0}]`);
            };
            world.setDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`, undefined);
        },
        onTick: event => {
            const {block, dimension } = event;   
            const pcSteps = world.getDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`)?.split(':');
            const sequencia = sequencialComputer[`${block.dimension.id},${block.x},${block.y},${block.z}`];
            if (!sequencia && pcSteps) sequencialComputer[`${block.dimension.id},${block.x},${block.y},${block.z}`] = system.run(async () => {
                for (let s = 0; s < pcSteps.length; s++) {
                    const stepConfig = pcSteps[s].split('.');
                    const redBlock = block.north();
                    const greenBlock = block.east();
                    const blueBlock = block.south();
                    const yellowBlock = block.west();
                    let stepConclude;
                    while (!stepConclude) {
                        if (stepConfig[0] == 'red' && redBlock?.typeId.includes('_repeater') && redBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['north'] && !computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]) {
                            if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                                let counter = 0;
                                const repeater = dimension.getBlock(redBlock.location);
                                if (computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                                computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                                    if (repeater.typeId.includes('_repeater')) block.dimension.runCommand(`setblock ${redBlock.x} ${redBlock.y} ${redBlock.z} powered_repeater ["minecraft:cardinal_direction"="south", "repeater_delay"=${redBlock.permutation.getState("repeater_delay") ?? 0}]`);
                                    if (counter >= stepConfig[1] * TicksPerSecond) {
                                        stepConclude = true;
                                        system.clearRun(computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`]);
                                        delete computerTick[`red:${block.dimension.id},${block.x},${block.y},${block.z}`];
                                    };
                                    counter++;
                                });
                            };
                        };
                        if (stepConfig[0] == 'green' && greenBlock?.typeId.includes('_repeater') && greenBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['east'] && !computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]) {
                            if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                                let counter = 0;
                                const repeater = dimension.getBlock(greenBlock.location);
                                if (computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                                computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                                    if (repeater.typeId.includes('_repeater')) block.dimension.runCommand(`setblock ${greenBlock.x} ${greenBlock.y} ${greenBlock.z} powered_repeater ["minecraft:cardinal_direction"="west", "repeater_delay"=${greenBlock.permutation.getState("repeater_delay") ?? 0}]`);
                                    if (counter >= stepConfig[1] * TicksPerSecond) {
                                        stepConclude = true;
                                        system.clearRun(computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`]);
                                        delete computerTick[`green:${block.dimension.id},${block.x},${block.y},${block.z}`];
                                    };
                                    counter++;
                                });
                            };
                        };
                        if (stepConfig[0] == 'blue' && blueBlock?.typeId.includes('_repeater') && blueBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['south'] && !computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]) {
                            if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                                let counter = 0;
                                const repeater = dimension.getBlock(blueBlock.location);
                                if (computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                                computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                                    if (repeater.typeId.includes('_repeater')) block.dimension.runCommand(`setblock ${blueBlock.x} ${blueBlock.y} ${blueBlock.z} powered_repeater ["minecraft:cardinal_direction"="north", "repeater_delay"=${blueBlock.permutation.getState("repeater_delay") ?? 0}]`);
                                    if (counter >= stepConfig[1] * TicksPerSecond) {
                                        stepConclude = true;
                                        system.clearRun(computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`]);
                                        delete computerTick[`blue:${block.dimension.id},${block.x},${block.y},${block.z}`];
                                    };
                                    counter++;
                                });
                            };
                        };
                        if (stepConfig[0] == 'yellow' && yellowBlock?.typeId.includes('_repeater') && yellowBlock.permutation.getState("minecraft:cardinal_direction") == invertFace['west'] && !computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]) {
                            if (computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] > 1 && computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] % (stepConfig[2] * TicksPerSecond) == 0) {
                                let counter = 0;
                                const repeater = dimension.getBlock(yellowBlock.location);
                                if (computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]) system.clearRun(computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]); 
                                computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`] = system.runInterval(() => {
                                    if (repeater.typeId.includes('_repeater')) block.dimension.runCommand(`setblock ${yellowBlock.x} ${yellowBlock.y} ${yellowBlock.z} powered_repeater ["minecraft:cardinal_direction"="east", "repeater_delay"=${yellowBlock.permutation.getState("repeater_delay") ?? 0}]`);
                                    if (counter >= stepConfig[1] * TicksPerSecond) {
                                        stepConclude = true;
                                        system.clearRun(computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`]);
                                        delete computerTick[`yellow:${block.dimension.id},${block.x},${block.y},${block.z}`];
                                    };
                                    counter++;
                                });
                            };
                        };
                        computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`]++;
                        await system.waitTicks(1);
                    };
                    stepConclude = false;
                };
                if (sequencialComputer[`${block.dimension.id},${block.x},${block.y},${block.z}`]) {
                    system.clearRun(sequencialComputer[`${block.dimension.id},${block.x},${block.y},${block.z}`]);
                    sequencialComputer[`${block.dimension.id},${block.x},${block.y},${block.z}`] = undefined;
                    delete sequencialComputer[`${block.dimension.id},${block.x},${block.y},${block.z}`];
                };
            });
            if (!computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] || computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] >= 10000) computerTimer[`${block.dimension.id},${block.x},${block.y},${block.z}`] = 1;
        },
        onPlayerInteract: event => {
            const {block, dimension, player} = event;
            const pcSteps = world.getDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`)?.split(':');
            const stepsNum = pcSteps != undefined ? pcSteps.length : 0;
            const configForm = new ui.ActionFormData();
            configForm.title('Computer Configuration');
            configForm.body('Configure each face to send redstone signals.');
            for (let i = 0; i < stepsNum; i++) {
                const color = pcSteps[i].split('.')[0].charAt(0).toUpperCase() + pcSteps[i].split('.')[0].slice(1);
                configForm.button(`Config ${i+1} - Side ${color}`);
            };
            configForm.button('Define a new config');
            configForm.show(player).then(r => {
                if (r.canceled) return;
                if (r.selection == stepsNum) addStep(block, dimension, player);
                else {
                    const configStep = new ui.ActionFormData();
                    configStep.title('Edit menu');
                    configStep.button('Edit config');
                    configStep.button('Delete config');
                    configStep.show(player).then(a => {
                        if (a.canceled) return;
                        if (a.selection == 0) {
                            editStep(block, dimension, player, r.selection);
                        };
                        if (a.selection == 1) {
                            world.setDynamicProperty(`PC-steps:${dimension.id}${block.x}${block.y}${block.z}`, stepsNum > 1 ? pcSteps.filter(step => step !== pcSteps[r.selection]).join(':') : undefined);
                        };
                    });
                };
            });
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:container_display', {
        onTick: event => {
            const {block, dimension } = event;
            const blockRotation = block.permutation.getState('minecraft:block_face');
            const containerBlock = block[invertFace[blockRotation]]();
            const chestInv = containerBlock.getComponent('minecraft:inventory')?.container;
            if (chestInv) {
                const percentRemaining = (((chestInv.size-chestInv.emptySlotsCount)/chestInv.size)*100).toFixed();
                block.setPermutation(block.permutation.withState('vtng_rt:display', percentRemaining >= 100 ? 10 : percentRemaining >= 90 ? 9 : percentRemaining >= 80 ? 8 :
                    percentRemaining >= 70 ? 7 : percentRemaining >= 60 ? 6 : percentRemaining >= 50 ? 5 : percentRemaining >= 40 ? 4 : percentRemaining >= 30 ? 3 :
                    percentRemaining >= 20 ? 2 : percentRemaining >= 10 ? 1 : 0));
            };            
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:glowBlock', {
        onTick: event => {
            event.block.setType('minecraft:air');
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:fan', {
        onTick: event => {
            const { dimension, block } = event;
            const isOn = block.permutation.getState('vtng_rt:on');
            const redstonePower = block.getRedstonePower();
            if (redstonePower > 0 && !isOn) block.setPermutation(block.permutation.withState('vtng_rt:on', true));
            if (redstonePower <= 1 && isOn) block.setPermutation(block.permutation.withState('vtng_rt:on', false));
            if (isOn) fan(block, dimension, redstonePower);
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:conveyor', {
        onTick: event => {
            const { dimension, block } = event;
            const blockRotation = block.permutation.getState('minecraft:cardinal_direction');
            const diagonal = block.permutation.getState('vtng_rt:diagonal');
            const directions = { 'north': {x: 0, y: 0, z: -0.3}, 'south': {x: 0, y: 0, z: 0.3}, 'east': {x: 0.3, y: 0, z: 0}, 'west': {x: -0.3, y: 0, z: 0} };
            const entities = dimension.getEntities({location: block.center(), maxDistance: 0.85});
            if (!entities.length) return;
            entities.forEach(entity => {
                const isItem = entity?.getComponent('minecraft:item');
                const isProjectile = entity?.getComponent('minecraft:projectile');
                if (entity?.typeId != 'minecraft:player') entity.teleport({
                    x: entity.location.x + directions[blockRotation].x + (blockRotation != 'east' && blockRotation != 'west' ? ((entity.location.x - block.bottomCenter().x) > 0 ? -0.05 : 0.05) : 0),
                    y: entity.location.y + (diagonal == 'diagonal_up' ? 0.35 : diagonal == 'diagonal_down' ? (!isItem && !isProjectile ? -0.3 : -0.2) : directions[blockRotation].y),
                    z: entity.location.z + directions[blockRotation].z + (blockRotation != 'north' && blockRotation != 'south' ? ((entity.location.z - block.bottomCenter().z) > 0 ? -0.05 : 0.05) : 0)
                });
                if (!isItem && !isProjectile && entity?.typeId != 'minecraft:player') entity.tryTeleport({
                    x: entity.location.x,
                    y: entity.location.y + (diagonal == 'none' ? ((entity.location.y - block.bottomCenter().y) > 0.1 ? -0.1 : 0.00) : 0),
                    z: entity.location.z
                }, {checkForBlocks: true});
            });
        },
        onPlace: event => {
            const { dimension, block } = event;  
            const blockRotation = block.permutation.getState('minecraft:cardinal_direction');
            const sides = ['south.below', 'south.above', 'north.above', 'north.below'];
            const correctDirections = {
                'north': { 'south.below': 'south.below', 'south.above': 'south.above', 'north.below': 'north.below', 'north.above': 'north.above' },
                'south': { 'south.below': 'north.below', 'south.above': 'north.above', 'north.below': 'south.below', 'north.above': 'south.above' },
                'east': { 'south.below': 'west.below', 'south.above': 'west.above', 'north.below': 'east.below', 'north.above': 'east.above' },
                'west': { 'south.below': 'east.below', 'south.above': 'east.above', 'north.below': 'west.below', 'north.above': 'west.above' }
            };
            for (let side of sides) {
                const newBlock = side.split('.')[1] ? block[correctDirections[blockRotation][side].split('.')[0]]()[correctDirections[blockRotation][side].split('.')[1]]() : block[correctDirections[blockRotation][side]]();
                const newRotation = newBlock.permutation.getState('minecraft:cardinal_direction');
                if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'south.below' && newRotation == blockRotation) newBlock.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_up"));
                if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'north.below' && newRotation == blockRotation) newBlock.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_down"));
                if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'north.above' && newRotation == blockRotation) block.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_up"));
                if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'south.above' && newRotation == blockRotation) block.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_down"));
            };   
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:conveyor_filter', {
        onTick: event => {
            const { dimension, block } = event;
            const blockRotation = block.permutation.getState('minecraft:cardinal_direction');
            const directions = { 'north': {x: 0, y: 0, z: 0.3}, 'south': {x: 0, y: 0, z: -0.3}, 'east': {x: -0.3, y: 0, z: 0}, 'west': {x: 0.3, y: 0, z: 0} };
            const backBlock = block[blockRotation]();
            if (backBlock?.typeId == 'vtng_rt:conveyor') {
                const entities = dimension.getEntities({location: backBlock.center(), maxDistance: 0.6});
                if (!entities.length) return;
                entities.forEach(entity => {
                    const isItem = entity?.getComponent('minecraft:item');
                    if (isItem) {
                        const itemStack = entity.getComponent('minecraft:item')?.itemStack;
                        if (itemStack?.typeId == world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`) 
                        || world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`) == undefined
                        || world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`) == 'undefined') {
                            block.setPermutation(block.permutation.withState('vtng_rt:on', true));
                            entity.teleport({
                                x: block.bottomCenter().x,
                                y: block.bottomCenter().y,
                                z: block.bottomCenter().z
                            });
                            system.runTimeout(() => { if (!block.isAir) block.setPermutation(block.permutation.withState('vtng_rt:on', false)); }, 5)
                        };
                    };
                });
            };
            const entities = dimension.getEntities({location: block.center(), maxDistance: 0.6});
            if (!entities.length) return;
            entities.forEach(entity => {
                const isItem = entity?.getComponent('minecraft:item');
                if (isItem) entity.teleport({
                    x: block.bottomCenter().x + directions[blockRotation].x,
                    y: block.bottomCenter().y,
                    z: block.bottomCenter().z + directions[blockRotation].z
                })
            });
        },
        onPlayerInteract: event => {
            const {block, dimension, player} = event;
            const item = player.getComponent('minecraft:equippable').getEquipment("Mainhand");
            const itemName = item?.typeId.split(':')[1].replace(/_/g, ' ').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
			const currentFilter = world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`) && world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`) != 'undefined' ? world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`).split(':')[1].replace(/_/g, ' ').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ') : 'There is no active filter.';
			const currentFilterId = world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`) == 'undefined' ? undefined : world.getDynamicProperty(`filter:${block.x},${block.y},${block.z}`);
            const form = new ui.ActionFormData()
			.title('Conveyor Filter')
			.body(`\nWant to filter the item you are holding?\n\n` )
            //`§aCurrent filter: §r${currentFilter}\n\n` +
            //`§eNew filter: §r${item ? itemName : 'Not holding an item.'}\n\n`
			.button(`${item ? 'Set new filter:\n§l§8>§r §5'+itemName+' §r§l§8<' : 'Remove current filter'}`, 
                `${item ? icons[item?.typeId] ? icons[item?.typeId] : icons.without_image : 'textures/ui/realms_red_x'}`) // 'textures/ui/realms_green_check'
			.button(`Keep the current filter:\n§2${currentFilter}`,
                `${currentFilterId ? icons[currentFilterId] ? icons[currentFilterId] : icons.without_image : 'textures/ui/realms_green_check'}`)
			.show(player).then(r => {
				if (r.canceled || r.selection == 1) return;
				world.setDynamicProperty(`filter:${block.x},${block.y},${block.z}`, `${item ? item?.typeId : 'undefined'}`)
			});
        }
        //onPlace: event => {
        //    const { dimension, block } = event;  
        //    const blockRotation = block.permutation.getState('minecraft:cardinal_direction');
        //    const sides = ['south.below', 'south.above', 'north.above', 'north.below'];
        //    const correctDirections = {
        //        'north': { 'south.below': 'south.below', 'south.above': 'south.above', 'north.below': 'north.below', 'north.above': 'north.above' },
        //        'south': { 'south.below': 'north.below', 'south.above': 'north.above', 'north.below': 'south.below', 'north.above': 'south.above' },
        //        'east': { 'south.below': 'west.below', 'south.above': 'west.above', 'north.below': 'east.below', 'north.above': 'east.above' },
        //        'west': { 'south.below': 'east.below', 'south.above': 'east.above', 'north.below': 'west.below', 'north.above': 'west.above' }
        //    };
        //    for (let side of sides) {
        //        const newBlock = side.split('.')[1] ? block[correctDirections[blockRotation][side].split('.')[0]]()[correctDirections[blockRotation][side].split('.')[1]]() : block[correctDirections[blockRotation][side]]();
        //        const newRotation = newBlock.permutation.getState('minecraft:cardinal_direction');
        //        if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'south.below' && newRotation == blockRotation) newBlock.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_up"));
        //        if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'north.below' && newRotation == blockRotation) newBlock.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_down"));
        //        if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'north.above' && newRotation == blockRotation) block.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_up"));
        //        if (newBlock?.typeId == 'vtng_rt:conveyor' && side == 'south.above' && newRotation == blockRotation) block.setPermutation(newBlock.permutation.withState('vtng_rt:diagonal', "diagonal_down"));
        //    };   
        //}
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:locked_chest', {
        onPlayerInteract: event => {
            const {block, dimension, player} = event;
            const chestEntity = dimension.getEntities({location: block.center(), maxDistance: 0.2})[0];
            if (!world.getDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`)) return setPassword(block, dimension, player, chestEntity);
            if (block.permutation.getState('vtng_rt:unlocked')) {
                return;
            } else {
                enterPassword(block, dimension, player, chestEntity);
            };
        },
        onPlayerDestroy:  event => {
            const {block, dimension, player} = event;
            world.setDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`, undefined);
        },
        beforeOnPlayerPlace: event => {
            const {block, dimension, player} = event;
            const chestEntity = dimension.spawnEntity('vtng_rt:locked_chest', block.center());
            chestEntity.nameTag = 'Locked Chest';
            setPassword(block, dimension, player, chestEntity);
        },
        onTick: event => {
            const {block, dimension } = event;
            if (block.permutation.getState('vtng_rt:unlocked')) block.setPermutation(block.permutation.withState('vtng_rt:unlocked', false));
        }
    });
    xZ.blockComponentRegistry.registerCustomComponent('vtng_rt:lock_block', {
        onPlayerInteract: event => {
            const {block, dimension, player} = event;
            if (!world.getDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`)) return setBlockPassword(block, dimension, player);
            enterBlockPassword(block, dimension, player);
        },
        beforeOnPlayerPlace: event => {
            const {block, dimension, player} = event;
            setBlockPassword(block, dimension, player);
        }
    })
});

function enterBlockPassword(block, dimension, player, error) {
    const rotation = block.permutation.getState('minecraft:cardinal_direction');
    if (error) player.playSound('note.bass', {volume: 0.5});
    const deniedForm = new ui.ModalFormData();
    deniedForm.title('Release access');
    if (!error) deniedForm.textField('\nEnter password:', 'Example: password123');
    if (error) deniedForm.textField('§cIncorrect password!§r\n\nEnter password:', 'Example: password123');
    deniedForm.show(player).then(r => {
        if (r.canceled) return;
        if (r.formValues == world.getDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`)) {
            player.playSound('note.bell', {volume: 0.5});
            racoAPI.setPermutation(block, 'vtng_rt:password_state', 'correct')
            system.runTimeout(()=> {racoAPI.setPermutation(block, 'vtng_rt:password_state', 'default')},30)
            const frontBlock = block[correctDirections[rotation]['south']]();
            const backBlock = block[correctDirections[rotation]['north']]();
            const leftBlock = block[correctDirections[rotation]['east']]();
            const rightBlock = block[correctDirections[rotation]['west']]();
            if (frontBlock?.typeId.includes('_repeater') && frontBlock.permutation.getState("minecraft:cardinal_direction") == rotation) {
                block.dimension.runCommand(`setblock ${frontBlock.x} ${frontBlock.y} ${frontBlock.z} powered_repeater ["minecraft:cardinal_direction"="${rotation}", "repeater_delay"=${frontBlock.permutation.getState("repeater_delay")}]`);
            };
            if (backBlock?.typeId.includes('_repeater') && backBlock.permutation.getState("minecraft:cardinal_direction") == invertFace[rotation]) {
                block.dimension.runCommand(`setblock ${backBlock.x} ${backBlock.y} ${backBlock.z} powered_repeater ["minecraft:cardinal_direction"="${invertFace[rotation]}", "repeater_delay"=${backBlock.permutation.getState("repeater_delay")}]`);
            };
            if (rightBlock?.typeId.includes('_repeater') && rightBlock.permutation.getState("minecraft:cardinal_direction") == correctDirections[rotation]['east']) {
                block.dimension.runCommand(`setblock ${rightBlock.x} ${rightBlock.y} ${rightBlock.z} powered_repeater ["minecraft:cardinal_direction"="${correctDirections[rotation]['east']}", "repeater_delay"=${rightBlock.permutation.getState("repeater_delay")}]`);
            };
            if (leftBlock?.typeId.includes('_repeater') && leftBlock.permutation.getState("minecraft:cardinal_direction") == correctDirections[rotation]['west']) {
                block.dimension.runCommand(`setblock ${leftBlock.x} ${leftBlock.y} ${leftBlock.z} powered_repeater ["minecraft:cardinal_direction"="${correctDirections[rotation]['west']}", "repeater_delay"=${leftBlock.permutation.getState("repeater_delay")}]`);
            };
        } else {
            racoAPI.setPermutation(block, 'vtng_rt:password_state', 'incorrect')
            system.runTimeout(()=> {racoAPI.setPermutation(block, 'vtng_rt:password_state', 'default')},30)
        };
    });
};

function setBlockPassword(block, dimension, player, error) {
    const passwordForm = new ui.ModalFormData();
    passwordForm.title('Set a Password!');
    if (!error)passwordForm.textField('\nEnter password:', 'Example: password123');
    if (error) passwordForm.textField('\n§cEnter at least 3 characters!!§r\n\nEnter password:', 'Example: password123');
    passwordForm.show(player).then(r => {
        if (r.canceled) return;
        if (r.formValues[0].length >= 3) {
            world.setDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`, r.formValues[0]);
            world.setDynamicProperty(`lockOwner:${dimension.id},${block.x},${block.y},${block.z}`, `${player.id}`);
            player.playSound('note.bell', {volume: 0.5});
        } else setBlockPassword(block, dimension, player, true);
    });
}; 

system.afterEvents.scriptEventReceive.subscribe(({id, sourceEntity}) => {
    if (id == 'vtng_rt:closeChest') {
        const block = sourceEntity.dimension.getBlock(sourceEntity.location);
        lockedChestClose(block, sourceEntity.dimension, sourceEntity);
    };
}, {namespaces: ['vtng_rt']});

const playerTick = {};

world.afterEvents.itemStartUse.subscribe(xZ => {
    const { itemStack, source } = xZ;
    if (itemStack?.typeId == 'vtng_rt:portable_fan') {
        if (playerTick[source.id] == undefined) playerTick[source.id] = system.runInterval(() => {
            const entities = source.getEntitiesFromViewDirection({maxDistance: 8});
            if (!entities) return;
            const particleMovement = new MolangVariableMap();
            particleMovement.setSpeedAndDirection('movement', 3, source.getViewDirection());
            source.dimension.spawnParticle('vtng_rt:portable_fan', {x: source.location.x, y: source.location.y+1.2, z: source.location.z}, particleMovement);
            source.playAnimation('animation.vtng_rt.portable_fan.equipped', {blendOutTime: 999, stopExpression: 'q.ground_speed > 10000'});
            entities.forEach(e => {
                let direction = getDirection(source.getHeadLocation(), e.entity.location);
                const isItem = e.entity?.getComponent('minecraft:item');
                const isProjectile = e.entity?.getComponent('minecraft:projectile');
                if (!isItem && !isProjectile) e.entity?.applyKnockback(direction.x, direction.z, 0.5, source.getRotation().x < -15 ? 0.6 : -0.1);
                else e.entity.applyImpulse({x: direction.x * 0.5, y: direction.y * 0.5, z: direction.z * 0.5});
            });
            
        });
    };
});

world.afterEvents.itemStopUse.subscribe(xZ => {
    const { itemStack, source } = xZ;
    if (itemStack?.typeId == 'vtng_rt:portable_fan') {
        source.playAnimation('animation.player.attack.positions', {blendOutTime: 0.1, stopExpression: 'q.ground_speed < 10000'});
        if (playerTick[source.id]) system.clearRun(playerTick[source.id]);
        delete playerTick[source.id];
    };
});

world.beforeEvents.playerBreakBlock.subscribe(xZ => {
    const {block, dimension, player} = xZ;
    if (block?.typeId == 'vtng_rt:locked_chest') {
        const chestEntity = dimension.getEntities({location: block.center(), maxDistance: 0.2, closest: 1})[0];
        if (chestEntity.hasTag(`owner:${player.id}`)) return;
        if (!block.permutation.getState('vtng_rt:unlocked')) {
            xZ.cancel = true;
            system.run(() => {
                player.playSound('note.bass', {volume: 0.5});
                player.sendMessage({translate: 'vtng_rt.locked_chest_break.warn'});
            });
        };
    };
    if (block?.typeId == 'vtng_rt:lock_block') {
        const owner = world.getDynamicProperty(`lockOwner:${dimension.id},${block.x},${block.y},${block.z}`);
        if (owner == player.id) return;
        xZ.cancel = true;
        system.run(() => {
            player.playSound('note.bass', {volume: 0.5});
            player.sendMessage({translate: 'vtng_rt.lock_block_break.warn'});
        });
    };
});

world.afterEvents.dataDrivenEntityTrigger.subscribe(xZ => {
    const entity = xZ.entity;
    const dimension = entity.dimension;
    const block = dimension.getBlock(entity.location);
    const entityInv = entity.getComponent('inventory').container;
    for (let slot = 0; slot < entityInv?.size; slot++) {
        const item = entityInv?.getItem(slot);
        if (item) dimension.spawnItem(item, entity.location), entityInv?.setItem(slot, undefined);
    };
    world.setDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`, undefined)
    entity.remove();
}, {eventTypes: ['vtng_rt:despawn']});

world.afterEvents.playerInteractWithEntity.subscribe(xZ => {
    const {player, target} = xZ;
    if (target?.typeId != 'vtng_rt:locked_chest') return;
    const block = target.dimension.getBlock(target.location);
    lockedChestOpen(block, target.dimension, target);
});

function lockedChestOpen(block, dimension) {
    dimension.playSound('random.chestopen', block.center());
    let opening = system.runInterval(() => {
        const openState = block.permutation.getState('vtng_rt:open');
        block.setPermutation(block.permutation.withState('vtng_rt:open', openState+1));
        if (openState >= 4) system.clearRun(opening);
    });
};

function lockedChestClose(block, dimension) {
    if (block?.typeId != 'vtng_rt:locked_chest' || !block.permutation.getState('vtng_rt:unlocked')) return;
    dimension.playSound('random.chestclosed', block.center());
    let closing = system.runInterval(() => {
        if (block?.typeId != 'vtng_rt:locked_chest') system.clearRun(closing);
        const openState = block.permutation.getState('vtng_rt:open');
        block.setPermutation(block.permutation.withState('vtng_rt:open', openState-1));
        if (openState <= 1) system.clearRun(closing);
    });
    block.setPermutation(block.permutation.withState('vtng_rt:unlocked', false));
};

function enterPassword(block, dimension, player, chestEntity, error) {
    if (error) player.playSound('note.bass', {volume: 0.5});
    const deniedForm = new ui.ModalFormData();
    deniedForm.title('Release access');
    if (!error) deniedForm.textField('\nEnter password:', 'Example: password123');
    if (error) deniedForm.textField('§cIncorrect password!§r\n\nEnter password:', 'Example: password123');
    deniedForm.show(player).then(r => {
        if (r.canceled) return;
        if (r.formValues == world.getDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`)) {
            player.playSound('note.bell', {volume: 0.5});
            chestEntity.addTag(`${player.id}`);
            block.setPermutation(block.permutation.withState('vtng_rt:unlocked', true));
        } else {
            enterPassword(block, dimension, player, chestEntity, true)
        };
    });
};

function setPassword(block, dimension, player, chestEntity, error) {
    const passwordForm = new ui.ModalFormData();
    passwordForm.title('Set a Password!');
    if (!error)passwordForm.textField('\nEnter password:', 'Example: password123');
    if (error) passwordForm.textField('\n§cEnter at least 3 characters!!§r\n\nEnter password:', 'Example: password123');
    passwordForm.show(player).then(r => {
        if (r.canceled) return;
        if (r.formValues[0].length >= 3) {
            world.setDynamicProperty(`password:${dimension.id},${block.x},${block.y},${block.z}`, r.formValues[0]);
            chestEntity.addTag(`owner:${player.id}`);
            player.playSound('note.bell', {volume: 0.5});
        } else setPassword(block, dimension, player, chestEntity, true);
    });
}; 

export function fan(block, dimension, redstonePower) {
    let blockRotation = block.permutation.getState('minecraft:facing_direction');
    let particleSpeed = redstonePower * 0.4;
    let directions = {
        'north': {x: 0, y: 0, z: 1}, 'south': {x: 0, y: 0, z: -1}, 'east': {x: -1, y: 0, z: 0},
        'west': {x: 1, y: 0, z: 0}, 'up': {x: 0, y: 1, z: 0}, 'down': {x: 0, y: -1, z: 0}
    };
    let newBlock = block[correctDirections[blockRotation]['north']]();
    const particleMovement = new MolangVariableMap();
    particleMovement.setSpeedAndDirection('movement', particleSpeed, directions[blockRotation]);
    dimension.spawnParticle('vtng_rt:fan_push', block.center(), particleMovement);
    if (newBlock?.isAir) dimension.spawnParticle('vtng_rt:fan_pull', block.center(), particleMovement);
    for (let c = 1; c <= redstonePower; c++) {
        const frontBlock = block[correctDirections[blockRotation]['south']](c);
        if (!frontBlock?.isAir) break;
        const entities = dimension.getEntities({location: frontBlock?.bottomCenter(), maxDistance: 1, excludeFamilies: ['fabricate']});
        let direction = getDirection(block.bottomCenter(), frontBlock?.bottomCenter());
        entities.forEach(entity => {
            const isItem = entity?.getComponent('minecraft:item');
            const isProjectile = entity?.getComponent('minecraft:projectile');
            if (!isItem && !isProjectile) entity?.applyKnockback(direction.x, direction.z, 1 - c * (1 / redstonePower), blockRotation == 'up' ? -(1 - c * (1 / redstonePower)) : blockRotation == 'down' ? 1 - c * (1 / redstonePower) : 0);
            else entity.applyImpulse({x: direction.x * 0.02 * ((redstonePower+1) / c), y: direction.y * 0.02 * ((redstonePower+1) / c), z: direction.z * 0.02 * ((redstonePower+1) / c)});
        });
    };
    for (let c = 1; c <= (redstonePower / 2); c++) {
        const backBlock = block[correctDirections[blockRotation]['north']](c);
        if (!backBlock?.isAir) break;
        const entities = dimension.getEntities({location: backBlock?.bottomCenter(), maxDistance: 1, excludeFamilies: ['fabricate']});
        let direction = getDirection(backBlock.bottomCenter(), block?.bottomCenter());
        entities.forEach(entity => {
            const isItem = entity?.getComponent('minecraft:item');
            const isProjectile = entity?.getComponent('minecraft:projectile');
            if (!isItem && !isProjectile) entity?.applyKnockback(direction.x, direction.z, 1 - c * (1 / redstonePower), blockRotation == 'up' ? -(1 - c * (1 / redstonePower)) : blockRotation == 'down' ? 1 - c * (1 / redstonePower) : 0);
            else entity.applyImpulse({x: direction.x * 0.02 * ((redstonePower+1) / c), y: direction.y * 0.02 * ((redstonePower+1) / c), z: direction.z * 0.02 * ((redstonePower+1) / c)});
        });
    };
};

function getDirection(vetorA, vetorB) {
    const direction = {
        x: vetorB.x - vetorA.x,
        y: vetorB.y - vetorA.y,
        z: vetorB.z - vetorA.z
    };
    const magnitude = Math.sqrt(direction.x ** 2 + direction.y ** 2 + direction.z ** 2);
    return {x: direction.x / magnitude, y: direction.y / magnitude, z: direction.z / magnitude};
};

const invertFace = { 'north': 'south', 'south': 'north', 'east': 'west',  'west': 'east', 'up': 'down', 'down': 'up' };

export const correctDirections = {
    'north': {
        north: 'north', south: 'south', east: 'east', west: 'west', above: 'above', below: 'below'
    },
    'south': { 
        north: 'south', south: 'north', east: 'west', west: 'east', above: 'above', below: 'below'
    },
    'east': { 
        north: 'east', south: 'west', east: 'south', west: 'north', above: 'above', below: 'below'
    },
    'west': { 
        north: 'west', south: 'east', east: 'north', west: 'south', above: 'above', below: 'below'
    },
    'up': { 
        north: 'above', south: 'below', east: 'east', west: 'west', above: 'south', below: 'north'
    },
    'down': { 
        north: 'below', south: 'above', east: 'east', west: 'west', above: 'north', below: 'south'
    }
};