import { system, world } from '@minecraft/server';
import { ActionFormData, FormRejectError, FormCancelationReason } from '@minecraft/server-ui';

const asyncWait = async (ticks) => {
    return new Promise((resolve) => {
        system.runTimeout(() => {
            resolve();
        }, ticks);
    });
};
// Use a builder pattern
// builder pattern: https://medium.com/geekculture/implementing-a-type-safe-object-builder-in-typescript-e973f5ecfb9c
// Needs updating to support optional types
// class ObjectBuilder {
//   public static new<Target>(): IWith<Target, {}> {
//     return new Builder<Target, {}>({});
//   }
// }
//
// interface IWith<Target, Supplied> {
//   with<T extends Omit<Target, keyof Supplied>, K extends keyof T>(
//     key: K,
//     value: T[K],
//   ): keyof Omit<Omit<Target, keyof Supplied>, K> extends never
//     ? IBuild<Target>
//     : IWith<Target, Supplied & Pick<T, K>>;
// }
//
// interface IBuild<Target> {
//   build(): Target;
// }
//
// class Builder<Target, Supplied> implements IBuild<Target>, IWith<Target, Supplied> {
//   constructor(private target: Partial<Target>) {}
//
//   with<T extends Omit<Target, keyof Supplied>, K extends keyof T>(key: K, value: T[K]) {
//     const target: Partial<Target> = { ...this.target, [key]: value };
//
//     return new Builder<Target, Supplied & Pick<T, K>>(target);
//   }
//
//   build() {
//     return this.target as Target;
//   }
// }

const DefaultShowDialogOptions = Object.freeze({
    lockPlayerCamera: true,
    busyRetriesCount: 5,
    busyRetriesTick: 5,
});
/**
 * Base class for all the script dialogues
 *
 * @category Script dialogue
 */
class ScriptDialogue {
    constructor() { }
    /**
     * Opens the script dialogue. It requires the {@link ShowDialogueOptions#player player} as one of the options.
     *
     * You can configure displaying features by setting the optional values in the {@link ShowDialogueOptions options}
     * @param options used to configure what and how to display the script dialogue.
     *
     * @see {@link ShowDialogueOptions}
     */
    async open(options) {
        const resolvedOptions = this.resolveShowDialogueOptions(options);
        try {
            if (resolvedOptions.lockPlayerCamera) {
                this.lockPlayerCamera(resolvedOptions);
            }
            try {
                const showable = await this.getShowable(resolvedOptions);
                const response = await this.show(showable, resolvedOptions);
                if (response.canceled) {
                    return new DialogueCanceledResponse(response.cancelationReason);
                }
                return await this.processResponse(response, resolvedOptions);
            }
            catch (e) {
                if (e && e instanceof FormRejectError) {
                    return new DialogueRejectedResponse(e.reason, e);
                }
                else {
                    return new DialogueRejectedResponse(undefined, e);
                }
            }
        }
        finally {
            if (resolvedOptions.lockPlayerCamera) {
                this.unlockPlayerCamera(resolvedOptions);
            }
        }
    }
    async show(showable, options) {
        let i = 0;
        for (;;) {
            const response = await showable.show(options.player);
            if (response.canceled && response.cancelationReason === FormCancelationReason.UserBusy) {
                if (i < options.busyRetriesCount) {
                    i++;
                    await asyncWait(options.busyRetriesTick);
                    if (options.player.isValid()) {
                        continue;
                    }
                }
            }
            return response;
        }
    }
    lockPlayerCamera(options) {
        options.player.runCommand(`inputpermission set "${options.player.name}" camera disabled`);
        options.player.runCommand(`inputpermission set "${options.player.name}" movement disabled`);
    }
    unlockPlayerCamera(options) {
        options.player.runCommand(`inputpermission set "${options.player.name}" camera enabled`);
        options.player.runCommand(`inputpermission set "${options.player.name}" movement enabled`);
    }
    resolveShowDialogueOptions(options) {
        return {
            ...DefaultShowDialogOptions,
            ...options,
        };
    }
}
/**
 * Base class for script dialogue responses
 *
 * @category Responses
 */
class ScriptDialogueResponse {
}
/**
 * Dialogue response when the script dialogue was canceled.
 *
 * Typically this happens if the player was busy when trying to open the script dialogue or if the user
 * pressed the close button (x).
 *
 * Valid reasons can be seen in {@link @minecraft/server-ui!FormCancelationReason FormCancelationReason}
 *
 * @category Responses
 */
class DialogueCanceledResponse extends ScriptDialogueResponse {
    /**
     * Reason the script dialogue was cancel.
     */
    reason;
    /**
     * @internal
     */
    constructor(reason) {
        super();
        this.reason = reason;
    }
}
/**
 * Dialogue response when the script dialogue was rejected, throwing an exception.
 *
 * Reasons can be seen in {@link @minecraft/server-ui!FormRejectReason FormRejectReason} if any reason could be
 * determined.
 *
 * Known reasons can be seen in {@link @minecraft/server-ui!FormRejectReason FormRejectReason}
 * @category Responses
 */
class DialogueRejectedResponse extends ScriptDialogueResponse {
    /**
     * Reason the script dialogue was rejected if there was any, else the {@link exception} needs to be checked
     * to further determine what was the error.
     */
    reason;
    /**
     * Exception that was throw to rejected the script dialogue.
     */
    exception;
    /**
     * @internal
     */
    constructor(reason, exception) {
        super();
        this.reason = reason;
        this.exception = exception;
    }
}
/**
 * Response for button script dialogues.
 *
 * Contains the selected button's name.
 *
 * @category Responses
 * @category Dual button script dialogue
 * @category Multi button script dialogue
 */
