var _a;
import * as mc from "@minecraft/server";
import * as utils from "../../utils/index";
import { HEART_DATA } from "../../shared/heart_data";
export const fullHealthObjective = 'FullHealth';
export class HealthManager {
    static listenToHealthChange(player, listen) {
        if (listen) {
            utils.removeFromArray(this.ignoreHealthEventPlayers, player.id);
        }
        else {
            this.ignoreHealthEventPlayers.push(player.id);
        }
    }
    static onHealed(player, newValue) {
        const health = player.Health;
        if (newValue > 20 && health) {
            if (health.defaultValue === health.currentValue) {
                player.setScore(fullHealthObjective, 1);
                return;
            }
            // Ensures that when in the custom heart range, the player heals in full heart increments
            if (newValue % 2 !== 0) {
                utils.waitForEvent(0, () => {
                    if (player.isValid()) {
                        health.setCurrentValue(Math.ceil(health.currentValue + 1));
                    }
                });
            }
        }
    }
    static onHurt(player, newValue, oldValue) {
        var _b;
        const health = player.Health;
        if (health) {
            // Ensures that when in the custom heart range, the player takes damage in full heart increments
            if (newValue % 2 !== 0) {
                utils.mclog(`${newValue} not even, running onHurt with ${newValue - 1}`);
                this.listenToHealthChange(player, false);
                this.onHurt(player, newValue - 1, oldValue);
                health.setCurrentValue(Math.ceil(newValue - 1));
                utils.waitForEvent(1, () => this.listenToHealthChange(player, true));
                return;
            }
            const validHearts = [];
            for (let index = 0; index < 10; index++) {
                const score = player.getScore(`Slot_${index}`);
                if (score > 0) {
                    validHearts.push(score);
                }
            }
            /**
             * We minus one from the end index because the valid hearts array is 0 indexed.
             * Start index is not minus one because we don't want to run the ability on our current, not lost heart.
             */
            const startIndex = utils.clamp(Math.floor((newValue - 20) / 2), 0, 10);
            const endIndex = Math.floor((oldValue - 20) / 2) - 1;
            const logData = { newValue, startIndex, oldValue, endIndex, validHearts: validHearts.map((v, i) => `${i}: ${HEART_DATA[v].displayName}`).join(", ") };
            utils.mclog(JSON.stringify(logData, null, 2));
            for (let index = startIndex; index <= endIndex; index++) {
                const heartIndex = validHearts[index];
                (_b = HEART_DATA[heartIndex]) === null || _b === void 0 ? void 0 : _b.performAbility(player);
            }
            player.setScore(fullHealthObjective, 0);
            if (player.hasTag("autoHeal") && newValue > 0) {
                utils.waitForEvent(1, () => {
                    health.resetToMaxValue();
                });
            }
        }
    }
    static getHeartData(player, health) {
        const data = {
            currentCustomHearts: 0,
            maxCustomHearts: 0,
            allHeartIndices: [],
            validHeartIndices: [],
        };
        data.currentCustomHearts = Math.ceil((health.currentValue - 20) / 2);
        for (let index = 0; index < 10; index++) {
            const score = player.getScore(`Slot_${index}`);
            data.allHeartIndices[index] = score;
            if (score > 0) {
                data.validHeartIndices.push(score);
                data.maxCustomHearts++;
            }
        }
        return data;
    }
    static displayHearts(player, data) {
        let text = '';
        const max = Math.min(data.validHeartIndices.length, data.currentCustomHearts);
        for (let index = 0; index < max; index++) {
            const score = data.validHeartIndices[index];
            const heartText = `${HEART_DATA[score].textureName}`;
            text += `H${zeroPad(index, 2)}:${heartText}`;
        }
        if (text === '') {
            text = 'null';
        }
        player.onScreenDisplay.setTitle(text);
    }
    static updateHealth(player, data, health) {
        const maxHearts = Math.ceil((health.effectiveMax - 20) / 2);
        // Raises the health change event if the player has less max health than number of custom hearts equipped
        if (data.maxCustomHearts !== maxHearts) {
            player.triggerEvent(`custom_hearts_${data.maxCustomHearts}`);
            const score = player.getScore(fullHealthObjective);
            // Prevents the player from autohealing to full if they weren't full health when the event was triggered
            if (score <= 0) {
                const cachedCurrentValue = health.currentValue;
                utils.mclog(`Restored To Cached Health: ${cachedCurrentValue}`);
                utils.waitForEvent(1, () => {
                    this.listenToHealthChange(player, false);
                    health.setCurrentValue(cachedCurrentValue);
                });
                utils.waitForEvent(2, () => {
                    this.listenToHealthChange(player, true);
                });
            }
        }
    }
}
_a = HealthManager;
HealthManager.ignoreHealthEventPlayers = [];
(() => {
    // Runs on a one second interval to detect changes to the heart sorter, the ui stays up-to-date with the health change event.
    utils.waitForLoopEvent(20, () => {
        utils.getAllPlayersSafe().forEach(player => {
            const health = player.Health;
            if (health) {
                const count = _a.getHeartData(player, health);
                _a.updateHealth(player, count, health);
                _a.displayHearts(player, count);
            }
        });
    });
    mc.world.afterEvents.entityHealthChanged.subscribe(event => {
        if (event.entity instanceof mc.Player && !_a.ignoreHealthEventPlayers.includes(event.entity.id)) {
            const player = new utils.SafetyCheckedPlayer(event.entity);
            if (event.newValue > event.oldValue) {
                _a.onHealed(player, Math.round(event.newValue));
            }
            else {
                _a.onHurt(player, Math.round(event.newValue), Math.round(event.oldValue));
            }
            _a.displayHearts(player, _a.getHeartData(player, player.Health));
        }
    });
})();
function zeroPad(num, places) {
    return String(num).padStart(places, "0");
}
