import { world, system, Block, BlockPermutation, MolangVariableMap, ItemStack  } from "@minecraft/server";
system.beforeEvents.startup.subscribe(init => {
    init.blockComponentRegistry.registerCustomComponent('qbdoors:door', {
        beforeOnPlayerPlace: (event, params) => {
            placeDoor(event, params.params.double_height, params.params.side_hinged );
        },
        onBreak: (event, params) => {
            if (params.params.double_height)
                breakDoor(event);
        },
        onRedstoneUpdate: (event, params) => {
            checkForRedstone(event, params.params.double_height, params.params.side_hinged);
        },
        onTick: (event, params) => {
            if (params.params.can_oxidize)
                checkOxidization(event, params.params.double_height);
        }
    });
});
system.beforeEvents.startup.subscribe(init => {
    init.blockComponentRegistry.registerCustomComponent('qbdoors:interact_door', {
        onPlayerInteract: (event) => {
            var doubleHeight = event.block.getComponent("qbdoors:door").customComponentParameters.params.double_height;
            var sideHingedDoor = event.block.getComponent("qbdoors:door").customComponentParameters.params.side_hinged;
            interactDoor(event, doubleHeight, sideHingedDoor);
        }
    });
});
system.beforeEvents.startup.subscribe(init => {
    init.blockComponentRegistry.registerCustomComponent('qbdoors:door_sounds', { });
});

function dump(object, levels, indent) {
    if (levels === undefined) levels = 1;
    if (indent === undefined) indent = 0;
    switch (typeof object) {
        case "string":
            return "\"" + object.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n") + "\"";
        case "number":
            return "" + object;
        case "bigint":
            return "(bigInt) " + object;
        case "boolean":
            return object ? "true" : "false";
        case "function":
            return "function()";
        case "symbol":
            return "(symbol) " + object;
        case "undefined":
            return "undefined";
    }
    if (object === null)
        return "null";
    if (Array.isArray(object)) {
        if (levels <= 0)
            return "Array (" + object.length + " items)";
        if (object.length === 0)
            return "[]";
        var lines = [];
        lines.push("[");
        for (var item in object) {
            lines.push(spaces(indent + 2) + dump(object[item], levels - 1, indent + 2));
        }
        lines.push(spaces(indent) + "]");
        return lines.join("\n");
    }
    if (levels <= 0)
        return "object()";
    var lines = [];
    lines.push("{");
    for (var item in object) {
        lines.push(spaces(indent + 2) + item + ": " + dump(object[item], levels - 1, indent + 2));
    }
    lines.push(spaces(indent) + "}");
    if (lines.length === 2)
        return "{}";
    return lines.join("\n");
}
function spaces(number) {
    return "                                ".substring(0, number);
}

