var _a;
import * as mc from "@minecraft/server";
import * as ui from "@minecraft/server-ui";
import { mcerror } from "./log";
import { addName } from "./namespace";
import { readDynamicProperty, writeDynamicProperty } from "./properties";
import { removeFromArray } from "./array";
/**
 * @remarks The result of a shown menu.
 */
export var MenuResult;
(function (MenuResult) {
    /**
     * @remarks The menu could not be opened as UI is unavailable.
     */
    MenuResult["UserBusy"] = "UserBusy";
    /**
     * @remarks The player closed the menu with the 'x' button or by pressing escape.
     */
    MenuResult["UserClosed"] = "UserClosed";
    /**
     * @remarks The menu could not be opened as the player is not available.
     */
    MenuResult["UserInvalid"] = "UserInvalid";
    /**
     * @remarks The player interacted with the menu and it closed via valid submission.
     */
    MenuResult["UserResolved"] = "UserResolved";
})(MenuResult || (MenuResult = {}));
/**
 * @remarks The base class for menus. Cannot be used directly.
 */
class Menu {
    /**
     * @remarks Creates a new Menu that can be shown to players.
     * @param title A {@link mc.RawMessage} or string. Provides the title of the menu.
     * @param elements An array of {@link MenuElement}s. Provides the look and behaviors of the menu.
     */
    constructor(title, elements) {
        this.queuedPlayers = new Map();
        this.onOpenedCallback = (player, ...params) => { };
        this.onClosedCallback = (player, ...params) => { };
        this.onCancelledCallback = (player, cancelationReason, ...params) => { };
        this.title = title;
        this.elements = elements;
    }
    /**
     * @remarks Handles what the menu should do when opens successfully. Generally this resolves immediately, but for queued menus it may take longer.
     * @param callback The callback to be executed when the menu is opened successfully.
     * @returns This menu.
     */
    onOpen(callback) {
        this.onOpenedCallback = callback;
        return this;
    }
    /**
     * @remarks Handles what the menu should do when closed successfully.
     * @param callback The callback to be executed when the menu is closed successfully.
     * @returns This menu.
     */
    onClose(callback) {
        this.onClosedCallback = callback;
        return this;
    }
    /**
     * @remarks Handles what the menu should do when cancelled.
     * @param callback The callback to be executed when the menu is cancelled (via the exit button or pressing escape).
     * @returns This menu.
     */
    onCancel(callback) {
        this.onCancelledCallback = callback;
        return this;
    }
    /**
     * @remarks Queues a menu to be shown to a player. If their UI hasn't loaded or they are currently in another menu
     * this method will queue the menu to be shown when possible. If the UI is available the menu will be shown immediately.
     * @param player The {@link SafetyCheckedPlayer} the menu should be shown to.
     * @param params An array of optional parameters that will be passed to the elements of the menu.
     * @returns A {@link MenuResult} describing the result of the displayed menu. Note that `UserBusy` will never be returned with this method.
     */
    queueMenuForPlayer(player, ...params) {
        this.queuedPlayers.set(player.id);
        return new Promise(async (resolve) => {
            while (player.isValid()) {
                const result = await this.showMenuToPlayer(player, ...params);
                if (result === MenuResult.UserBusy) {
                    continue;
                }
                this.queuedPlayers.delete(player.id);
                resolve(result);
                return;
            }
            this.queuedPlayers.delete(player.id);
            resolve(MenuResult.UserInvalid);
        });
    }
}
/**
 * @remarks A class for creating advanced {@link ui.ActionFormData} objects.
 */