class ButtonDialogueResponse extends ScriptDialogueResponse {
    /**
     * Selected button's name.
     *
     * @see {@link DualButton#name}
     * @see {@link MultiButton#name}
     */
    selected;
    callback;
    /**
     * @internal
     */
    constructor(selected, callback) {
        super();
        this.selected = selected;
        this.callback = callback;
    }
}
class MissingButtonsException extends Error {
    constructor() {
        super('Missing buttons');
    }
}

/**
 * Initializes a empty multi button script dialogue.
 *
 * Buttons needs to be added using {@link MultiButtonDialogue#addButton} or {@link MultiButtonDialogue#addButtons}
 *
 * @category Creation
 * @category Multi button script dialogue
 * @param title
 */
const multiButtonScriptDialogue = (title) => {
    return new MultiButtonDialogue(title, undefined, []);
};
/**
 * Class used to build multi button script dialogues.
 *
 * Use {@link multiButtonScriptDialogue} to initialize one.
 *
 * @category Multi button script dialogue
 * @see {@link multiButtonScriptDialogue}
 */
class MultiButtonDialogue extends ScriptDialogue {
    title;
    body;
    buttons;
    /**
     * @internal
     */
    constructor(title, body, buttons) {
        super();
        this.title = title;
        this.body = body;
        this.buttons = buttons;
    }
    /**
     * Sets the content body of the multi button dialogue
     * @param body
     */
    setBody(body) {
        return new MultiButtonDialogue(this.title, body, this.buttons);
    }
    /**
     * Adds a button to the multi button script dialogue.
     * @param name name of the button
     * @param text content of the button
     * @param iconPath path to an icon to show in the button
     */
    addButton(name, text, iconPath, callback) {
        return new MultiButtonDialogue(this.title, this.body, [
            ...this.buttons,
            {
                name,
                text,
                iconPath,
                callback,
            },
        ]);
    }
    /**
     * Adds multiple buttons to the multi button script dialogue.
     * @param buttons array of buttons
     */
    addButtons(buttons) {
        return new MultiButtonDialogue(this.title, this.body, [
            ...this.buttons,
            ...buttons,
        ]);
    }
    getShowable(_options) {
        if (this.buttons.length === 0) {
            throw new MissingButtonsException();
        }
        const formData = new ActionFormData();
        formData.title(this.title);
        if (this.body) {
            formData.body(this.body);
        }
        this.buttons.forEach((button) => {
            formData.button(button.text, button.iconPath);
        });
        return formData;
    }
    async processResponse(response, _options) {
        const selectedButton = this.buttons[response.selection];
        let callbackResponse = undefined;
        if (selectedButton.callback) {
            callbackResponse = await selectedButton.callback(selectedButton.name);
        }
        return new ButtonDialogueResponse(selectedButton.name, callbackResponse);
    }
}
/**
 * Script dialogue response from a input script dialogue.
 * Holds a map with the values used by each of the element's name.
 *
 * @category Input script dialogue
 * @category Responses
 */
class InputScriptDialogueResponse {
    /**
     * Map with the values of the script dialogue..
     */
    values;
    constructor(values) {
        this.values = values;
    }
}

const defaultScriptDialogueOptions = {
    busyRetriesCount: 5,
    busyRetriesTick: 5,
    lockPlayerCamera: true,
};

const TRANSLATE = (key, ...params) => ({
    translate: key,
    with: params
});
const RAW_TEXT = (...rawText) => ({
    rawtext: rawText
});
const TEXT = (value) => ({
    text: value.toString()
});

const viewTut_Book_1 = () => {
    return multiButtonScriptDialogue(RAW_TEXT(TRANSLATE("jig_atw:book.name")))
        .setBody(RAW_TEXT(TRANSLATE("jig_atw:info_book.page_01.a", "\n\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_02.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_03.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_04.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_05.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_06.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_07.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_08.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_09.a", "\n"), TEXT('§r\n\n'), TRANSLATE("jig_atw:info_book.page_10.a", "\n\n", "\n", "\n\n")))
        .addButton('exit', TRANSLATE("jig_atw:book.close"));
};

world.afterEvents.playerSpawn.subscribe(async (event) => {
    event.player.runCommand("function jig/atw/init");
    event.player.runCommand("function jig/atw/book_giver");
});
const GUIDE_BOOK = "jig_atw:guide_book";
world.afterEvents.itemUse.subscribe(async (event) => {
    let player = event.source;
    const { source, itemStack } = event;
    if (itemStack.typeId === GUIDE_BOOK) {
        player.playSound('mob.sheep.say');
        const response = await viewTut_Book_1().open({ ...defaultScriptDialogueOptions, player });
        if (response instanceof InputScriptDialogueResponse) {
            player.playSound('mob.sheep.say');
        }
    }
    if (event.itemStack.typeId === "jig_common:wrench") {
        const hit = event.source.dimension.getBlockFromRay(event.source.getHeadLocation(), event.source.getViewDirection(), {
            maxDistance: 9
        });
        if (hit) {
            const jigTetaType = hit.block.permutation.getState("jig_atw:type");
            if (typeof jigTetaType === "number") {
                hit.block.setPermutation(hit.block.permutation.withState("jig_atw:type", jigTetaType + 1)); //increment the state by 1
                world.playSound("dig.cloth", hit.block.location);
            }
        }
    }
});
//# sourceMappingURL=main.js.map