function placeDoor(event, doubleHeightDoor, sideHingedDoor) {
    const { block, player, permutationToPlace, face } = event;
    var direction = Math.round(player.getRotation().y / 90) + 1;
    if (direction < 0) direction += 4;
    if (direction > 3) direction -= 4;
    var cardinalDirection = [ "west", "north", "east", "south" ][direction];
    if (doubleHeightDoor) {
        var blockAbove = block.above();
        if  (!blockAbove.isEmpty()) {
            event.cancel = true;
        }
        else if (sideHingedDoor) {
            var blockLeft = (direction === 0 ? block.north() : (direction === 1 ? block.east() : (direction === 2 ? block.south() : block.west())));
            var blockRight = (direction === 0 ? block.south() : (direction === 1 ? block.west() : (direction === 2 ? block.north() : block.east())));
            var door_hinge_bit = blockLeft.isDoor() || (blockLeft.isEmpty() && !blockRight.isEmpty() && !blockRight.isDoor());
            event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", cardinalDirection)
                                                         .withState("qbdoors:door_hinge_bit", door_hinge_bit)
                                                         .withState("qbdoors:locked", "unlocked");
            system.run(() => {
                blockAbove.setPermutation(permutationToPlace.withState("qbdoors:upper_block_bit", true)
                                                            .withState("qbdoors:cardinal_direction", cardinalDirection)
                                                            .withState("qbdoors:door_hinge_bit", door_hinge_bit)
                                                            .withState("qbdoors:locked", "unlocked"));
            });
        }
        else {
            event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", cardinalDirection)
                                                         .withState("qbdoors:locked", "unlocked");
            system.run(() => {
                blockAbove.setPermutation(permutationToPlace.withState("qbdoors:upper_block_bit", true)
                                                            .withState("qbdoors:cardinal_direction", cardinalDirection)
                                                            .withState("qbdoors:locked", "unlocked"));
            });
        }
    }
    else {
        var rayCastHit = player.getBlockFromViewDirection({ includeLiquidBlocks: false});
        var faceLocation = rayCastHit ? rayCastHit.faceLocation : { x: 0.5, z: 0.5 };
        if (rayCastHit && face !== rayCastHit.face) {
            if (rayCastHit.face === "North")
                faceLocation.z = 0;
            else if (rayCastHit.face === "South")
                faceLocation.z = 1;
            if (rayCastHit.face === "West")
                faceLocation.x = 0;
            else if (rayCastHit.face === "East")
                faceLocation.x = 1;
        }
        switch (face) {
            case "North":
                event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", "north")
                                                             .withState("qbdoors:hinge_side", faceLocation.x < 0.5 ? "right" : "left")
                                                             .withState("qbdoors:locked", "unlocked");
                break;
            case "East":
                event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", "east")
                                                             .withState("qbdoors:hinge_side", faceLocation.z < 0.5 ? "right" : "left")
                                                             .withState("qbdoors:locked", "unlocked");
                break;
            case "South":
                event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", "south")
                                                             .withState("qbdoors:hinge_side", faceLocation.x > 0.5 ? "right" : "left")
                                                             .withState("qbdoors:locked", "unlocked");
                break;
            case "West":
                event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", "west")
                                                             .withState("qbdoors:hinge_side", faceLocation.z > 0.5 ? "right" : "left")
                                                             .withState("qbdoors:locked", "unlocked");
                break;
            case "Down":
                event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", cardinalDirection)
                                                             .withState("qbdoors:hinge_side", "up")
                                                             .withState("qbdoors:locked", "unlocked");
                break;
            default:
                event.permutationToPlace = permutationToPlace.withState("qbdoors:cardinal_direction", cardinalDirection)
                                                             .withState("qbdoors:hinge_side", "down")
                                                             .withState("qbdoors:locked", "unlocked");
                break;
        }
    }
}
function breakDoor(event) {
    const { block, entitySource, brokenBlockPermutation } = event;
    var otherBlock = brokenBlockPermutation.getState("qbdoors:upper_block_bit") ? block.below() : block.above();
    if ((otherBlock.isDoor() || otherBlock.isGatedoor())) {
        if (entitySource && entitySource.typeId === "minecraft:player" && entitySource.getGameMode() !== "Creative")
            block.dimension.runCommand(`setblock ${otherBlock.x} ${otherBlock.y} ${otherBlock.z} minecraft:air [] destroy`);
        else {
            otherBlock.setPermutation(BlockPermutation.resolve("minecraft:air"));
        }
    }
}
function interactDoor(event, doubleHeightDoor, sideHingedDoor, openSound, closeSound) {
    const { block, player } = event;
    var inHand = itemInHand(player);
    var changeMade = false;
    if (block.hasTag("copper")) {
        if (inHand && inHand.typeId === "minecraft:honeycomb") {
            changeMade = applyWaxToBlock(block, doubleHeightDoor, player)
        }
        else if (inHand && inHand.hasTag("is_axe")) {
            changeMade = applyAxeToBlock(block, doubleHeightDoor)
        }
    }
    if (!changeMade) {
        var lockedState = block.lockedState();
        if (lockedState !== "redstone" && lockedState !== "slimed") {
            var openDoor = !block.isOpenDoor(sideHingedDoor);
            var stateChanged = false;
            var keyId = null;
            var singleUsekeyId = null;
            if (block.isDoor()) {
                keyId = block.permutation.type.id.replace("_door", "_key");
                singleUsekeyId = block.permutation.type.id.replace("_door", "_singleuse_key");
            }
            else if (block.isGatedoor()) {
                keyId = block.permutation.type.id.replace("glass_pane_", "").replace("_door", "_key");
                singleUsekeyId = block.permutation.type.id.replace("glass_pane_", "").replace("_door", "_singleuse_key");
            }
            if (inHand) {
                if (inHand.typeId !== keyId)
                    keyId = null;
                if (inHand.typeId !== singleUsekeyId)
                    singleUsekeyId = null;
                if (lockedState === "locked") {
                    if (keyId || singleUsekeyId) {
                        lockedState = "unlocked";
                        stateChanged = true;
                    }
                    if (singleUsekeyId)
                        reduceItemInHand(player);
                }
                else if (keyId || singleUsekeyId) {
                    lockedState = "locked";
                    stateChanged = true;
                }
                else if (inHand.typeId === "qbdoors:redstone_key") {
                    lockedState = "redstone";
                    openDoor = null;
                    stateChanged = true;
                }
                else if (inHand.typeId === "qbdoors:slime_key") {
                    lockedState = "slimed";
                    openDoor = null;
                    stateChanged = true;
                }
            }
            if (lockedState === "locked" && !stateChanged) {
                playDoorLockedSound(block);
                player.onScreenDisplay.setActionBar({ translate: "action.qbdoors:door.locked" });
            }
            else {
                if (stateChanged) {
                    playDoorLockSound(block);
                    if (lockedState === "locked") {
                        player.onScreenDisplay.setActionBar({ translate: "action.qbdoors:door.now_locked" });
                        openDoor = null;
                    }
                    else if (lockedState === "redstone") {
                        showParticleEffect(block, doubleHeightDoor, sideHingedDoor, openDoor, "qbdoors:redstone_particles");
                    }
                    else if (lockedState === "slimed") {
                        showParticleEffect(block, doubleHeightDoor, sideHingedDoor, openDoor, "qbdoors:slime_particles");
                    }
                }
                setDoorStates(block, sideHingedDoor, doubleHeightDoor, player, openDoor, lockedState);
            }
        }
    }
}
function setDoorStates(block, sideHingedDoor, doubleHeightDoor, player, openDoor, lockedState) {
    if (sideHingedDoor) {
        block.setPermutation(block.withDoorStates(openDoor, lockedState));
        if (doubleHeightDoor) {
            var otherBlock = block.permutation.getState("qbdoors:upper_block_bit") ? block.below() : block.above();
            if (otherBlock.isDoor())
                otherBlock.setPermutation(otherBlock.withDoorStates(openDoor, lockedState));
        }
    }
    else {
        var openState = null;
        if (openDoor !== null) {
            openState = "closed";
            if (openDoor) {
                if (!player)
                    openState = "inwards";
                else {
                    switch(block.permutation.getState("qbdoors:cardinal_direction")) {
                        case "north":
                            openState = block.location.z + 0.5 > player.location.z ? "outwards" : "inwards";
                            break;
                        case "east":
                            openState = block.location.x + 0.5 < player.location.x ? "outwards" : "inwards";
                            break;
                        case "south":
                            openState = block.location.z + 0.5 < player.location.z ? "outwards" : "inwards";
                            break;
                        case "west":
                            openState = block.location.x + 0.5 > player.location.x ? "outwards" : "inwards";
                            break;
                    }
                }
            }
        }
        block.setPermutation(block.withDoorStates(openState, lockedState));
        if (doubleHeightDoor) {
            var otherBlock = block.permutation.getState("qbdoors:upper_block_bit") ? block.below() : block.above();
            if (otherBlock.isGatedoor())
                otherBlock.setPermutation(otherBlock.withDoorStates(openState, lockedState));
        }
    }
    if (openDoor !== null)
        playDoorSound(block, openDoor);
}
function breakDoorBlockParts(block, permutation) {
    if (permutation.hasTag("door")) {
        var breakBlock = null;
        if (permutation.getState("qbdoors:upper_block_bit") && block.below().isDoor() && !block.below().permutation.getState("qbdoors:upper_block_bit"))
            breakBlock = block.below();
        else if (!permutation.getState("qbdoors:upper_block_bit") && block.above().isDoor() && block.above().permutation.getState("qbdoors:upper_block_bit"))
            breakBlock = block.above();
        if (breakBlock) {
            breakBlock.dimension.runCommand(`setblock ${breakBlock.x} ${breakBlock.y} ${breakBlock.z} minecraft:air [] destroy`);
        }
    }
    else if (permutation.hasTag("gatedoor")) {
        var breakBlock = null;
        if (permutation.getState("qbdoors:upper_block_bit") && block.below().isGatedoor() && !block.below().permutation.getState("qbdoors:upper_block_bit"))
            breakBlock = block.below();
        else if (!permutation.getState("qbdoors:upper_block_bit") && block.above().isGatedoor() && block.above().permutation.getState("qbdoors:upper_block_bit"))
            breakBlock = block.above();
        if (breakBlock) {
            breakBlock.dimension.runCommand(`setblock ${breakBlock.x} ${breakBlock.y} ${breakBlock.z} minecraft:air [] destroy`);
        }
    }
}
function showParticleEffect(block, doubleHeightDoor, sideHingedDoor, openState, particleEffect) {
    if (doubleHeightDoor && block.permutation.getState("qbdoors:upper_block_bit"))
        block = block.below();
    var location = { x: block.location.x + 0.5, y: block.location.y + 0.5, z: block.location.z + 0.5 };
    if (doubleHeightDoor && sideHingedDoor) {
        if (openState === null)
            openState = block.permutation.getState("qbdoors:open_bit");
        var state = block.permutation.getState("qbdoors:cardinal_direction") +
            (block.permutation.getState("qbdoors:door_hinge_bit") ? "_right" : "_left") +
            (openState ? "_open" : "_closed");
        switch (state) {
            case "north_left_closed":
            case "north_right_closed":
            case "west_left_open":
            case "east_right_open":
                location.z -= 0.4;
                break;
            case "east_left_closed":
            case "east_right_closed":
            case "north_left_open":
            case "south_right_open":
                location.x += 0.4;
                break;
            case "south_left_closed":
            case "south_right_closed":
            case "east_left_open":
            case "west_right_open":
                location.z += 0.4;
                break;
            case "west_left_closed":
            case "west_right_closed":
            case "south_left_open":
            case "north_right_open":
                location.x -= 0.4;
                break;
        }
    }
    else if (sideHingedDoor) {
        if (openState === null)
            openState = block.permutation.getState("qbdoors:open_bit");
        var state = block.permutation.getState("qbdoors:cardinal_direction") +
            "_" + block.permutation.getState("qbdoors:hinge_side") +
            (openState ? "_open" : "_closed");
        switch (state) {
            case "south_left_closed":
            case "south_right_closed":
            case "east_right_open":
            case "west_left_open":
            case "south_down_open":
            case "south_up_open":
                location.z -= 0.4;
                break;
            case "west_left_closed":
            case "west_right_closed":
            case "south_right_open":
            case "north_left_open":
            case "west_down_open":
            case "west_up_open":
                location.x += 0.4;
                break;
            case "north_left_closed":
            case "north_right_closed":
            case "west_right_open":
            case "east_left_open":
            case "north_down_open":
            case "north_up_open":
                location.z += 0.4;
                break;
            case "east_left_closed":
            case "east_right_closed":
            case "north_right_open":
            case "south_left_open":
            case "east_down_open":
            case "east_up_open":
                location.x -= 0.4;
                break;
            case "south_down_closed":
            case "west_down_closed":
            case "north_down_closed":
            case "east_down_closed":
                location.y -= 0.4;
                break;
            case "south_up_closed":
            case "west_up_closed":
            case "north_up_closed":
            case "east_up_closed":
                location.y += 0.4;
                break;
        }
    }
    if (doubleHeightDoor)
        location.y += 0.5;
    block.dimension.spawnParticle(particleEffect, location);
}

