import { EquipmentSlot, ItemStack, GameMode, world, MinecraftDimensionTypes, } from "@minecraft/server";
import CustomVector3 from '../calc/vector3';
export const Dimensions = {
    "overworld": world.getDimension(MinecraftDimensionTypes.overworld),
    "nether": world.getDimension(MinecraftDimensionTypes.nether),
    "the_end": world.getDimension(MinecraftDimensionTypes.theEnd)
};
export function randomNum(min, max) {
    return Math.random() * (max - min) + min;
}
export function applyKnockbackFromLocation(fromLocation, entity, velocity) {
    const entityLoc = entity.location;
    const total = Math.abs(fromLocation.x - entityLoc.x) + Math.abs(fromLocation.y - entityLoc.y) + Math.abs(fromLocation.z - entityLoc.z);
    const direction = {
        x: (entityLoc.x - fromLocation.x) / total,
        y: (entityLoc.y - fromLocation.y) / total,
        z: (entityLoc.z - fromLocation.z) / total
    };
    try {
        entity.applyKnockback(direction.x, direction.z, (Math.abs(direction.x) + Math.abs(direction.z)) * velocity, direction.y * velocity);
    }
    catch { }
}
export function applyKnockbackFromEntity(source, entity, velocity) {
    const sourceLoc = source.location;
    const entityLoc = entity.location;
    const total = Math.abs(sourceLoc.x - entityLoc.x) + Math.abs(sourceLoc.z - entityLoc.z);
    const direction = {
        x: (entityLoc.x - sourceLoc.x) / total,
        z: (entityLoc.z - sourceLoc.z) / total
    };
    try {
        entity.applyKnockback(direction.x, direction.z, velocity.xz, velocity.y);
    }
    catch { }
}
/**
 * Retrieves the item in the specified equipment slot for a player.
 * @param player - The player entity.
 * @param slot - The equipment slot to retrieve the item from.
 * @returns The item in the specified equipment slot, or undefined if no item is found.
 */
export function getItemInSlot(player, slot) {
    const equippableComponent = player.getComponent("minecraft:equippable");
    if (!equippableComponent)
        return undefined;
    return equippableComponent.getEquipment(slot);
}
/**
 * Sets an item in the specified equipment slot for a player.
 *
 * @param player - The player entity.
 * @param slot - The equipment slot to set the item in.
 * @param itemStack - The item stack to set in the slot. If not provided, the slot will be cleared.
 */
export function setItemInSlot(player, slot, itemStack) {
    const equippableComponent = player.getComponent("minecraft:equippable");
    equippableComponent.setEquipment(slot, itemStack);
}
/**
 * Adds an item to the inventory of an entity.
 * If the entity's inventory is full, it will drop the excess items on the ground.
 * @param entity - The entity to add the item to.
 * @param itemStack - The item stack to add to the inventory.
 * @param options - Additional options for adding the item to the inventory.
 */
export function addItemToInventory(entity, itemStack, options) {
    const inventory = entity.getComponent("inventory");
    const container = inventory?.container;
    if (!container) {
        entity.dimension.spawnItem(itemStack, entity.location);
        return;
    }
    if (inventory.container.emptySlotsCount === 0) {
        let spaceRemaining = 0;
        for (let slotId = 0; slotId < container.size; slotId++) {
            const slot = container.getSlot(slotId);
            if (slot.getItem()?.typeId === itemStack.typeId) {
                spaceRemaining += Math.max(slot.maxAmount - slot.amount, 0);
                if (spaceRemaining >= itemStack.amount)
                    break;
            }
        }
        const overflowAmount = itemStack.amount - spaceRemaining;
        if (overflowAmount > 0) {
            const droppedStack = itemStack.clone();
            droppedStack.amount = overflowAmount;
            const spawnLocation = entity.location;
            if (options?.spawnOffset) {
                spawnLocation.x += options.spawnOffset.x;
                spawnLocation.y += options.spawnOffset.y;
                spawnLocation.z += options.spawnOffset.z;
            }
            const itemEntity = entity.dimension.spawnItem(droppedStack, spawnLocation);
            options?.spawnCallback?.(itemEntity);
        }
    }
    inventory.container.addItem(itemStack);
}
/**
 * Removes a specified number of items from the player's inventory.
 * @param entity The player or entity whose inventory is being modified.
 * @param itemId The ID of the item to remove (e.g., "minecraft:raw_iron").
 * @param count The number of items to remove (optional, default is 1).
 * @returns Whether the item removal was successful.
 */
