import { world, system, BlockPermutation, ItemStack } from "@minecraft/server"
import { Vector } from './vector.js'
import { ActionFormData } from "@minecraft/server-ui"



class RailPlacer {
    constructor() {

    }

    onItemUse(e) {
        const { itemStack, source } = e;
        if (source.typeId === "minecraft:player" && itemStack.typeId === "pb_red:spawn_rail_placer") {
            const minecart = source.getEntitiesFromViewDirection()[0]?.entity;
            if (!minecart) return;
            const alreadyplacer = source.dimension.getEntities({ type: "pb_red:rail_placer", location: minecart.location, maxDistance: 2 })[0]
            if (alreadyplacer) return;
            if (minecart.typeId === "minecraft:minecart" && this.isWithinDistance(minecart.location, source.location, 6)) {
                minecart.runCommand(`ride @s summon_rider pb_red:rail_placer`);
                const placerEntity = source.dimension.getEntities({ type: "pb_red:rail_placer", location: minecart.location, maxDistance: 2 })[0]
                placerEntity.nameTag = "§r"
                placerEntity.setRotation(minecart.getRotation())

                placerEntity.runCommand(`ride @s summon_rider pb_red:rail_placer_furnace`);
                const furnaceEntity = source.dimension.getEntities({ type: "pb_red:rail_placer_furnace", location: placerEntity.location, maxDistance: 2 })[0]
                furnaceEntity.nameTag = "§r"
                furnaceEntity.setRotation(placerEntity.getRotation())

                placerEntity.runCommand(`ride @s summon_rider pb_red:rail_placer_seat`);
                const seatEntity = source.dimension.getEntities({ type: "pb_red:rail_placer_seat", location: placerEntity.location, maxDistance: 2 })[0]
                seatEntity.nameTag = "§r"
                seatEntity.setRotation(placerEntity.getRotation())

                placerEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", "normal")
                placerEntity.setDynamicProperty("pb_red:rail_placer:placement_state", 1)

                minecart.dimension.runCommand(`playsound mob.agent.spawn @a ${minecart.location.x} ${minecart.location.y} ${minecart.location.z}`);
                source.runCommand(`clear @s ${itemStack.typeId} 0 1`);
            }

        }
    }

    isWithinDistance(pos1, pos2, maxDistance) {
        const distance = Math.sqrt(
            Math.pow(pos2.x - pos1.x, 2) +
            Math.pow(pos2.y - pos1.y, 2) +
            Math.pow(pos2.z - pos1.z, 2)
        );
        return distance <= maxDistance;
    }

    onScriptEventReceive(data) {
        const { id, sourceEntity } = data;

        if (id === 'pb_red:rail_placer_despawn') {
            this.handleplacerDespawn(sourceEntity);
        } else if (id === 'pb_red:rail_placer_working') {
            this.handleplacer(sourceEntity);
        } else if (id === 'pb_red:rail_placer_interact') {
            this.placerInteract(sourceEntity);
        }
    }

    placerInteract(sourceEntity) {
        const player = sourceEntity.dimension.getEntities({ type: "minecraft:player", location: sourceEntity.location, maxDistance: 10 })[0]
        if (player.isSneaking) {
            sourceEntity.dimension.runCommand(`playsound random.click @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`);

            const modeUI = new ActionFormData()
                .title("Placement Mode")
                .body("Select the placement mode:")
                .button("Normal")
                .button("1/2")
                .button("1/4")
                .button("1/6")
                .button("1/8")
                .button("Autonomous (1/6 with detector rail)")
                .show(player)
                .then((r) => {
                    if (r.canceled) return;
                    switch (r.selection) {
                        case 0: sourceEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", "normal"); break;
                        case 1: sourceEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", 2); break;
                        case 2: sourceEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", 4); break;
                        case 3: sourceEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", 6); break;
                        case 4: sourceEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", 8); break;
                        case 5: sourceEntity.setDynamicProperty("pb_red:rail_placer:placing_mode", "auto"); break;
                    }
                })

        } else {
            const minecart = sourceEntity.dimension.getEntities({
                location: sourceEntity.location,
                maxDistance: 2,
                type: 'minecraft:minecart'
            })[0];
            sourceEntity.dimension.runCommand(`playsound vr.stutterturn @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`);

            minecart.setRotation({
                x: minecart.getRotation().x,
                y: minecart.getRotation().y + 180
            });
        }
    }