function itemInHand(player) {
    var playerInventory = player.getComponent("minecraft:equippable");
    if (playerInventory) {
        var mainhandItemStack = playerInventory.getEquipmentSlot("Mainhand");
        if (mainhandItemStack && mainhandItemStack.hasItem()) {
            return mainhandItemStack;
        }
    }
    return null;
}
function reduceItemInHand(player) {
    var playerInventory = player.getComponent("minecraft:equippable");
    if (playerInventory) {
        var mainhandItemStack = playerInventory.getEquipmentSlot("Mainhand");
        if (mainhandItemStack) {
            if (mainhandItemStack.amount > 1)
                mainhandItemStack.amount--;
            else
                playerInventory.setEquipment("Mainhand", null);
        }
    }
}
function changeMainPermutation(block, typeId) {
    var permutation = BlockPermutation.resolve(typeId);
    var states = block.permutation.getAllStates();
    for (var i in states) {
        permutation = permutation.withState(i, states[i]);
    }
    block.setPermutation(permutation);
}

world.afterEvents.playerInventoryItemChange.subscribe((event) => {
    const { player, slot, itemStack } = event;
    if (itemStack && itemStack.typeId === "qbdoors:guidebook") {
        var playerInventory = player.getComponent("minecraft:inventory");
        playerInventory.container.getSlot(slot).setItem(guidebookItem());
    }
});
world.afterEvents.playerSpawn.subscribe((event) => {
    const { player, initialSpawn } = event;
    if (initialSpawn && player.getGameMode() !== "Adventure") {
        var guidebookState = world.getDynamicProperty("qbdoors:" + player.id + "_guidebook");
        if (guidebookState !== "received") {
            var playerInventory = player.getComponent("minecraft:inventory");
            playerInventory.container.addItem(guidebookItem());
            world.setDynamicProperty("qbdoors:" + player.id + "_guidebook", "received");
        }
    }
});
function guidebookItem() {
    var bookItem = new ItemStack("minecraft:writable_book", 1);
    var bookComponent = bookItem.getComponent("minecraft:book");
    for (var p = 0; p < 31; p++) {
        bookComponent.setPageContent(p, { translate: "book.qbdoors:book.page" + (p + 1) });
    }
    bookComponent.signBook("QB Doors", "Quaival");
    return bookItem;
}

