import * as mc from "@minecraft/server";
import { getAllPlayersSafe, SafetyCheckedPlayer } from "./entity";
import { clearDynamicProperty, readDynamicProperty, writeDynamicProperty } from "./properties";
import { ButtonMenu } from "./menu";
import { Command, StringArg } from "./debug";
import { TextFormat, translate } from "./text_format";
class Version {
    static get() {
        return this.version;
    }
    static set(version) {
        this.version = version;
    }
}
Version.version = "1.0.0";
/**
 * @remarks Initializes the patch notes menu. This stamps all players with the current version number, and if they already have a stamp for a previous version
 * it will show them the patch notes menu. The patch notes expects the following lang entries:
 * - `project_namespace:update.version.name=Project Name v%1` # %1 will be dynamically replaced with the provided version number.
 * - `project_namespace:update.button=Confirm Text`
 * - `project_namespace:update.body=A description%1of the current patch v%2` # %1 will be replaced with newlines %2 will be replaced with the provided version number.
 *
 * Also adds the command `scriptevent ldz:test_patch` which will set the player's patch number and trigger the patch menu.
 * @param version_number The current version number of the project as x.x.x.
 * @param required_difference The level of difference required to show the menu to a player.
 */
export function setVersionNumber(version_number, required_difference = { major: true, minor: true, patch: false }) {
    if (version_number.startsWith("v"))
        version_number = version_number.slice(1);
    Version.set(version_number);
    mc.world.afterEvents.playerSpawn.subscribe(event => {
        if (event.initialSpawn) {
            displayPatchNotes(new SafetyCheckedPlayer(event.player), version_number, required_difference);
        }
    });
    getAllPlayersSafe().forEach(player => displayPatchNotes(player, version_number, required_difference));
    new Command("test_patch", (source, version) => {
        const player = source.getPlayer();
        if (player) {
            if (!version)
                version = "0.0.0";
            if (!isVersionValid(version)) {
                player.sendMessage(TextFormat.red(`${version} is not a valid semantic version number, must be x.x.x`));
                return;
            }
            writeDynamicProperty(player, "version", version);
            player.sendMessage(`Your version number was set to ${version}. Now /reload or rejoin the game to test the menu prompt`);
        }
    })
        .optional(new StringArg())
        .description("Opens the patch notes menu, you can optionally provide a version number, defaults to 0.0.0");
}
/**
 * Gets the current version number of the project as set by {@link setVersionNumber}.
 * @returns The current version number of the project as x.x.x.
 */
export function getVersionNumber() {
    return Version.get();
}
async function displayPatchNotes(player, version_number, requirements) {
    const player_version = readDynamicProperty(player, "version");
    const compare = player_version ? compareVersions(version_number, player_version) : { major: false, minor: false, patch: false };
    if ((compare.major && requirements.major) || (compare.minor && requirements.minor) || (compare.patch && requirements.patch)) {
        writeDynamicProperty(player, "patch_menu_mode", player.getGameMode());
        player.setGameMode(mc.GameMode.spectator);
        await new ButtonMenu(translate("update.version.name", [version_number]), [
            {
                text: translate("update.button"),
                onClick() { },
            }
        ], translate("update.body", ["\n", version_number])).queueMenuForPlayer(player);
    }
    resetGameMode(player);
    writeDynamicProperty(player, "version", version_number);
}
function resetGameMode(player) {
    const old_mode = readDynamicProperty(player, "patch_menu_mode");
    if (player.matches({ gameMode: mc.GameMode.spectator }) && old_mode) {
        player.setGameMode(old_mode);
        clearDynamicProperty(player, "patch_menu_mode");
    }
}
function compareVersions(current_version, compare_version) {
    const current = current_version.split(".").map(val => Number(val));
    const compare = compare_version.split(".").map(val => Number(val));
    return {
        major: current[0] - compare[0] > 0,
        minor: current[1] - compare[1] > 0,
        patch: current[1] - compare[1] > 0,
    };
}
function isVersionValid(version) {
    if (!version)
        return false;
    const v = version.split(".").map(val => Number(val));
    return v.length === 3 && v.every(val => !isNaN(val));
}
