import { world, EquipmentSlot } from '@minecraft/server';
import { print } from './print';
//optomization sugestion 
// instead of looping effects. apply once when triggered for max time 
// and use player.runCommand(/effect 'effectType' 0 0 false) to remove the effect from the player
/**
 * Array of armor names to compare with item IDs.
 * @type {string[]>}
 * @param ["minecraft:apple"]
 */
const armorSetNames = new Array("fire", "water", "purpur", "skeletal", "mega", "warden");
let previousEquipmentState = new Map();
export let currentArmorSet = "none";
/**
 * Calculates the knockback vector based on an entity's position, a force source position, and the force magnitude.
 * @param {Vector3} entityPosition - The position of the entity as a Vector3.
 * @param {Vector3} forceSourcePosition - The position of the force source as a Vector3.
 * @param {Vector3} forceMagnitude - The magnitude of the force to be applied.
 * @returns {Vector3} The knockback vector as a Vector3.
 */
export function calculateKnockbackVector(entityPosition, pusherPosition, forceMagnitude) {
    let direction = {
        x: entityPosition.x - pusherPosition.x,
        y: entityPosition.y - pusherPosition.y,
        z: entityPosition.z - pusherPosition.z
    };
    let distance = magnitude(direction);
    // Normalize the direction vector so it has a magnitude of 1
    direction = {
        x: direction.x / distance,
        y: direction.y / distance,
        z: direction.z / distance
    };
    // Scale the direction vector by the force magnitude to get the final knockback vector
    let knockback = {
        x: direction.x * forceMagnitude,
        y: direction.y * forceMagnitude,
        z: direction.z * forceMagnitude
    };
    return knockback;
}
function magnitude(vector) {
    return Math.sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
}
/**
 * Retrieves the current state of a player's armor equipment slots.
 *
 * @param {Object} playerEquipment - The container representing the player's inventory.
 * @returns {Map<string, number>} A map of item type IDs based on the equipment slot.
 */
function getEquipmentState(playerEquipment) {
    var _a, _b, _c, _d, _e, _f, _g, _h;
    let state = new Map();
    if (((_a = playerEquipment.getEquipment(EquipmentSlot.Head)) === null || _a === void 0 ? void 0 : _a.typeId) !== undefined) {
        state.set("head", (_b = playerEquipment.getEquipment(EquipmentSlot.Head)) === null || _b === void 0 ? void 0 : _b.typeId);
    }
    if (((_c = playerEquipment.getEquipment(EquipmentSlot.Chest)) === null || _c === void 0 ? void 0 : _c.typeId) !== undefined) {
        state.set("chest", (_d = playerEquipment.getEquipment(EquipmentSlot.Chest)) === null || _d === void 0 ? void 0 : _d.typeId);
    }
    if (((_e = playerEquipment.getEquipment(EquipmentSlot.Legs)) === null || _e === void 0 ? void 0 : _e.typeId) !== undefined) {
        state.set("legs", (_f = playerEquipment.getEquipment(EquipmentSlot.Legs)) === null || _f === void 0 ? void 0 : _f.typeId);
    }
    if (((_g = playerEquipment.getEquipment(EquipmentSlot.Feet)) === null || _g === void 0 ? void 0 : _g.typeId) !== undefined) {
        state.set("feet", (_h = playerEquipment.getEquipment(EquipmentSlot.Feet)) === null || _h === void 0 ? void 0 : _h.typeId);
    }
    return state;
}
/**
 * Checks if there has been a change in the player's armor equipment slots since the last check.
 *
 * @param {Object} playerEquipment - The container representing the player's inventory.
 * @returns {boolean} True if the equipment slots have changed, false otherwise.
 */
function hasEquipmentChanged(player) {
    let playerEquipment = player.getComponent("equippable");
    const currentState = getEquipmentState(playerEquipment);
    if (currentState.size !== previousEquipmentState.get(player.name).size) {
        return true;
    }
    for (let [position, id] of currentState) {
        if (previousEquipmentState.get(player.name).get(position) !== id) {
            return true;
        }
    }
    return false;
}
/**
 * Checks if the player is wearing a full set of armor by comparing the item id with the armorSetNames array.
 *
 * @param {Object} equipmentState - The entities equipmentState.
 * @returns {boolean, armorSet} Fals if no full set was detected, armorSet if a full set is equiped by the player.
 */