export function removeItemFromInventory(entity, itemId, count = 1, ignoreInCreative) {
    const inventory = entity.getComponent("inventory");
    const container = inventory?.container;
    if (ignoreInCreative === true) {
        return false;
    }
    if (!container)
        return false;
    for (let slotId = 0; slotId < container.size; slotId++) {
        const slot = container.getSlot(slotId);
        const item = slot.getItem();
        if (item?.typeId === itemId) {
            // Check if we can remove the requested count from this slot
            if (item.amount > count) {
                item.amount -= count; // Decrease item amount by the count
                container.setItem(slotId, item);
                return true;
            }
            else {
                // If the count is greater than or equal to the item amount, remove all items in this slot
                count -= item.amount; // Reduce count by the amount in this slot
                container.setItem(slotId, undefined);
                if (count <= 0) {
                    return true; // If we've removed the required amount, return true
                }
            }
        }
    }
    return count <= 0; // Return true if the required amount was removed, otherwise false
}
/**
 * Decreases the durability of the item in the player's hand.
 *
 * @param player - The player whose item durability needs to be decreased.
 * @param options - Optional parameters for decreasing item durability.
 * @returns A boolean indicating whether the item durability was decreased.
 */
export function decreaseItemDurabilityInHand(player, options) {
    const ignoreInCreative = !(options?.ignoreInCreative === false);
    if (ignoreInCreative && player.getGameMode() === GameMode.creative)
        return true;
    const item = getItemInSlot(player, EquipmentSlot.Mainhand);
    const durability = item?.getComponent("minecraft:durability");
    if (item && durability) {
        let unbreakingLevel;
        const enchantments = item.getComponent("minecraft:enchantable");
        if (enchantments) {
            unbreakingLevel = enchantments.getEnchantment("minecraft:unbreaking")?.level ?? 0;
        }
        else {
            unbreakingLevel = 0;
        }
        const chance = 1 / (1 + unbreakingLevel);
        // Randomly decide if durability decreases
        if (Math.random() < chance) {
            if (durability.damage === durability.maxDurability) {
                setItemInSlot(player, EquipmentSlot.Mainhand, options?.convertInto ? new ItemStack(options.convertInto, item.amount) : undefined);
                world.playSound(options?.destructionSound || "random.break", player.location);
            }
            else {
                durability.damage++;
                setItemInSlot(player, EquipmentSlot.Mainhand, item);
            }
            return true;
        }
    }
    return false;
}
/**
 * Decrease the amount of the item in the player's mainhand
 * @param player The event data of the player
 * @param amount The amount to decrease
 * @param ignoreInCreative If true, the item amount will not be decreased
 *                         if the player is in creative mode
 * @returns True if the item amount was decreased, false otherwise.
 *          Always returns true if ignoreInCreative is true and the player is in creative mode.
 */
export function decreaseItemAmountInHand(player, amount = 1, ignoreInCreative = false) {
    if (ignoreInCreative && player.getGameMode() === GameMode.creative)
        return true;
    const item = getItemInSlot(player, EquipmentSlot.Mainhand);
    if (item) {
        if (item.amount > amount) {
            item.amount -= amount;
            setItemInSlot(player, EquipmentSlot.Mainhand, item);
        }
        else {
            setItemInSlot(player, EquipmentSlot.Mainhand, undefined);
        }
        return true;
    }
    return false;
}
/**
 * Emits a particle on the specified target entity at the given offset.
 * @param targetEntity - The target entity on which to emit the particle.
 * @param particleIdentifier - The identifier of the particle to emit.
 * @param offset - The offset at which to emit the particle relative to the target entity's location.
 */