function checkOxidization(event, doubleHeightDoor) {
    const { block } = event;
    if (block.permutation.hasTag("copper")) {
        var thisBlock = block.permutation.type.id;
        var changeToBlock = null;
        var probability = 0;
        if (thisBlock.startsWith("qbdoors:copper_")) {
            changeToBlock = thisBlock.replace("copper_", "exposed_copper_");
            probability = 0.05;
        }
        else if (thisBlock.startsWith("qbdoors:exposed_copper_")) {
            changeToBlock = thisBlock.replace("exposed_copper_", "weathered_copper_");
            probability = 0.2;
        }
        else if (thisBlock.startsWith("qbdoors:weathered_copper_")) {
            changeToBlock = thisBlock.replace("weathered_copper_", "oxidized_copper_");
            probability = 0.3;
        }
        if (changeToBlock) {
            if (Math.random() < probability * world.gameRules.randomTickSpeed) {
                if (doubleHeightDoor) {
                    if (block.permutation.getState("qbdoors:upper_block_bit") && block.below().permutation.type.id === thisBlock) {
                    }
                    else if (!block.permutation.getState("qbdoors:upper_block_bit") && block.above().permutation.type.id === thisBlock) {
                        changeMainPermutation(block, changeToBlock);
                        changeMainPermutation(block.above(), changeToBlock);
                    }
                    else {
                        changeMainPermutation(block, changeToBlock);
                    }
                }
                else {
                    changeMainPermutation(block, changeToBlock);
                }
            }
        }
    }
}
function applyWaxToBlock(block, doubleHeightDoor, player) {
    var thisBlock = block.permutation.type.id;
    var changeToBlock = null;
    if (thisBlock.startsWith("qbdoors:copper_")) {
        changeToBlock = thisBlock.replace("copper_", "waxed_copper_");
    }
    else if (thisBlock.startsWith("qbdoors:exposed_copper_")) {
        changeToBlock = thisBlock.replace("exposed_copper_", "waxed_exposed_copper_");
    }
    else if (thisBlock.startsWith("qbdoors:weathered_copper_")) {
        changeToBlock = thisBlock.replace("weathered_copper_", "waxed_weathered_copper_");
    }
    else if (thisBlock.startsWith("qbdoors:oxidized_copper_")) {
        changeToBlock = thisBlock.replace("oxidized_copper_", "waxed_oxidized_copper_");
    }
    if (changeToBlock) {
        system.run(() => {
            changeMainPermutation(block, changeToBlock);
            var particleOptions = new MolangVariableMap();
            particleOptions.setColorRGB("variable.color", { "red": 1.0, "green": 0.5, "blue": 0.0 });
            block.dimension.spawnParticle("qbdoors:wax_particles", { x: block.location.x + 0.5, y: block.location.y + 0.5, z: block.location.z + 0.5 }, particleOptions);
            block.dimension.playSound("copper.wax.on", block.location);
            if (doubleHeightDoor) {
                if (block.permutation.getState("qbdoors:upper_block_bit") && block.below().permutation.type.id === thisBlock) {
                    changeMainPermutation(block.below(), changeToBlock);
                    block.dimension.spawnParticle("qbdoors:wax_particles", { x: block.location.x + 0.5, y: block.location.y - 0.5, z: block.location.z + 0.5 }, particleOptions);
                }
                else if (!block.permutation.getState("qbdoors:upper_block_bit") && block.above().permutation.type.id === thisBlock) {
                    changeMainPermutation(block.above(), changeToBlock);
                    block.dimension.spawnParticle("qbdoors:wax_particles", { x: block.location.x + 0.5, y: block.location.y + 1.5, z: block.location.z + 0.5 }, particleOptions);
                }
            }
            if (changeToBlock && player.getGameMode() !== "Creative")
                reduceItemInHand(player);
        });
        return true;
    }
    else
        return false;
}
function applyAxeToBlock(block, doubleHeightDoor) {
    var thisBlock = block.permutation.type.id;
    var changeToBlock = null;
    var effectColor = null;
    if (thisBlock.startsWith("qbdoors:waxed_")) {
        changeToBlock = thisBlock.replace("waxed_", "");
        effectColor = { "red": 1.0, "green": 1.0, "blue": 1.0 };
    }
    else if (thisBlock.startsWith("qbdoors:exposed_copper_")) {
        changeToBlock = thisBlock.replace("exposed_copper_", "copper_");
        effectColor = { "red": 0.4, "green": 0.75, "blue": 0.6 };
    }
    else if (thisBlock.startsWith("qbdoors:weathered_copper_")) {
        changeToBlock = thisBlock.replace("weathered_copper_", "exposed_copper_");
        effectColor = { "red": 0.4, "green": 0.75, "blue": 0.6 };
    }
    else if (thisBlock.startsWith("qbdoors:oxidized_copper_")) {
        changeToBlock = thisBlock.replace("oxidized_copper_", "weathered_copper_");
        effectColor = { "red": 0.4, "green": 0.75, "blue": 0.6 };
    }
    if (changeToBlock) {
        system.run(() => {
            changeMainPermutation(block, changeToBlock);
            var particleOptions = new MolangVariableMap();
            particleOptions.setColorRGB("variable.color", effectColor);
            block.dimension.spawnParticle("qbdoors:wax_particles", { x: block.location.x + 0.5, y: block.location.y + 0.5, z: block.location.z + 0.5 }, particleOptions);
            block.dimension.playSound("copper.wax.off", block.location, { pitch: 0.9 + Math.floor(Math.random() * 3) / 10});
            if (doubleHeightDoor) {
                if (block.permutation.getState("qbdoors:upper_block_bit") && block.below().permutation.type.id === thisBlock) {
                    changeMainPermutation(block.below(), changeToBlock);
                    block.dimension.spawnParticle("qbdoors:wax_particles", { x: block.location.x + 0.5, y: block.location.y - 0.5, z: block.location.z + 0.5 }, particleOptions);
                }
                else if (!block.permutation.getState("qbdoors:upper_block_bit") && block.above().permutation.type.id === thisBlock) {
                    changeMainPermutation(block.above(), changeToBlock);
                    block.dimension.spawnParticle("qbdoors:wax_particles", { x: block.location.x + 0.5, y: block.location.y + 1.5, z: block.location.z + 0.5 }, particleOptions);
                }
            }
        });
        return true;
    }
    else
        return false;
}