export class ButtonMenu extends Menu {
    /**
     * @remarks Creates a new ButtonMenu that can be shown to players.
     * @param title A {@link mc.RawMessage} or string. Provides the title of the menu.
     * @param elements An array of {@link ButtonMenuElement}s. Provides the look and behaviors of the menu.
     * @param body An optional {@link mc.RawMessage} or string. Provides the body text of the menu above the buttons.
     * @example
     * https://github.com/Dotzip-Developments/craftee-items-dx/blob/main/scripts/weather_clock.ts
     * ```typescript
     * new utils.ButtonMenu({translate: 'action.menu.time.title'}, [
     *     {
     *         text: {translate: 'action.menu.time.stop'},
     *         texture: 'textures/ui/weather_clock/time.pause',
     *         shouldHide() {
     *             return GlobalTime.Paused;
     *         },
     *         onClick(player) {
     *             printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'has paused'}]});
     *             GlobalTime.Paused = true;
     *         },
     *     },
     *     {
     *         text: {translate: 'action.menu.time.resume'},
     *         texture: 'textures/ui/weather_clock/time.resume',
     *         shouldHide() {
     *             return !GlobalTime.Paused;
     *         },
     *         onClick(player) {
     *             printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'has resumed'}]});
     *             GlobalTime.Paused = false;
     *         },
     *     },
     *     {
     *         text: {translate: 'action.menu.time.start'},
     *         texture: 'textures/ui/weather_clock/time.start',
     *         onClick(player) {
     *             printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'reset'}]});
     *             GlobalTime.Speed = GlobalTime.defaultSpeed;
     *             GlobalTime.Paused = false;
     *         },
     *     },
     *     {
     *         text: {translate: 'action.menu.time.reverse'},
     *         texture: 'textures/ui/weather_clock/time.reverse',
     *         shouldHide() {
     *             return GlobalTime.Paused;
     *         },
     *         onClick(player) {
     *             printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'reversed'}]});
     *             GlobalTime.Speed *= -1;
     *         },
     *     },
     *     {
     *         text: {translate: 'action.menu.time.fast'},
     *         texture: 'textures/ui/weather_clock/time.fast',
     *         shouldHide() {
     *             return GlobalTime.Paused;
     *         },
     *         onClick(player) {
     *             if (GlobalTime.Speed === 0) {
     *                 printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'sped up'}]});
     *                 GlobalTime.Speed = GlobalTime.defaultSpeed;
     *                 return;
     *             }
     *             if (Math.abs(GlobalTime.Speed * 2) <= GlobalTime.speedMax) {
     *                 printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'sped up'}]});
     *                 GlobalTime.Speed *= 2;
     *             } else {
     *                 player.onScreenDisplay.setActionBar({translate: 'action.menu.time.error_max'});
     *             }
     *         },
     *     },
     *     {
     *         text: {translate: 'action.menu.time.slow'},
     *         texture: 'textures/ui/weather_clock/time.slow',
     *         shouldHide() {
     *             return GlobalTime.Paused;
     *         },
     *         onClick(player) {
     *             if (GlobalTime.Speed !== 0) {
     *                 printMessageToPlayers(`action.menu.time.message`, {rawtext: [{text: player.nameTag}, {text: 'slowed down'}]});
     *                 GlobalTime.Speed /= 2;
     *             } else {
     *                 player.onScreenDisplay.setActionBar({translate: 'action.menu.time.error_min'});
     *             }
     *         },
     *     }
     * ]);
     * ```
     */
    constructor(title, elements, body) {
        super(title, elements);
        this.body = body;
    }
    /**
     * @inheritdoc
     * @example
     * https://github.com/Dotzip-Developments/craftee-items-dx/blob/main/scripts/weather_clock.ts
     * ```typescript
     * function interactWithWeatherClock(event: mc.ScriptEventCommandMessageAfterEvent) {
     *     if (event.id === 'ldz:interact_with_weather_clock' && event.sourceEntity) {
     *         const player = new utils.SafetyCheckedPlayer(event.sourceEntity as mc.Player);
     *         utils.getEntitiesFromViewDirectionSafe(player, {maxDistance: 5}).forEach(hit => {
     *             if (hit.entity.typeId === 'ldz:weather_clock') {
     *                 ROOT_MENU.showMenuToPlayer(player, new utils.SafetyCheckedEntity(hit.entity));
     *             }
     *         });
     *     }
     * }
     * ```
     */
    showMenuToPlayer(player, ...params) {
        return new Promise((resolve) => {
            if (!player.isValid()) {
                resolve(MenuResult.UserInvalid);
                return;
            }
            ;
            const form = new ui.ActionFormData();
            form.title(this.title instanceof Function ? this.title(player, ...params) : this.title);
            if (this.body) {
                const body = this.body instanceof Function ? this.body(player, ...params) : this.body;
                form.body(body);
            }
            const filteredElements = this.elements.filter(element => !(element.shouldHide && element.shouldHide(player, ...params)));
            filteredElements.forEach(element => {
                const text = element.text instanceof Function ? element.text(player, ...params) : element.text;
                const texture = element.texture instanceof Function ? element.texture(player, ...params) : element.texture;
                form.button(text, texture);
            });
            const handler = mc.system.runTimeout(() => this.onOpenedCallback(player, ...params), 5);
            form.show(player.UnsafeEntity).then(result => {
                if (result.selection !== undefined && player.isValid()) {
                    filteredElements[result.selection].onClick(player, ...params);
                    this.onClosedCallback(player, ...params);
                    resolve(MenuResult.UserResolved);
                }
                else {
                    const reason = MenuResult[result.cancelationReason];
                    // Do not call cancelled callback if the player is busy and the menu was queued.
                    if ((this.queuedPlayers.has(player.id) && reason !== MenuResult.UserBusy) || !this.queuedPlayers.has(player.id)) {
                        this.onCancelledCallback(player, result.cancelationReason, ...params);
                    }
                    mc.system.clearRun(handler);
                    resolve(reason);
                }
            });
        });
    }
}
/**
 * @remarks A class for creating advanced {@link ui.ModalFormData} objects.
 */