    handleplacerDespawn(sourceEntity) {
        const placerEntity = sourceEntity.dimension.getEntities({ type: "pb_red:rail_placer", location: sourceEntity.location, maxDistance: 2 })[0]
        const inv = placerEntity.getComponent('inventory').container;
        const location = { x: placerEntity.location.x, y: placerEntity.location.y + 1, z: placerEntity.location.z };
        for (let i = 0; i < inv.size; i++) {
            const item = inv.getItem(i);
            if (item) sourceEntity.dimension.spawnItem(item, location);
        }

        const furnaceEntity = sourceEntity.dimension.getEntities({ type: "pb_red:rail_placer_furnace", location: sourceEntity.location, maxDistance: 2 })[0]
        const furnaceInv = furnaceEntity.getComponent('inventory').container;
        for (let i = 0; i < furnaceInv.size; i++) {
            const item = furnaceInv.getItem(i);
            if (item) sourceEntity.dimension.spawnItem(item, location);
        }

        const seatEntity = sourceEntity.dimension.getEntities({ type: "pb_red:rail_placer_seat", location: sourceEntity.location, maxDistance: 2 })[0]

        sourceEntity.dimension.spawnItem(new ItemStack("pb_red:spawn_rail_placer", 1), location);
        sourceEntity.dimension.runCommand(`playsound random.break @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z}`);

        furnaceEntity.remove()
        seatEntity.remove()
        placerEntity.remove();
    }

    handleplacer(sourceEntity) {
        if (!sourceEntity) return;

        try {
            let accessTest = sourceEntity.dimension;
        } catch {
            return;
        }

        const minecart = sourceEntity.dimension.getEntities({
            location: sourceEntity.location,
            maxDistance: 1,
            type: 'minecraft:minecart'
        })[0];
        const furnace = sourceEntity.dimension.getEntities({
            location: sourceEntity.location,
            maxDistance: 1.5,
            type: 'pb_red:rail_placer_furnace'
        })[0];
        const seat = sourceEntity.dimension.getEntities({
            location: sourceEntity.location,
            maxDistance: 2,
            type: 'pb_red:rail_placer_seat'
        })[0];

        if (!minecart || !furnace || !seat) return;

        sourceEntity.setRotation({
            x: minecart.getRotation().x,
            y: minecart.getRotation().y + 90
        });
        furnace.setRotation({
            x: sourceEntity.getRotation().x,
            y: sourceEntity.getRotation().y
        });
        seat.setRotation({
            x: sourceEntity.getRotation().x,
            y: sourceEntity.getRotation().y
        });

        const inv = sourceEntity.getComponent('inventory').container;
        const furnaceInv = furnace.getComponent('inventory').container;
        let globalSpeed = Math.sqrt(sourceEntity.getVelocity().x ** 2 + sourceEntity.getVelocity().z ** 2);

        this.placeIfPossible(sourceEntity, inv, furnaceInv, minecart, furnace);
    }

    getBlockPos(viewDir, sourceEntity) {
        const radians = (viewDir % 360) * (Math.PI / 180);
        const offset = {
            x: Math.cos(radians),
            z: Math.sin(radians),
        };

        return {
            x: sourceEntity.location.x + offset.x,
            y: sourceEntity.location.y,
            z: sourceEntity.location.z + offset.z
        };
    }

    isRail(block) {
        return block.typeId.includes("rail") && block.typeId.startsWith("minecraft:")
    }

    canBeRail(block) {
        return block.typeId == "minecraft:air"
            && !block.below().isAir
            && !block.below().typeId.includes("lava")
            && !block.below().typeId.includes("water")
    }

    getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    placeIfPossible(sourceEntity, inv, furnaceInv, minecart, furnaceEntity) {

        let viewDirection = sourceEntity.getRotation().y + 90;
        const blockPositions = {
            original: this.getBlockPos(viewDirection, sourceEntity),
            roundedUp: this.getBlockPos(viewDirection + 45, sourceEntity),
            roundedDown: this.getBlockPos(viewDirection - 45, sourceEntity),
        };
        const blockPos = blockPositions.original

        minecart.clearVelocity()

        const block = sourceEntity.dimension.getBlock(sourceEntity.location);
        const blockToPlace = sourceEntity.dimension.getBlock(blockPos);
        const blockToPlaceLeft = sourceEntity.dimension.getBlock(blockPositions.roundedDown);
        const blockToPlaceRight = sourceEntity.dimension.getBlock(blockPositions.roundedUp);

        if (!block || !blockToPlace || !blockToPlaceLeft || !blockToPlaceRight) return

        if (this.isRail(block)) {

            const generalSpeedFactor = 1
            const powerFactor = !block.typeId.includes("golden") ? (0.15 * generalSpeedFactor) : block.permutation.getState("rail_data_bit") == true ? (0.3 * generalSpeedFactor) : (0.6 * generalSpeedFactor)

            let previousRail = sourceEntity.getDynamicProperty("pb_red:rail_placer:previous_rail") || JSON.stringify({ x: 0, y: 0, z: 0 })
            previousRail = JSON.parse(previousRail)

            let rail1 = this.checkAndRemoveItems(inv, "minecraft:rail", 0, false);
            let rail2 = this.checkAndRemoveItems(inv, "minecraft:activator_rail", 0, false);
            let rail3 = this.checkAndRemoveItems(inv, "minecraft:detector_rail", 0, false);
            let rail4 = this.checkAndRemoveItems(inv, "minecraft:golden_rail", 0, false);
            const railCount = rail1 + rail2 + rail3 + rail4

            const coalCost = this.getRandomInt(0, 1);
            let availableCoal = this.checkAndRemoveItems(furnaceInv, "minecraft:coal", 0, false);
            let availableCharcoal = this.checkAndRemoveItems(furnaceInv, "minecraft:charcoal", 0, false);
            const totalFuelAvailable = availableCoal + availableCharcoal;

            furnaceEntity.setProperty("pb_red:activated", totalFuelAvailable > 0)

            if (totalFuelAvailable > 0) {
                if ((this.isRail(blockToPlace)) || ((this.isRail(blockToPlaceLeft) || this.isRail(blockToPlaceRight)))) {

                    if ((block.location.x !== previousRail.x || block.location.y !== previousRail.y || block.location.z !== previousRail.z)) {
                        if (availableCoal > 0) {
                            this.checkAndRemoveItems(furnaceInv, "minecraft:coal", coalCost, true);
                        } else {
                            this.checkAndRemoveItems(furnaceInv, "minecraft:charcoal", coalCost, true);
                        }
                        sourceEntity.setDynamicProperty("pb_red:rail_placer:previous_rail", JSON.stringify({ x: block.location.x, y: block.location.y, z: block.location.z }))
                    }

                    minecart.applyImpulse(Vector.subtract(blockPos, sourceEntity.location).multiply(powerFactor))

                } else {
                    if (railCount <= 0) return

                    if (this.canBeRail(blockToPlace) || viewDirection % 90 !== 0) {

                        if (viewDirection % 90 !== 0) {
                            if (this.canBeRail(blockToPlaceLeft)) {
                                this.placeRail(blockToPlaceLeft, minecart, blockPositions.roundedDown, sourceEntity, powerFactor, inv, furnaceInv, coalCost, availableCoal, rail1, rail4, rail3)

                            } else if (this.canBeRail(blockToPlaceRight)) {
                                this.placeRail(blockToPlaceRight, minecart, blockPositions.roundedUp, sourceEntity, powerFactor, inv, furnaceInv, coalCost, availableCoal, rail1, rail4, rail3)

                            }
                        } else {
                            this.placeRail(blockToPlace, minecart, blockPos, sourceEntity, powerFactor, inv, furnaceInv, coalCost, availableCoal, rail1, rail4, rail3)

                        }

                    }
                }
            }

        }
    }