Block.prototype.isEmpty = function() {
    return this.isAir || this.isLiquid;
}
Block.prototype.isDoor = function() {
    return this.permutation.hasTag("door");
}
Block.prototype.isGatedoor = function() {
    return this.permutation.hasTag("gatedoor");
}
Block.prototype.isHatchdoor = function() {
    return this.permutation.hasTag("hatchdoor");
}
Block.prototype.isAnyDoor = function() {
    return this.isDoor() || this.isGatedoor() || this.isHatchdoor();
}
Block.prototype.withDoorStates = function(openState, lockedState) {
    var permutation = this.permutation;
    if (openState === true || openState === false)
        permutation = permutation.withState("qbdoors:open_bit", openState);
    if (openState === "closed" || openState === "outwards" || openState === "inwards")
        permutation = permutation.withState("qbdoors:open_state", openState);
    if (lockedState !== null)
        permutation = permutation.withState("qbdoors:locked", lockedState);
    return permutation;
}
Block.prototype.isOpenDoor = function (sideHingedDoor) {
    return sideHingedDoor ? this.permutation.getState("qbdoors:open_bit") : this.permutation.getState("qbdoors:open_state") !== "closed";
}
Block.prototype.lockedState = function () {
    var lockedState = this.permutation.getState("qbdoors:locked");
    return lockedState === undefined ? "unlockable" : lockedState;
}
function checkForRedstone(event, doubleHeightDoor, sideHingedDoor) {
    const { block } = event;
    if (block) {
        var lockedState = block.lockedState();
        if (lockedState === "locked" || lockedState === "slimed")
            return;
        var powerLevel = event.powerLevel;
        var previouslyPowerLevel = event.previousPowerLevel;
        var openIfClosed = powerLevel > previouslyPowerLevel;
        var closeIfOpen = previouslyPowerLevel !== 0 && powerLevel === 0;
        var isOpen = block.isOpenDoor(sideHingedDoor);
        if (openIfClosed && !isOpen) {
            switch (lockedState) {
                case "redstone":
                    setDoorStates(block, sideHingedDoor, doubleHeightDoor, null, true, null);
                    break;
                case "unlocked":
                    setDoorStates(block, sideHingedDoor, doubleHeightDoor, null, true, null);
                    break;
            }
        }
        else if (closeIfOpen && isOpen) {
            switch (lockedState) {
                case "redstone":
                    setDoorStates(block, sideHingedDoor, doubleHeightDoor, null, false, null);
                    break;
                case "unlocked":
                    setDoorStates(block, sideHingedDoor, doubleHeightDoor, null, false, null);
                    break;
            }
        }
    }
}

