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.title = title;
        this.elements = elements;
    }
}
/**
 * @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) {
        if (!player.isValid())
            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);
            }
        });
    }
}
/**
 * @remarks A class for creating advanced {@link ui.ModalFormData} objects.
 */
export class ModalMenu extends Menu {
    showMenuToPlayer(player, ...params) {
        if (!player.isValid())
            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':
                            submittedElement.options[Number(value)].onSelected(player, ...params);
                            break;
                        case 'slider':
                            submittedElement.onSumbit(player, Number(value), ...params);
                            break;
                        case 'textField':
                            submittedElement.onSumbit(player, String(value), ...params);
                            break;
                        case "toggle":
                            submittedElement.onSumbit(player, Boolean(value), ...params);
                            break;
                        default:
                            mcerror(`Unexpected type${new Error().stack}`);
                            break;
                    }
                });
            }
        });
    }
}
/**
 * @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) {
        if (!player.isValid())
            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);
            }
            else if (result.selection === 0 && player.isValid()) {
                this.denyButton.onClick(player, ...params);
            }
        });
    }
}
