import * as mc from "@minecraft/server";
import * as lib from '../lib/lib';
import * as config from '../pb_scraft_config';
import * as altarconfig from '../pb_scraft_altar_config';
import * as manatank from "./mana_tank";
import * as mana_network from '../lib/mana_network';
import { getPedestalEntity } from "./arcane_pedestal";
import { beamBetweenLocations } from "../lib/beam_utils";
import * as item_assembler from "../lib/item_assembler";

const ALTAR_RADIUS = 4;
const MAX_PEDESTALS = 5;
const MANA_CONSUMPTION = 10;

export class arcaneAltarManager {
    static beforeplace(data) {
        data.permutationToPlace = mana_network.connectToPipes(data.block, data.permutationToPlace);
    }

    static destroy(data) {
        mana_network.disconnectFromPipes(data.block, data.destroyedBlockPermutation);
    }

    static interact(data) {
        const block = data.block;
        const player = data.player;

        const mainhand = player.getComponent(mc.EntityEquippableComponent.componentId).getEquipmentSlot(mc.EquipmentSlot.Mainhand);
        const heldItem = mainhand.getItem();
        if (heldItem == undefined) return;

        if (heldItem.typeId == "pb_scraft:mana_meter") {

            const operationalData = this.checkOperational(data);
            player.sendMessage(operationalData.message);
            player.dimension.playSound("pb_scraft.interact_contraption", data.block.location);
            operationalData.pedestalsBlocks.forEach(pedestal => {

                lib.highlightBlock(pedestal);
            });
            return;
        }

        this.checkCrafting(data, heldItem, player);
    }

    static tick(data) {
        const block = data.block;
        const location = block.center();
        const fuel = block.permutation.getState("pb_scraft:fuel");

        if (fuel == MANA_CONSUMPTION) {
            block.dimension.playSound("pb_scraft.altar_idle", location);
            mc.world.getDimension(block.dimension.id).spawnParticle("pb_scraft:altar_ambient_1", location);
            mc.world.getDimension(block.dimension.id).spawnParticle("pb_scraft:altar_ambient_1", location);
        }

        if (fuel != MANA_CONSUMPTION) {
            const pull_fuel = MANA_CONSUMPTION - fuel;

            const permutation = block.permutation;
            const north = block.north();
            const east = block.east();
            const south = block.south();
            const west = block.west();

            const northConnects = permutation.getState("pb_scraft:pipe_north");
            const eastConnects = permutation.getState("pb_scraft:pipe_east");
            const southConnects = permutation.getState("pb_scraft:pipe_south");
            const westConnects = permutation.getState("pb_scraft:pipe_west");

            const blockmap = new Map();
            blockmap.set(lib.locString(block.location), block);
            const network_range = 16;

            if (northConnects) mana_network.findTanks(north, blockmap, network_range);
            if (eastConnects) mana_network.findTanks(east, blockmap, network_range);
            if (southConnects) mana_network.findTanks(south, blockmap, network_range);
            if (westConnects) mana_network.findTanks(west, blockmap, network_range);

            for (let element of blockmap.values()) {

                if (element.type.id == "pb_scraft:mana_tank") {
                    //const tankfuel = element.permutation.getState("pb_scraft:fuel");

                    if (manatank.removeMana(element, pull_fuel)) //try to pull mana from nearest tank
                    {
                        block.setPermutation(block.permutation.withState('pb_scraft:fuel', fuel + pull_fuel));
                        break;
                    }
                }

            }
        }
    }

    static locationAdd(location1, location2) {
        return { x: location1.x + location2.x, y: location1.y + location2.y, z: location1.z + location2.z }
    }

    static checkOperational(data) {
        const block = data.block;
        const location = { x: block.location.x - ALTAR_RADIUS, y: block.location.y, z: block.location.z - ALTAR_RADIUS };
        const dimension = block.dimension;
        const fuel = block.permutation.getState("pb_scraft:fuel");

        let operational = true;
        let message = "§u[Magic]§r Altar is operational";
        let pedestals = [];

        let pedestals_found = 0;
        for (let i = 0; i < ALTAR_RADIUS * 2 + 1; i++) {
            for (let j = 0; j < ALTAR_RADIUS * 2 + 1; j++) {
                const pedestalblock = dimension.getBlock({ x: location.x + i, y: location.y, z: location.z + j });
                if (pedestalblock.type.id == "pb_scraft:arcane_pedestal") {
                    pedestals.push(pedestalblock);
                    pedestals_found++;
                }
            }
        };

        if (fuel != MANA_CONSUMPTION) {
            operational = false;
            message = "§u[Magic]§r Altar does not have enough Mana";
        };
        if (pedestals_found == 0) {
            operational = false;
            message = "§u[Magic]§r Altar does not have enough pedestals";
        };
        if (pedestals_found > 5) {
            operational = false;
            message = "§u[Magic]§r Altar has too many pedestals";
        };

        return {
            operational: operational,
            message: message,
            pedestalsBlocks: pedestals
        }
    }

