import * as mc from '@minecraft/server';
import * as utils from './utils/index';
export function nearestEntityToLocation(location, entities) {
    const utilsLocation = new utils.Vector3(location);
    let nearestDistance = -1;
    let nearestIndex = 0;
    for (let x = 0; x < entities.length; x++) {
        const distance = utilsLocation.distance(entities[x].location);
        if (distance < nearestDistance || nearestDistance === -1) {
            nearestDistance = distance;
            nearestIndex = x;
        }
    }
    return entities[nearestIndex];
}
export function entityToPlayer(entity) {
    const allPlayers = mc.world.getAllPlayers();
    for (const player of allPlayers) {
        if (entity.id === player.id) {
            return new utils.SafetyCheckedPlayer(player);
        }
    }
    return undefined;
}
const PREVENT_FALL_DAMAGE_LOG_TAG = "logPreventFallDamage";
/**
 * Prevents fall damage for an entity by giving them the `slow_falling` effect just before they hit the ground.
 * @param {utils.SafetyCheckedEntity} entity - The entity to prevent fall damage for.
 * @param {object} [options] - Optional configuration options.
 * @param {boolean} [options.continuous=false] - A boolean indicating whether fall damage should continue to be prevented
 * after the entity has safely landed on the ground or in water.
 * @param {number} [options.startDelayTicks=0] - The number of ticks to delay the starting the fall damage prevention loop.
 * Useful for non-continuous mode when the player is on the ground and expected to leave it soon.
 * @returns {{ stopWhenSafe: () => void, stopImmediately: () => void }} An object containing two methods: `stopWhenSafe` and `stopImmediately`.
 * `stopWhenSafe` can be called to halt fall damage prevention when the entity becomes safe from falling, and
 * `stopImmediately` can be called to immediately & completely halt the fall damage prevention.
 */
export function preventFallDamage(entity, options) {
    const entityName = entity.nameTag !== "" ? entity.nameTag : entity.typeId;
    utils.mclog(`Prevent ${entityName}'s fall damage`, PREVENT_FALL_DAMAGE_LOG_TAG);
    const { continuous = false, startDelayTicks = 0 } = options || {};
    let loopAfterSafe = true;
    let delayedStartHandle;
    let logicLoopHandle;
    // Delay the start of the fall damage prevention logic by startDelayTicks if greater than 0
    if (startDelayTicks > 0) {
        delayedStartHandle = mc.system.runTimeout(() => startLoop(), startDelayTicks);
    }
    else {
        startLoop();
    }
    function startLoop() {
        utils.mclog(`Start preventing fall damage for ${entityName}`, PREVENT_FALL_DAMAGE_LOG_TAG);
        logicLoopHandle = mc.system.runInterval(loopLogic);
    }
    function loopLogic() {
        // Stop if entity is invalid
        if (!entity.isValid()) {
            stopImmediately();
            return;
        }
        // If the entity is safe then check whether the loop should stop
        if (entity.isOnGround || entity.isInWater) {
            // utils.mclog(`${entityName} is safe from falling`, PREVENT_FALL_DAMAGE_LOG_TAG);
            // Stop the loop if continuous=false (aka one-shot) or it shouldn't loop again
            if (!continuous || !loopAfterSafe) {
                stopImmediately();
                return;
            }
        }
        else {
            // Give slow-falling if the entity will soon hit the ground
            const yVelocity = entity.getVelocity().y;
            if (yVelocity < 0) {
                const rayDistance = Math.abs(yVelocity) * 10;
                const block = entity.dimension.getBlockFromRay(entity.location, utils.Vector3.DOWN, { maxDistance: rayDistance });
                if (block) {
                    // utils.mclog("Preventing fall damage", PREVENT_FALL_DAMAGE_LOG_TAG);
                    entity.addEffect("slow_falling", 3, { showParticles: false });
                }
            }
        }
    }
    /**
     * Stops preventing fall damage for the entity once it lands on ground or in water.
     */
    function stopWhenSafe() {
        utils.mclog(`Will stop preventing fall damage for ${entityName} when safe`, PREVENT_FALL_DAMAGE_LOG_TAG);
        loopAfterSafe = false;
    }
    /**
     * Stops preventing fall damage immediately.
     *
     * Will not start preventing fall damage after startDelayTicks, if it was set.
     */
    function stopImmediately() {
        utils.mclog(`Stopped preventing fall damage for ${entityName}`, PREVENT_FALL_DAMAGE_LOG_TAG);
        if (delayedStartHandle)
            mc.system.clearRun(delayedStartHandle);
        if (logicLoopHandle)
            mc.system.clearRun(logicLoopHandle);
    }
    return { stopWhenSafe, stopImmediately };
}
mc.system.run(() => {
    mc.system.afterEvents.scriptEventReceive.subscribe(event => {
        if (event.id === "ldz:fall") {
            preventFallDamage(new utils.SafetyCheckedEntity(event.sourceEntity), { continuous: true });
        }
    });
});