export class ModalMenu extends Menu {
    constructor(title, elements, submit) {
        super(title, elements);
        this.submit = submit;
    }
    showMenuToPlayer(player, ...params) {
        return new Promise((resolve) => {
            if (!player.isValid()) {
                resolve(MenuResult.UserInvalid);
                return;
            }
            ;
            const form = new ui.ModalFormData();
            form.title(this.title instanceof Function ? this.title(player, ...params) : this.title);
            const filteredElements = this.elements.filter(element => !(element.shouldHide && element.shouldHide(player, ...params)));
            filteredElements.forEach(element => {
                switch (element.type) {
                    case 'dropdown':
                        {
                            const filteredDropdownOptions = element.options.filter(option => !(option.shouldHide && option.shouldHide(player, ...params)));
                            const options = filteredDropdownOptions.map(option => option.text instanceof Function ? option.text(player, ...params) : option.text);
                            const label = element.text instanceof Function ? element.text(player, ...params) : element.text;
                            const defaultValueIndex = element.defaultValueIndex instanceof Function ? element.defaultValueIndex(player, ...params) : element.defaultValueIndex;
                            form.dropdown(label, options, defaultValueIndex);
                        }
                        break;
                    case 'slider':
                        {
                            const label = element.text instanceof Function ? element.text(player, ...params) : element.text;
                            const minimumValue = element.minimumValue instanceof Function ? element.minimumValue(player, ...params) : element.minimumValue;
                            const maximumValue = element.maximumValue instanceof Function ? element.maximumValue(player, ...params) : element.maximumValue;
                            const valueStep = element.valueStep instanceof Function ? element.valueStep(player, ...params) : element.valueStep;
                            const defaultValue = element.defaultValue instanceof Function ? element.defaultValue(player, ...params) : element.defaultValue;
                            form.slider(label, minimumValue, maximumValue, valueStep, defaultValue);
                        }
                        break;
                    case 'textField':
                        {
                            const label = element.text instanceof Function ? element.text(player, ...params) : element.text;
                            const placeholderText = element.placeholderText instanceof Function ? element.placeholderText(player, ...params) : element.placeholderText;
                            const defaultValue = element.defaultValue instanceof Function ? element.defaultValue(player, ...params) : element.defaultValue;
                            form.textField(label, placeholderText, defaultValue);
                        }
                        break;
                    case "toggle":
                        {
                            const label = element.text instanceof Function ? element.text(player, ...params) : element.text;
                            const defaultValue = element.defaultValue instanceof Function ? element.defaultValue(player, ...params) : element.defaultValue;
                            form.toggle(label, defaultValue);
                        }
                        break;
                    default:
                        mcerror(`Unexpected type${new Error().stack}`);
                        break;
                }
            });
            if (this.submit) {
                form.submitButton(this.submit instanceof Function ? this.submit(player, ...params) : this.submit);
            }
            const handler = mc.system.runTimeout(() => this.onOpenedCallback(player, ...params), 5);
            form.show(player.UnsafeEntity).then(result => {
                if (result.formValues) {
                    result.formValues.forEach((value, index) => {
                        const submittedElement = filteredElements[index];
                        switch (submittedElement.type) {
                            case 'dropdown':
                                const filteredDropdownOptions = submittedElement.options.filter(option => !(option.shouldHide && option.shouldHide(player, ...params)));
                                filteredDropdownOptions[Number(value)].onSelected(player, ...params);
                                break;
                            case 'slider':
                                submittedElement.onSubmit(player, Number(value), ...params);
                                break;
                            case 'textField':
                                submittedElement.onSubmit(player, String(value), ...params);
                                break;
                            case "toggle":
                                submittedElement.onSubmit(player, Boolean(value), ...params);
                                break;
                            default:
                                mcerror(`Unexpected type${new Error().stack}`);
                                break;
                        }
                    });
                    resolve(MenuResult.UserResolved);
                    this.onClosedCallback(player, ...params);
                }
                else {
                    const reason = MenuResult[result.cancelationReason];
                    // Do not call cancelled callback if the player is busy and the menu was queued.
                    if ((this.queuedPlayers.has(player.id) && reason !== MenuResult.UserBusy) || !this.queuedPlayers.has(player.id)) {
                        this.onCancelledCallback(player, result.cancelationReason, ...params);
                    }
                    mc.system.clearRun(handler);
                    resolve(reason);
                }
            });
        });
    }
}
/**
 * @remarks A class for creating advanced {@link ui.MessageFormData} objects.
 */
