import * as mc from "@minecraft/server";
import * as utils from "../../utils/index";
export class PopupMessage {
    constructor(messages) {
        this.aborted = false;
        this.messageList = messages;
        this.totalDuration = 0;
        this.messageList.forEach(message => (this.totalDuration += message.duration * 20));
    }
    async displayMessage(player) {
        this.aborted = false;
        for (const message of this.messageList) {
            if (this.aborted) {
                return;
            }
            if (message.sound)
                player.playSound(message.sound);
            player.onScreenDisplay.setActionBar(`{dialogue:${message.message},dialogue_stay:${Math.ceil(message.duration)}}`);
            await utils.waitForTicks(message.duration * 20 - 10);
            if (this.aborted) {
                player.onScreenDisplay.setActionBar(``);
                return;
            }
            player.onScreenDisplay.setActionBar(`{dialogue_stop:${message.message}}`);
            await utils.waitForTicks(10);
            if (message.onComplete) {
                await message.onComplete(player);
            }
        }
    }
    abort() {
        this.aborted = true;
    }
}
export class PopupSystem {
    constructor(name, messages) {
        this.dialogueTagPrefix = 'Dialogue';
        this.players = [];
        this.dialogueSuspended = false;
        this.playing = false;
        this.popupMessages = messages;
        this.name = name;
        this.dialoguePendingObjective = this.name + 'DialoguePending';
        if (this.getDialoguePending()) {
            this.startPopup();
        }
    }
    async startPopup() {
        this.playing = true;
        await utils.waitForLoopEvent(20, () => !utils.getScoreAsBool('.var', 'YouTube'));
        for (const popupMessage of this.popupMessages) {
            this.players = utils.getPlayersSafe({ tags: [`${this.dialogueTagPrefix}${popupMessage.tag}`] });
            for (const player of this.players) {
                this.stopsounds();
                this.currentMessage = popupMessage;
                await popupMessage.message.displayMessage(player);
                if (this.dialogueSuspended) {
                    popupMessage.message.abort();
                    this.playing = false;
                    return;
                }
                this.removeDialogueTag(player, popupMessage.tag);
            }
        }
        this.setDialoguePending(false);
        this.playing = false;
    }
    stopsounds() {
        this.players.forEach(player => {
            for (const popupMessage of this.popupMessages) {
                popupMessage.message.messageList.forEach(message => player.runCommand(`stopsound @s ${message.sound}`));
            }
        });
    }
    addDialogueTag(player, tag) {
        player.addTag(this.dialogueTagPrefix + tag);
        this.setDialoguePending(true);
    }
    removeDialogueTag(player, tag) {
        player.removeTag(this.dialogueTagPrefix + tag);
    }
    setDialoguePending(state) {
        utils.overworld.runCommand(`scoreboard objectives add ${this.dialoguePendingObjective} dummy`);
        utils.overworld.runCommand(`scoreboard players set .var ${this.dialoguePendingObjective} ${state ? 1 : 0}`);
        if (state && !this.dialogueSuspended && !this.playing) {
            this.startPopup();
        }
    }
    getDialoguePending() {
        return utils.getScoreAsBool('.var', this.dialoguePendingObjective);
    }
    suspend() {
        var _a;
        this.dialogueSuspended = this.getDialoguePending();
        this.stopsounds();
        (_a = this.currentMessage) === null || _a === void 0 ? void 0 : _a.message.abort();
    }
    resume() {
        this.dialogueSuspended = false;
        if (this.getDialoguePending() && !this.playing) {
            this.startPopup();
        }
    }
}
export class Behavior {
    constructor(data) {
        var _a;
        this.startTime = 0;
        this.finished = false;
        this.localRandom = 0;
        this.aborted = false;
        this.ID = data.ID;
        this.onEntry = data.onEntry;
        this.running = data.running;
        this.onExit = data.onExit;
        this.transitions = (_a = data.transitions) !== null && _a !== void 0 ? _a : [];
    }
    async entry(entity) {
        utils.mclog(`${entity.nameTag} (${entity.typeId}) Starting Behavior: ${this.ID}`, 'behaviors');
        this.startTime = mc.system.currentTick;
        this.finished = false;
        this.localRandom = Math.random();
        this.aborted = false;
        if (this.onEntry) {
            await this.onEntry(entity);
        }
    }
    async run(entity) {
        utils.mclog(`${entity.nameTag} (${entity.typeId}) Running Behavior: ${this.ID}`, 'behaviors');
        if (this.running) {
            await this.running(entity, this.aborted);
        }
        this.finished = true;
    }
    async evaluateTransitions(entity) {
        for (const transition of this.transitions) {
            if (transition.condition({ entity, age: mc.system.currentTick - this.startTime, finished: this.finished, random: this.getLocalRandom(transition.randomMin, transition.randomMax) })) {
                return transition.name;
            }
        }
        await utils.waitForTicks(0);
        return await this.evaluateTransitions(entity);
    }
    async exit(entity) {
        utils.mclog(`${entity.nameTag} (${entity.typeId}) Ending Behavior: ${this.ID}`, 'behaviors');
        this.aborted = true;
        if (this.onExit) {
            await this.onExit(entity);
        }
    }
    getLocalRandom(min, max) {
        min = min !== null && min !== void 0 ? min : 0;
        max = max !== null && max !== void 0 ? max : 1;
        return Math.floor(this.localRandom * (max - min + 1)) + min;
    }
}
export class NPC {
    constructor(data) {
        this.behaviorList = data.behaviorList;
        this.entity = data.npcEntity;
        this.startBehavior(data.defaultBehavior);
    }
    async startBehavior(newBehavior) {
        if (this.currentBehavior && this.entity.isValid()) {
            await this.currentBehavior.exit(this.entity);
        }
        if (this.entity.isValid()) {
            this.currentBehavior = newBehavior;
            await this.currentBehavior.entry(this.entity);
        }
        if (this.currentBehavior && this.entity.isValid()) {
            this.currentBehavior.run(this.entity);
            const transitionName = await this.currentBehavior.evaluateTransitions(this.entity);
            const transitionBehavior = this.behaviorList.find(behavior => behavior.ID === transitionName);
            if (transitionBehavior && this.entity.isValid()) {
                this.startBehavior(transitionBehavior);
            }
            else if (this.entity.isValid()) {
                utils.mcerror(`${this.entity.nameTag} (${this.entity.typeId}) Does Not Have Behavior: ${transitionName}\nRestarting Behavior: ${this.currentBehavior.ID}`, 'behaviors');
                this.startBehavior(this.currentBehavior);
            }
        }
        if (!this.entity.isValid()) {
            utils.mcwarn(`NPC Entity is no longer valid.`, 'behaviors');
            await utils.waitForLoopEvent(20, () => {
                return this.entity.isValid();
            });
            utils.mclog(`NPC is now valid. Starting Behavior: ${newBehavior.ID}`, 'behaviors');
            this.startBehavior(newBehavior);
        }
    }
}