export function emitParticleOnEntity(targetEntity, particleIdentifier, offset) {
    const location = {
        x: targetEntity.location.x + offset.x,
        y: targetEntity.location.y + offset.y,
        z: targetEntity.location.z + offset.z,
    };
    targetEntity.dimension.spawnParticle(particleIdentifier, location);
}
/**
 * Emits a sound on the specified entity at an offset location.
 * @param targetEntity - The target entity on which to emit the sound.
 * @param soundIdentifier - The identifier of the sound to emit.
 * @param offset - The offset location at which to emit the sound.
 */
export function emitSoundOnEntity(targetEntity, soundIdentifier, offset) {
    const location = {
        x: targetEntity.location.x + offset.x,
        y: targetEntity.location.y + offset.y,
        z: targetEntity.location.z + offset.z,
    };
    targetEntity.dimension.playSound(soundIdentifier, location);
}
/**
 * Filters an entity based on the provided options.
 * @param entity - The entity to filter.
 * @param options - The filtering options.
 * @returns A boolean indicating whether the entity passes the filter.
 */
export function filterEntities(entity, options) {
    const entityType = entity.id;
    const typeFamilyComponent = entity.getComponent("minecraft:type_family");
    if (!typeFamilyComponent) {
        return false;
    }
    const familyTypes = typeFamilyComponent.getTypeFamilies();
    if (options.include !== "all" && !options.include.includes(entityType)) {
        return false;
    }
    if (options.exclude && options.exclude.includes(entityType)) {
        return false;
    }
    if (options.isFamily && !options.isFamily.some((family) => familyTypes.includes(family))) {
        return false;
    }
    return true;
}
// Get the cardinal direction based on the player's view direction
export function getCardinalDirection(viewDirection) {
    const { x, z } = viewDirection;
    // Determine the cardinal direction (north, south, east, west)
    if (Math.abs(x) > Math.abs(z)) {
        return x > 0 ? "east" : "west";
    }
    else {
        return z > 0 ? "south" : "north";
    }
}
/**
 * Get a random number within the specified range
 * @param min The lowest possible value
 * @param max The highest possible value
 * @returns A random number between `min` and `max`
 */
export function getRandomRange(min, max) {
    if (min > max)
        throw new RangeError('min cannot be greater than max');
    return (max - min) * Math.random() + min;
}
export function getCenterOfBlock(location) {
    const centerLocation = {
        x: location.x + 0.5,
        y: location.y,
        z: location.z + 0.5
    };
    return centerLocation;
}
/**
 * Get the total number of a specific item type in the player's inventory.
 * @param player The player whose inventory is being checked.
 * @param itemTypeId The item type ID to search for (e.g., "minecraft:raw_iron").
 * @returns The total count of the specified item type in the player's inventory.
 */
export function getItemCountInInventory(player, itemTypeId) {
    let itemCount = 0;
    // Get the player's inventory
    const inventory = player.getComponent("minecraft:inventory");
    // Iterate through each slot in the player's inventory
    for (let i = 0; i < inventory.inventorySize; i++) {
        const item = inventory.container?.getItem(i);
        // If the item in the slot matches the itemTypeId, add its count to the total
        if (item && item.typeId === itemTypeId) {
            itemCount += item.amount; // Add the amount of the item to the total count
        }
    }
    return itemCount;
}
export function playSoundInDimension(dimension, location, radius, sound) {
    try {
        const players = dimension.getEntities({
            location: location,
            maxDistance: radius,
            type: "minecraft:player",
        });
        players.forEach((player) => {
            const playerLoc = player.location;
            const soundLoc = {
                x: playerLoc.x + (location.x - playerLoc.x) / (radius / 20),
                y: playerLoc.y + (location.y - playerLoc.y) / (radius / 20),
                z: playerLoc.z + (location.z - playerLoc.z) / (radius / 20),
            };
            const volume = CustomVector3.distance(playerLoc, location) * (radius / (radius * (radius / 1.3)));
            player.playSound(sound.id, {
                location: soundLoc,
                volume: (sound.volume ? sound.volume : 1) * (1 - volume),
                pitch: sound.pitch ? sound.pitch : 1,
            });
        });
    }
    catch { }
}