    static checkCrafting(data, heldItem, player) {
        //try to find at least one recipe for the interacting item
        let altarItem = altarconfig.ALTAR_RECIPES.find((f) => f.altarItem == heldItem.typeId);
        if (altarItem == undefined) {
            player.sendMessage("§u[Magic]§r No recipe found for this item");
            return;
        }
        //check if altar is operational
        const operationalData = this.checkOperational(data);
        if (operationalData.operational == false) {
            player.sendMessage("§u[Magic]§r Altar is not operational");
            return;
        }
        //gather item info on pedestals
        let pedestalItems = [];
        let pedestalEntities = [];
        operationalData.pedestalsBlocks.forEach(pedestal => {

            const display_entity = getPedestalEntity(pedestal);
            if (display_entity != undefined) {
                pedestalItems.push(display_entity.getDynamicProperty("pb_scraft_id"));
                pedestalEntities.push(display_entity);
            }
        });

        //try to find the correct recipe for our case
        let altarRecipe;
        let resultItem;
        let consumeAltarItem = true;

        //check if we have a spell apply recipe
        resultItem = item_assembler.checkSpellApply(heldItem, pedestalEntities);

        //check if we have a spell removal recipe
        if (resultItem == undefined) resultItem = item_assembler.checkSpellRemove(heldItem, pedestalEntities);

        //check if we have a orb modification recipe
        if (resultItem == undefined) resultItem = item_assembler.checkOrbModify(heldItem, pedestalEntities);

        //check regular recipes
        for (let index = 0; index < altarconfig.ALTAR_RECIPES.length; index++) {
            const recipe = altarconfig.ALTAR_RECIPES[index];

            if (recipe.altarItem == heldItem.typeId && recipe.type != "mirror") {
                if (JSON.stringify(pedestalItems.sort()) === JSON.stringify(recipe.pedestalItems.sort())) {
                    altarRecipe = recipe;
                    break;
                }
            }

            if (recipe.altarItem == heldItem.typeId && recipe.type == "mirror") {
                const orbspelldata = config.SPELL_LIST.find((f) => f.identifier == heldItem.getDynamicProperty("pb_scraft_spell"));
                if (JSON.stringify(pedestalItems.sort()) === JSON.stringify(recipe.pedestalItems.sort()) && orbspelldata.identifier == recipe.spell_base) {
                    altarRecipe = recipe;
                    break;
                }
            }
        };

        if (resultItem == undefined && altarRecipe?.type == "generic") {
            resultItem = new mc.ItemStack(altarRecipe.result, 1);
        }

        if (resultItem == undefined && altarRecipe?.type == "staffup") {
            resultItem = new mc.ItemStack(altarRecipe.result, 1);

            const spellId = heldItem.getDynamicProperty("pb_scraft_spell");
            if (spellId != undefined)
            {
                const spellData = config.SPELL_LIST.find((f) => f.identifier == spellId);
                const glyph_1 = heldItem.getDynamicProperty("pb_scraft_glyph_1");
                const glyph_2 = heldItem.getDynamicProperty("pb_scraft_glyph_2");
                const itemdata = config.CAST_ITEM_LIST.find((f) => f.identifier == resultItem.typeId);

                resultItem = item_assembler.applySpell(resultItem, itemdata, spellData, glyph_1, glyph_2);
            }
            
        }

        if (resultItem == undefined && altarRecipe?.type == "spell") {
            resultItem = new mc.ItemStack(altarRecipe.result, 1);
            const spellData = config.SPELL_LIST.find((f) => f.identifier == altarRecipe.spell_result);
            resultItem = item_assembler.assembleSpellOrb(resultItem, spellData, "pb_scraft:empty", "pb_scraft:empty");

            consumeAltarItem = false;
        }

        if (resultItem == undefined && altarRecipe?.type == "mirror") {
            resultItem = item_assembler.checkOrbMirror(heldItem, pedestalEntities, altarRecipe);
        }

        if (resultItem == undefined) {
            player.sendMessage("§u[Magic]§r Recipe not viable");
            return;
        }

        if (consumeAltarItem && (player.getGameMode() != mc.GameMode.creative)) {
            const mainhand = player.getComponent(mc.EntityEquippableComponent.componentId).getEquipmentSlot(mc.EquipmentSlot.Mainhand);
            mainhand.setItem(lib.itemManager.reduceAmount(heldItem, 1));
        }

        //initiate crafting
        const dimension = player.dimension.id;
        player.dimension.playSound("pb_scraft.altar_trigger", data.block.location);

        //effects
        pedestalEntities.forEach(pedestal => {
            beamBetweenLocations(this.locationAdd(pedestal.location, { x: 0, y: 0.5, z: 0 }), data.block.above().bottomCenter(), dimension, ["pb_scraft:arc_lightning_90"]);
        });
        mc.world.getDimension(dimension).spawnParticle("pb_scraft:magic_summoning_rune", data.block.bottomCenter());
        mc.world.getDimension(dimension).spawnParticle("pb_scraft:altar_shine", data.block.center());

        //resest pedestal item displays early to prevent taking off items
        operationalData.pedestalsBlocks.forEach(pedestal => {
            pedestal.setPermutation(pedestal.permutation.withState('pb_scraft:item_displayed', 0));
        });
        //consume altar fuel
        if (player.getGameMode() != mc.GameMode.creative) data.block.setPermutation(data.block.permutation.withState('pb_scraft:fuel', 0));

        //1 sec delay
        mc.system.runTimeout(() => {
            //effects
            player.dimension.playSound("pb_scraft.altar_item_creation", data.block.location);
            pedestalEntities.forEach(pedestal => {
                mc.world.getDimension(dimension).spawnParticle("pb_scraft:altar_pedestal_clear", pedestal.location);
                pedestal.remove();
            });
            mc.world.getDimension(dimension).spawnParticle("pb_scraft:altar_clear", data.block.center());

            //spawn item
            const itementity = mc.world.getDimension(dimension).spawnItem(resultItem, data.block.above().center());
            itementity.clearVelocity();
            itementity.applyImpulse({ x: 0, y: 0.2, z: 0 });
        }, 20)

    }
}