function isArmorSet(equipmentState) {
    if (!equipmentState.has("head") || !equipmentState.has("chest") || !equipmentState.has("legs") || !equipmentState.has("feet")) {
        currentArmorSet = "none";
        return false;
    }
    let headEquipment = equipmentState.get("head");
    if (headEquipment.startsWith('ss')) {
        let armorSet = processString(equipmentState.get("head"));
        //print(armorSet)
        if (!armorSetNames.includes(armorSet)) {
            currentArmorSet = "none";
            return false;
        }
        for (let [position, id] of equipmentState) {
            if (processString(id) !== armorSet) {
                currentArmorSet = "none";
                return false;
            }
        }
        currentArmorSet = armorSet;
        return armorSet;
    }
    return false;
}
function processString(inputString) {
    // Remove prefix "ss_hyd:"
    let stringWithoutPrefix = inputString.replace(/^ss_hyd:/, '');
    // Extract part before the first underscore
    let parts = stringWithoutPrefix.split('_');
    if (parts.length > 1) {
        return parts[0]; // Return the part before the first underscore
    }
    //print(stringWithoutPrefix.toString())
    return stringWithoutPrefix; // Return the original string if there is no underscore
}
function fire_hydra(player) {
    //print("armor skill fire_hydra")
    player.addEffect("fire_resistance", 205, { amplifier: 1, showParticles: false });
    //GET PLAYER LOCATION AND REPLACE LAVA WITH COBBLESTONE BLOCK IF ABOVE
}
function water_hydra(player) {
    //print("armor skill water_hydra ")
    player.addEffect("water_breathing", 205, { amplifier: 1, showParticles: false });
    if (player.isInWater) {
        player.addEffect("conduit_power", 205, { amplifier: 1, showParticles: false });
    }
}
function purpur_hydra(player) {
    //print("armor skill purpur_hydra ")
    player.addEffect("night_vision", 205, { amplifier: 1, showParticles: false });
}
function skeletal_hydra(player) {
    //print("armor skill skeletal_hydra")
    player.addEffect("night_vision", 205, { amplifier: 1, showParticles: false });
}
function mega_hydra(player) {
    //print("armor skill mega_hydra")
    player.addEffect("resistance", 205, { amplifier: 2, showParticles: false });
    player.addEffect("fire_resistance", 205, { amplifier: 1, showParticles: false });
    player.addEffect("water_breathing", 205, { amplifier: 2, showParticles: false });
    player.addEffect("jump_boost", 205, { amplifier: 2, showParticles: false });
    if (player.isInWater) {
        player.addEffect("conduit_power", 205, { amplifier: 1, showParticles: false });
    }
}
function warden_hydra(player) {
    //print("armor skill mega_hydra")
    player.addEffect("resistance", 205, { amplifier: 1, showParticles: false });
}
/**
 * Checks if the the attacking entity of the entityHitEntity event is a player if yes and the a relevant armor skill is equipped it applies effects onto the hit entity
 *
 * @param {Object} event - entityHitEntity event.
 */
export function armorEntityHitEntityHandler(event) {
    if (event.hitEntity.typeId !== "minecraft:player") {
        return;
    }
    if (event.hitEntity.typeId == "minecraft:player" && isArmorSet(getEquipmentState(event.hitEntity.getComponent("equippable"))) == "fire") {
        ////print("hit " + currentArmorSet + " " + event.hitEntity.typeId + " " + event.hitEntity.typeId)
        event.damagingEntity.setOnFire(4, false);
    }
    if (event.hitEntity.typeId == "minecraft:player" && isArmorSet(getEquipmentState(event.hitEntity.getComponent("equippable"))) == "water") {
        ////print("hit " + currentArmorSet + " " + event.hitEntity.typeId + " " + event.hitEntity.typeId)
        event.damagingEntity.addEffect("slowness", 100, { amplifier: 1, showParticles: true });
    }
    if (event.hitEntity.typeId == "minecraft:player" && isArmorSet(getEquipmentState(event.hitEntity.getComponent("equippable"))) == "purpur") {
        ////print("hit " + currentArmorSet + " " + event.hitEntity.typeId + " " + event.hitEntity.typeId)
        event.damagingEntity.addEffect("poison", 100, { amplifier: 1, showParticles: true });
    }
    if (event.hitEntity.typeId == "minecraft:player" && isArmorSet(getEquipmentState(event.hitEntity.getComponent("equippable"))) == "wither") {
        event.damagingEntity.addEffect("wither", 100, { amplifier: 1, showParticles: true });
    }
    if (event.hitEntity.typeId == "minecraft:player" && isArmorSet(getEquipmentState(event.hitEntity.getComponent("equippable"))) == "mega") {
        ////print("hit " + currentArmorSet + " " + event.hitEntity.typeId + " " + event.hitEntity.typeId)
        event.damagingEntity.addEffect("levitation", 60, { amplifier: 2, showParticles: true });
        const kbVector = calculateKnockbackVector(event.damagingEntity.location, event.hitEntity.location, 1);
        event.damagingEntity.applyKnockback(kbVector.x, kbVector.z, 2, 0.3);
    }
}
export function armorEatingAfterHandler(event) {
    if (event.source.typeId !== "minecraft:player") {
        return;
    }
    const item = event.itemStack.typeId;
    const validFoodItems = ["minecraft:chicken", "minecraft:porkchop", "minecraft:beef", "minecraft:mutton", "minecraft:rabbit", "minecraft:cod", "minecraft:salmon"];
    if (isArmorSet(getEquipmentState(event.source.getComponent("equippable"))) === "zombie" && validFoodItems.includes(item)) {
        event.source.addEffect("saturation", 20, { amplifier: 1, showParticles: false });
    }
}
/**
 * Handels armor skills for players.
 *
 * @param {Object} player - The player whose armor set effects should be applied.
 */
export function armorHandler(player) {
    const playerEquipment = player.getComponent('equippable');
    const playerName = player.name;
    if (!previousEquipmentState.has(playerName)) {
        previousEquipmentState.set(playerName, getEquipmentState(playerEquipment));
    }
    if (!hasEquipmentChanged(player)) {
        const currentArmorSet = isArmorSet(previousEquipmentState.get(playerName));
        if (currentArmorSet) {
            switch (currentArmorSet) {
                case armorSetNames[0]:
                    fire_hydra(player);
                    break;
                case armorSetNames[1]:
                    water_hydra(player);
                    break;
                case armorSetNames[2]:
                    purpur_hydra(player);
                    break;
                case armorSetNames[3]:
                    skeletal_hydra(player);
                    break;
                case armorSetNames[4]:
                    mega_hydra(player);
                    break;
                case armorSetNames[5]:
                    warden_hydra(player);
                    break;
                default:
                    return;
            }
        }
        return;
    }
    previousEquipmentState.set(playerName, getEquipmentState(playerEquipment));
    //print("equipment changed")
    const currentArmorSet = isArmorSet(previousEquipmentState.get(playerName));
    if (!currentArmorSet) {
        //print("no armor set detected")
        return;
    }
    //print("armor set " + currentArmorSet + " detected!")
}