function playDoorSound_OLD(block, opening) {
    var blockId = block.permutation.type.id.substring("qbdoors:".length);
    if (opening) {
        if (soundsOpen[blockId])
            block.dimension.playSound(soundsOpen[blockId], block.location);
        else if (block.isHatchdoor())
            block.dimension.playSound("open.wooden_trapdoor", block.location);
        else
            block.dimension.playSound("open.wooden_door", block.location);
    }
    else {
        if (soundsClose[blockId])
            block.dimension.playSound(soundsClose[blockId], block.location);
        else if (block.isHatchdoor())
            block.dimension.playSound("close.wooden_trapdoor", block.location);
        else
            block.dimension.playSound("close.wooden_door", block.location);
    }
}
function playDoorSound(block, opening) {
    var sound = opening ? block.getComponent("qbdoors:door_sounds").customComponentParameters.params.open :
                          block.getComponent("qbdoors:door_sounds").customComponentParameters.params.close;
    block.dimension.playSound(sound, block.location);
}
function playDoorLockSound(block) {
    block.dimension.playSound("random.door.lock", block.location, { "volume": 0.5 });
}
function playDoorLockedSound(block) {
    block.dimension.playSound("random.door.locked", block.location, { "pitch": 1.5, "volume": 0.2 });
}
var soundsOpen = {
    "glass_door": "mob.shulker.open",
    "glass_pane_door": "mob.shulker.open",
    "glass_hatchdoor": "mob.shulker.open",
    "netherite_door": "open.iron_door",
    "gold_door": "open.iron_door",
    "iron_bars_door": "open.iron_trapdoor",
    "netherite_hatchdoor": "open.iron_trapdoor",
    "hay_door": "use.moss",
    "stone_door": "block.grindstone.use",
    "stone_hatchdoor": "block.grindstone.use",
    "sandstone_door": "block.grindstone.use",
    "sandstone_hatchdoor": "block.grindstone.use",
    "red_sandstone_door": "block.grindstone.use",
    "red_sandstone_hatchdoor": "block.grindstone.use",
    "quartz_door": "open.bamboo_wood_fence_gate",
    "quartz_hatchdoor": "open.bamboo_wood_fence_gate",
    "bamboo_hatchdoor": "open.bamboo_wood_trapdoor",
    "cherry_hatchdoor": "open.cherry_wood_trapdoor",
    "crimson_hatchdoor": "open.nether_wood_trapdoor",
    "warped_hatchdoor": "open.nether_wood_trapdoor"
}
var soundsClose = {
    "glass_door": "mob.shulker.close",
    "glass_pane_door": "mob.shulker.close",
    "glass_hatchdoor": "mob.shulker.close",
    "netherite_door": "close.iron_door",
    "gold_door": "close.iron_door",
    "iron_bars_door": "close.iron_trapdoor",
    "netherite_hatchdoor": "close.iron_trapdoor",
    "hay_door": "dig.moss",
    "stone_door": "block.grindstone.use",
    "stone_hatchdoor": "block.grindstone.use",
    "sandstone_door": "block.grindstone.use",
    "sandstone_hatchdoor": "block.grindstone.use",
    "red_sandstone_door": "block.grindstone.use",
    "red_sandstone_hatchdoor": "block.grindstone.use",
    "quartz_door": "close.bamboo_wood_fence_gate",
    "quartz_hatchdoor": "close.bamboo_wood_fence_gate",
    "bamboo_hatchdoor": "close.bamboo_wood_trapdoor",
    "cherry_hatchdoor": "close.cherry_wood_trapdoor",
    "crimson_hatchdoor": "close.nether_wood_trapdoor",
    "warped_hatchdoor": "close.nether_wood_trapdoor"
}