export class MessageMenu extends Menu {
    /**
     * @remarks Creates a new MessageMenu that can be shown to players.
     * @param title A {@link mc.RawMessage} or string. Provides the title of the menu.
     * @param elements An object containing a confirm and deny {@link MessageButtonMenuElement}. Note that the {@link MenuElement.shouldHide} property does nothing in this menu, there will always be two buttons.
     * @param body An optional {@link mc.RawMessage} or string. Provides the body text of the message above the buttons.
     */
    constructor(title, elements, body) {
        super(title, []);
        this.confirmButton = elements.confirm;
        this.denyButton = elements.deny;
        this.body = body;
    }
    showMenuToPlayer(player, ...params) {
        return new Promise((resolve) => {
            if (!player.isValid()) {
                resolve(MenuResult.UserInvalid);
                return;
            }
            ;
            const form = new ui.MessageFormData();
            form.title(this.title instanceof Function ? this.title(player, ...params) : this.title);
            if (this.body) {
                const body = this.body instanceof Function ? this.body(player, ...params) : this.body;
                form.body(body);
            }
            const confirmText = this.confirmButton.text instanceof Function ? this.confirmButton.text(player, ...params) : this.confirmButton.text;
            form.button1(confirmText);
            const denyText = this.denyButton.text instanceof Function ? this.denyButton.text(player, ...params) : this.denyButton.text;
            form.button2(denyText);
            const handler = mc.system.runTimeout(() => this.onOpenedCallback(player, ...params), 1);
            form.show(player.UnsafeEntity).then(result => {
                if (result.selection === 0 && player.isValid()) {
                    this.confirmButton.onClick(player, ...params);
                    resolve(MenuResult.UserResolved);
                    this.onClosedCallback(player, ...params);
                    return;
                }
                else if (result.selection === 1 && player.isValid()) {
                    this.denyButton.onClick(player, ...params);
                    resolve(MenuResult.UserResolved);
                    this.onClosedCallback(player, ...params);
                    return;
                }
                const reason = MenuResult[result.cancelationReason];
                // Do not call cancelled callback if the player is busy and the menu was queued.
                if ((this.queuedPlayers.has(player.id) && reason !== MenuResult.UserBusy) || !this.queuedPlayers.has(player.id)) {
                    this.onCancelledCallback(player, result.cancelationReason, ...params);
                }
                mc.system.clearRun(handler);
                resolve(reason);
            });
        });
    }
}
function resolveStringBuilder(builder, player, ...params) {
    return builder instanceof Function ? builder(player, ...params) : builder;
}
function resolveTextureBuilder(builder, player, ...params) {
    return builder instanceof Function ? builder(player, ...params) : builder;
}
/**
 * @remarks A class for building advanced menus with support for submenu navigation, automatic layout creation, and bookmarking.
 * Note that automatically generated back buttons use the lang key `<namespace>:menu.button.back`.
 */