    placeRail(block, minecart, pos, sourceEntity, powerFactor, inv, furnaceInv, coalCost, availableCoal, normal_rail, golden_rail, detector_rail) {

        if (availableCoal > 0) {
            this.checkAndRemoveItems(furnaceInv, "minecraft:coal", coalCost, true);
        } else {
            this.checkAndRemoveItems(furnaceInv, "minecraft:charcoal", coalCost, true);
        }

        let placingMode = sourceEntity.getDynamicProperty("pb_red:rail_placer:placing_mode")
        let placementState = sourceEntity.getDynamicProperty("pb_red:rail_placer:placement_state")
        let placementModeId = null

        if (placingMode == "auto" && detector_rail > 0) {
            placementModeId = (placementState > 1 && golden_rail > 0) ? "minecraft:golden_rail" : (detector_rail > 0) ? "minecraft:detector_rail" : null

        } else {
            placementModeId = (placementState == 1 && golden_rail > 0) ? "minecraft:golden_rail" : (normal_rail > 0) ? "minecraft:rail" : null

        }

        let railId = null
        for (let slot = 0; slot < inv.size; slot++) {
            const chestItem = inv.getItem(slot);
            if (!chestItem || !this.isRailItem(chestItem.typeId)) continue;

            if (placingMode !== "normal" && placementModeId !== null) {
                if (chestItem.typeId !== placementModeId) continue;
            }

            railId = chestItem.typeId
            if (chestItem.amount === 1) {
                inv.setItem(slot, null);
            } else {
                chestItem.amount--;
                inv.setItem(slot, chestItem);
            }
            break;
        }

        if (placingMode == "auto") placingMode = 6
        if (placingMode !== "normal" && placementModeId !== null) {
            sourceEntity.setDynamicProperty("pb_red:rail_placer:placement_state", placementState >= placingMode ? 1 : placementState + 1)
        } else {
            sourceEntity.setDynamicProperty("pb_red:rail_placer:placement_state", 1)
        }

        block.setType(railId);
        sourceEntity.dimension.runCommand(`playsound place.calcite @a ${sourceEntity.location.x} ${sourceEntity.location.y} ${sourceEntity.location.z} 1 0.7`);
        minecart.applyImpulse(Vector.subtract(pos, sourceEntity.location).multiply(powerFactor))
    }

    isRailItem(id) {
        const railItems = [
            "minecraft:rail",
            "minecraft:activator_rail",
            "minecraft:detector_rail",
            "minecraft:golden_rail",
        ]
        return railItems.includes(id)
    }

    checkAndRemoveItems(inventory, itemId, requiredAmount, removeItem) {
        let totalAmount = 0;
        let slotsToUpdate = [];

        // Iterate through the player's inventory to count items and track their slots
        for (let i = 0; i < inventory.size; i++) {
            const item = inventory.getItem(i);
            if (item && item.typeId === itemId) {
                totalAmount += item.amount;
                slotsToUpdate.push({
                    slot: i,
                    item,
                });
                if (totalAmount >= requiredAmount) break;
            }
        }

        if (!removeItem) return totalAmount

        // Check if the player has enough items
        if (totalAmount >= requiredAmount) {
            if (removeItem) {
                let amountToRemove = requiredAmount;

                // Remove the required amount of items from the inventory
                slotsToUpdate.forEach(({
                    slot,
                    item
                }) => {
                    if (amountToRemove > 0) {
                        if (item.amount > amountToRemove) {
                            item.amount -= amountToRemove;
                            inventory.setItem(slot, item);
                            amountToRemove = 0;
                        } else {
                            amountToRemove -= item.amount;
                            inventory.setItem(slot, null); // Clear the slot if all items are removed
                        }
                    }
                });
            }

            return true; // Successfully removed the required items
        } else {
            return false; // Not enough items
        }
    }


}




const placer = new RailPlacer();
world.afterEvents.itemUse.subscribe((e) => {
    placer.onItemUse(e)
})

system.afterEvents.scriptEventReceive.subscribe((data) => {
    placer.onScriptEventReceive(data)
})
