import * as ui from "@minecraft/server-ui";
import { mcerror } from "./log";
/**
 * @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.onClosedCallback = (player, ...params) => { };
        this.onCancelledCallback = (player, ...params) => { };
        this.title = title;
        this.elements = elements;
    }
    /**
     * @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 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(false);
                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);
            });
            form.show(player.UnsafeEntity).then(result => {
                if (result.selection !== undefined && player.isValid()) {
                    filteredElements[result.selection].onClick(player, ...params);
                    resolve(true);
                    this.onClosedCallback(player, ...params);
                }
                else {
                    resolve(false);
                    this.onCancelledCallback(player, ...params);
                }
            });
        });
    }
}
/**
 * @remarks A class for creating advanced {@link ui.ModalFormData} objects.
 */
export class ModalMenu extends Menu {
    showMenuToPlayer(player, ...params) {
        return new Promise((resolve) => {
            if (!player.isValid()) {
                resolve(false);
                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;
                }
            });
            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(true);
                    this.onClosedCallback(player, ...params);
                }
                else {
                    resolve(false);
                    this.onCancelledCallback(player, ...params);
                }
            });
        });
    }
}
/**
 * @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(false);
                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);
            form.show(player.UnsafeEntity).then(result => {
                if (result.selection === 0 && player.isValid()) {
                    this.confirmButton.onClick(player, ...params);
                    resolve(true);
                    this.onClosedCallback(player, ...params);
                    return;
                }
                else if (result.selection === 1 && player.isValid()) {
                    this.denyButton.onClick(player, ...params);
                    resolve(true);
                    this.onClosedCallback(player, ...params);
                    return;
                }
                resolve(false);
                this.onCancelledCallback(player, ...params);
            });
        });
    }
}