export class MenuBuilder {
    /**
     * @remarks Creates a new menu builder that can be shown to players.
     * @param homePage The home page, the root of the menu.
     * @param options A {@link MenuBuilderOptions} providing additional options for the menu.
     */
    constructor(homePage, options) {
        this.playerPromises = {};
        this.layout = { root: homePage };
        this.options = options;
    }
    /**
     * @remarks Shows the menu to a given player.
     * @param player The {@link SafetyCheckedPlayer} this menu should be shown to.
     * @param path An optional path to navigate to a specific submenu.
     */
    async showMenuToPlayer(player, path) {
        const promise = new Promise(resolve => {
            var _b, _c, _d, _e, _f, _g;
            this.playerPromises[player.id] = resolve;
            if (!path) {
                if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.bookmarkPropertyKey) {
                    path = (_c = readDynamicProperty(player, this.options.bookmarkPropertyKey)) !== null && _c !== void 0 ? _c : "root/";
                }
                else {
                    path = "root/";
                }
            }
            if ((_d = this.options) === null || _d === void 0 ? void 0 : _d.closeOnHurt) {
                MenuBuilder.hurtListenIds.push(player.id);
            }
            const page = this.getPageFromString(path);
            const menu = this.getMenuFromPage(page.page, page.path, player);
            if (!menu) {
                resolve(MenuResult.UserResolved);
                return;
            }
            (_g = ((_e = page.page.onPageOpen) !== null && _e !== void 0 ? _e : (_f = this.options) === null || _f === void 0 ? void 0 : _f.defaultOnPageOpen)) === null || _g === void 0 ? void 0 : _g(player);
            menu.showMenuToPlayer(player);
        });
        promise.then(() => {
            removeFromArray(MenuBuilder.hurtListenIds, player.id);
        });
        return promise;
    }
    /**
     * @remarks Queues a menu to be shown to a player. If their UI hasn't loaded or they are currently in another menu this method will queue the menu to be shown when possible. If the UI is available the menu will be shown immediately.
     * @param player The {@link SafetyCheckedPlayer} this menu should be shown to.
     * @param path An optional path to navigate to a specific submenu.
     */
    async queueMenuForPlayer(player, path) {
        const promise = new Promise(resolve => {
            var _b, _c, _d;
            this.playerPromises[player.id] = resolve;
            if (!path) {
                if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.bookmarkPropertyKey) {
                    path = (_c = readDynamicProperty(player, this.options.bookmarkPropertyKey)) !== null && _c !== void 0 ? _c : "root/";
                }
                else {
                    path = "root/";
                }
            }
            if ((_d = this.options) === null || _d === void 0 ? void 0 : _d.closeOnHurt) {
                MenuBuilder.hurtListenIds.push(player.id);
            }
            const page = this.getPageFromString(path);
            const menu = this.getMenuFromPage(page.page, page.path, player);
            if (!menu) {
                resolve(MenuResult.UserResolved);
                return;
            }
            menu.onCancel((player, reason) => this.playerPromises[player.id](MenuResult[reason]));
            menu.onOpen(() => {
                var _b, _c, _d;
                (_d = ((_b = page.page.onPageOpen) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.defaultOnPageOpen)) === null || _d === void 0 ? void 0 : _d(player);
            });
            menu.queueMenuForPlayer(player);
        });
        promise.then(() => {
            removeFromArray(MenuBuilder.hurtListenIds, player.id);
        });
        return promise;
    }
    /**
     * @remarks Clones a page from this menu builder, allowing you to use an identical page in multiple menus.
     * @param path The path to the desired page.
     * @returns The page at the given path.
     */
    clonePage(path) {
        const { page } = this.getPageFromString(path);
        return page;
    }
    getPageFromString(path) {
        const keys = path.split("/").filter(key => key !== "");
        let currentLayout = this.layout;
        let currentPage = this.layout.root;
        let currentPath = "";
        for (const key of keys) {
            if (currentLayout && typeof currentLayout === "object" && key in currentLayout) {
                currentPage = currentLayout[key];
                currentPath += key + "/";
                if (currentPage.type === "page_list") {
                    currentLayout = currentPage.contents;
                }
            }
        }
        return { page: currentPage, path: currentPath };
    }
    getMenuFromPage(page, pagePath, player) {
        switch (page.type) {
            case "page_list":
                return this.getLayoutMenu(page, pagePath);
            case "buttons":
                return this.getButtonMenu(page, pagePath);
            case "modal":
                return this.getModalMenu(page, pagePath);
            case "message":
                return this.getMessageMenu(page, pagePath);
            case "event":
                this.playerPromises[player.id](MenuResult.UserResolved);
                page.contents(player);
                return;
        }
    }
    getLayoutMenu(page, pagePath) {
        var _b, _c, _d, _e, _f;
        const buttons = [];
        for (const entry of Object.entries(page.contents)) {
            if (entry[1].hidden)
                continue;
            buttons.push({
                text: (_b = entry[1].buttonLabel) !== null && _b !== void 0 ? _b : entry[1].title,
                texture: entry[1].icon,
                onClick: (player) => {
                    var _b, _c, _d;
                    const newPage = this.getPageFromString(pagePath + entry[0]);
                    const menu = this.getMenuFromPage(newPage.page, newPage.path, player);
                    if (!menu)
                        return;
                    this.updateStoredPath(newPage, player);
                    menu.showMenuToPlayer(player);
                    ((_d = (_b = newPage.page.onPageOpen) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.defaultOnPageOpen) !== null && _d !== void 0 ? _d : function () { })(player);
                }
            });
        }
        const path = pagePath.split("/").filter(key => key !== "");
        if (path.length > 1) {
            path.pop();
            const backButton = {
                text: addName("menu.button.back"),
                texture: (_c = page.backIcon) !== null && _c !== void 0 ? _c : (_d = this.options) === null || _d === void 0 ? void 0 : _d.defaultBackIcon,
                onClick: (player) => {
                    var _b, _c, _d;
                    const newPage = this.getPageFromString(path.join("/"));
                    const menu = this.getMenuFromPage(newPage.page, newPage.path, player);
                    if (!menu)
                        return;
                    this.updateStoredPath(newPage, player);
                    menu.showMenuToPlayer(player);
                    ((_d = (_b = newPage.page.onPageOpen) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.defaultOnPageOpen) !== null && _d !== void 0 ? _d : function () { })(player);
                },
            };
            const backPos = (_e = page.backButtonPosition) !== null && _e !== void 0 ? _e : (_f = this.options) === null || _f === void 0 ? void 0 : _f.defaultBackPosition;
            switch (backPos) {
                case "none": break;
                case "top":
                    buttons.unshift(backButton);
                    break;
                case "bottom":
                    buttons.push(backButton);
                    break;
                case "both":
                    buttons.unshift(backButton);
                default:
                    buttons.push(backButton);
                    break;
            }
        }
        if (!buttons.length) {
            buttons.push({
                text: "Missing Buttons",
                onClick() { },
            });
        }
        const menu = new ButtonMenu(page.title, buttons, page.body);
        menu.onCancel((player, reason) => this.playerPromises[player.id](MenuResult[reason]));
        return menu;
    }
    getButtonMenu(page, pagePath) {
        var _b, _c, _d, _e;
        const buttons = [...page.contents];
        // Update buttons to resolve when closed
        for (const button of buttons) {
            const onClick = button.onClick;
            button.onClick = (player) => {
                onClick(player);
                this.playerPromises[player.id](MenuResult.UserResolved);
            };
        }
        const path = pagePath.split("/").filter(key => key !== "");
        if (path.length > 1) {
            path.pop();
            const backButton = {
                text: addName("menu.button.back"),
                texture: (_b = page.backIcon) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.defaultBackIcon,
                onClick: (player) => {
                    var _b, _c, _d;
                    const newPage = this.getPageFromString(path.join("/"));
                    const menu = this.getMenuFromPage(newPage.page, newPage.path, player);
                    if (!menu)
                        return;
                    this.updateStoredPath(newPage, player);
                    menu.showMenuToPlayer(player);
                    ((_d = (_b = newPage.page.onPageOpen) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.defaultOnPageOpen) !== null && _d !== void 0 ? _d : function () { })(player);
                },
            };
            const backPos = (_d = page.backButtonPosition) !== null && _d !== void 0 ? _d : (_e = this.options) === null || _e === void 0 ? void 0 : _e.defaultBackPosition;
            switch (backPos) {
                case "none": break;
                case "top":
                    buttons.unshift(backButton);
                    break;
                case "bottom":
                    buttons.push(backButton);
                    break;
                case "both":
                    buttons.unshift(backButton);
                default:
                    buttons.push(backButton);
                    break;
            }
        }
        if (!buttons.length) {
            buttons.push({
                text: "Missing Buttons",
                onClick() { },
            });
        }
        const menu = new ButtonMenu(page.title, buttons, page.body);
        menu.onCancel((player, reason) => this.playerPromises[player.id](MenuResult[reason]));
        return menu;
    }
    getModalMenu(page, pagePath) {
        var _b, _c;
        const menu = new ModalMenu(page.title, page.contents, (_b = page.submitText) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.defaultSubmitText);
        menu.onCancel(player => {
            var _b;
            (_b = page.onCanceled) === null || _b === void 0 ? void 0 : _b.call(page, player);
            this.playerPromises[player.id](MenuResult.UserClosed);
        })
            .onClose(player => {
            var _b;
            (_b = page.onSubmit) === null || _b === void 0 ? void 0 : _b.call(page, player);
            this.playerPromises[player.id](MenuResult.UserResolved);
        });
        return menu;
    }
    getMessageMenu(page, pagePath) {
        const menu = new MessageMenu(page.title, page.contents, page.body);
        menu.onCancel(player => this.playerPromises[player.id](MenuResult.UserClosed))
            .onClose(player => this.playerPromises[player.id](MenuResult.UserResolved));
        return menu;
    }
    updateStoredPath(page, player) {
        var _b;
        if (((_b = this.options) === null || _b === void 0 ? void 0 : _b.bookmarkPropertyKey) && !page.page.ignoreBookmark && page.page.type !== "modal" && page.page.type !== "message") {
            writeDynamicProperty(player, this.options.bookmarkPropertyKey, page.path);
        }
    }
}
_a = MenuBuilder;
MenuBuilder.hurtListenIds = [];
(() => {
    mc.world.afterEvents.entityHurt.subscribe(event => {
        if (event.hurtEntity instanceof mc.Player && _a.hurtListenIds.includes(event.hurtEntity.id)) {
            ui.uiManager.closeAllForms(event.hurtEntity);
        }
    });
})();
