"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.SidePaneMinWidth = exports.SidePaneMaxWidth = exports.CartoTargetStrings = exports.CartoMinecraftErrorStatus = exports.CartoMinecraftState = void 0;
const ICartoData_1 = require("./ICartoData");
const Project_1 = require("./Project");
const ste_events_1 = require("ste-events");
const Status_1 = require("./Status");
const StorageUtilities_1 = require("../storage/StorageUtilities");
const Utilities_1 = require("../core/Utilities");
const GitHubManager_1 = require("../github/GitHubManager");
const axios_1 = require("axios");
const Log_1 = require("../core/Log");
const AppServiceProxy_1 = require("../core/AppServiceProxy");
const CommandRunner_1 = require("./CommandRunner");
const CartoApp_1 = require("./CartoApp");
const WorldLevelDat_1 = require("../minecraft/WorldLevelDat");
const IWorldSettings_1 = require("../minecraft/IWorldSettings");
const Package_1 = require("./Package");
const CommandRegistry_1 = require("./CommandRegistry");
const ZipStorage_1 = require("../storage/ZipStorage");
const IProjectItemData_1 = require("./IProjectItemData");
const DeploymentStorageMinecraft_1 = require("./DeploymentStorageMinecraft");
const MinecraftUtilities_1 = require("../minecraft/MinecraftUtilities");
const IGalleryItem_1 = require("./IGalleryItem");
const ProjectUtilities_1 = require("./ProjectUtilities");
var CartoMinecraftState;
(function (CartoMinecraftState) {
    CartoMinecraftState[CartoMinecraftState["none"] = 0] = "none";
    CartoMinecraftState[CartoMinecraftState["initializing"] = 1] = "initializing";
    // for remote server, this is authenticating
    // for web sockets, this is opening up the web socket to receive connections from a client
    CartoMinecraftState[CartoMinecraftState["initialized"] = 2] = "initialized";
    CartoMinecraftState[CartoMinecraftState["preparing"] = 3] = "preparing";
    CartoMinecraftState[CartoMinecraftState["prepared"] = 4] = "prepared";
    CartoMinecraftState[CartoMinecraftState["starting"] = 5] = "starting";
    // for remote server, this is creating a session on a socket
    // for web sockets, note you have to wait for external connections to come in.
    CartoMinecraftState[CartoMinecraftState["started"] = 6] = "started";
    // for remote server, the session is ready and initial status has been established
    // for web sockets, an external client has connected in
    CartoMinecraftState[CartoMinecraftState["stopping"] = 7] = "stopping";
    CartoMinecraftState[CartoMinecraftState["stopped"] = 8] = "stopped";
    CartoMinecraftState[CartoMinecraftState["error"] = 9] = "error";
    CartoMinecraftState[CartoMinecraftState["newMinecraft"] = 10] = "newMinecraft";
    CartoMinecraftState[CartoMinecraftState["disconnected"] = 11] = "disconnected";
})(CartoMinecraftState = exports.CartoMinecraftState || (exports.CartoMinecraftState = {}));
var CartoMinecraftErrorStatus;
(function (CartoMinecraftErrorStatus) {
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["none"] = 0] = "none";
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["actionInProgress"] = 1] = "actionInProgress";
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["serverUnavailable"] = 2] = "serverUnavailable";
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["serverError"] = 3] = "serverError";
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["loginFailed"] = 4] = "loginFailed";
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["generalError"] = 5] = "generalError";
    CartoMinecraftErrorStatus[CartoMinecraftErrorStatus["configuration"] = 6] = "configuration";
})(CartoMinecraftErrorStatus = exports.CartoMinecraftErrorStatus || (exports.CartoMinecraftErrorStatus = {}));
exports.CartoTargetStrings = [
    "Latest Minecraft Bedrock",
    "Latest Minecraft Bedrock preview",
    "Latest Minecraft Education",
    "Latest Minecraft Education preview",
];
exports.SidePaneMaxWidth = 880;
exports.SidePaneMinWidth = 280;
class Carto {
    _isLoaded;
    _userGitHub;
    _anonGitHub;
    contentRoot = "";
    processHostedMinecraft;
    deploymentStorageMinecraft;
    gameMinecraft;
    remoteMinecraft;
    activeMinecraft;
    prefsStorage;
    projectsStorage;
    deploymentStorage;
    previewDeploymentStorage;
    worldStorage;
    packStorage;
    workingStorage;
    localFolderExists;
    localFileExists;
    ensureLocalFolder;
    createMinecraft;
    canCreateMinecraft;
    _deployBehaviorPacksFolder;
    _deployResourcePacksFolder;
    _pendingPackLoadRequests = [];
    _arePacksLoading = false;
    local;
    #data;
    projects;
    status;
    activeOperations;
    packs;
    mcLogs = {};
    _gallery;
    _galleryLoaded = false;
    isDeployingToComMojang = false;
    hasAttemptedPersistentBrowserStorageSwitch = false;
    _onDeploymentStorageChanged = new ste_events_1.EventDispatcher();
    _onMinecraftStateChanged = new ste_events_1.EventDispatcher();
    _onMinecraftRefreshed = new ste_events_1.EventDispatcher();
    _onPropertyChanged = new ste_events_1.EventDispatcher();
    _onLoaded = new ste_events_1.EventDispatcher();
    _onStatusAdded = new ste_events_1.EventDispatcher();
    _onOperationCompleted = new ste_events_1.EventDispatcher();
    _onStatusAddedAsync = [];
    _onGalleryLoaded = new ste_events_1.EventDispatcher();
    get isLoaded() {
        return this._isLoaded;
    }
    get collapsedTypes() {
        if (!this.#data.collapsedTypes) {
            this.#data.collapsedTypes = [];
        }
        return this.#data.collapsedTypes;
    }
    get data() {
        return this.#data;
    }
    set collapsedTypes(newCollapsedTypes) {
        this.#data.collapsedTypes = newCollapsedTypes;
    }
    get worldSettings() {
        return this.#data.worldSettings;
    }
    get editorWorldSettings() {
        return this.#data.editorWorldSettings;
    }
    get activeMinecraftState() {
        if (this.activeMinecraft === undefined) {
            return CartoMinecraftState.none;
        }
        return this.activeMinecraft.state;
    }
    get onMinecraftStateChanged() {
        return this._onMinecraftStateChanged.asEvent();
    }
    get onDeploymentStorageChanged() {
        return this._onDeploymentStorageChanged.asEvent();
    }
    get formatBeforeSave() {
        if (this.#data.formatBeforeSave === undefined) {
            return true;
        }
        return this.#data.formatBeforeSave;
    }
    set formatBeforeSave(newValue) {
        this.#data.formatBeforeSave = newValue;
    }
    get conversionJarPath() {
        return this.#data.conversionJarPath;
    }
    set conversionJarPath(newValue) {
        this.#data.conversionJarPath = newValue;
    }
    get preferredTextSize() {
        if (this.#data.preferredTextSize === undefined) {
            return 16;
        }
        return this.#data.preferredTextSize;
    }
    set preferredTextSize(newValue) {
        this.#data.preferredTextSize = newValue;
    }
    get itemSidePaneWidth() {
        if (this.#data.itemSidePaneWidth === undefined) {
            return 300;
        }
        if (this.#data.itemSidePaneWidth < exports.SidePaneMinWidth) {
            return exports.SidePaneMinWidth;
        }
        if (this.#data.itemSidePaneWidth > exports.SidePaneMaxWidth) {
            return exports.SidePaneMaxWidth;
        }
        return this.#data.itemSidePaneWidth;
    }
    set itemSidePaneWidth(newValue) {
        this.#data.itemSidePaneWidth = newValue;
    }
    get preferredSuite() {
        return this.#data.preferredSuite;
    }
    set preferredSuite(newValue) {
        this.#data.preferredSuite = newValue;
    }
    get track() {
        return this.#data.track;
    }
    set track(newTrack) {
        if (newTrack !== this.#data.track) {
            this.#data.track = newTrack;
            this._onPropertyChanged.dispatch(this, "track");
        }
    }
    get effectiveTrack() {
        if (this.#data.track !== undefined) {
            return this.#data.track;
        }
        return ICartoData_1.MinecraftTrack.main;
    }
    get useEditor() {
        return this.#data.useEditor;
    }
    set useEditor(newUseEditor) {
        if (newUseEditor !== this.#data.useEditor) {
            this.#data.useEditor = newUseEditor;
        }
    }
    get windowX() {
        if (this.#data.windowX === undefined) {
            return 0;
        }
        return this.#data.windowX;
    }
    set windowX(newVal) {
        this.#data.windowX = newVal;
    }
    get windowY() {
        if (this.#data.windowY === undefined) {
            return 0;
        }
        return this.#data.windowY;
    }
    set windowY(newVal) {
        this.#data.windowY = newVal;
    }
    get windowWidth() {
        if (this.#data.windowWidth === undefined) {
            return 1200;
        }
        return this.#data.windowWidth;
    }
    set windowWidth(newVal) {
        this.#data.windowWidth = newVal;
    }
    get windowHeight() {
        if (this.#data.windowHeight === undefined) {
            return 900;
        }
        return this.#data.windowHeight;
    }
    set windowHeight(newVal) {
        this.#data.windowHeight = newVal;
    }
    get windowSlot() {
        if (this.#data.windowSlot === undefined) {
            return 0;
        }
        return this.#data.windowSlot;
    }
    set windowSlot(newVal) {
        this.#data.windowSlot = newVal;
    }
    get creator() {
        return this.#data.creator;
    }
    set creator(newCreator) {
        if (this.#data.creator !== newCreator) {
            this.#data.creator = newCreator;
            this._onPropertyChanged.dispatch(this, "creator");
        }
    }
    get windowState() {
        if (this.#data.windowState === undefined) {
            return ICartoData_1.WindowState.regular;
        }
        return this.#data.windowState;
    }
    set windowState(newVal) {
        this.#data.windowState = newVal;
    }
    get lastActiveMinecraftFlavor() {
        return this.#data.lastActiveMinecraftFlavor;
    }
    set lastActiveMinecraftFlavor(lastActiveMinecraftFlavor) {
        if (lastActiveMinecraftFlavor !== this.#data.lastActiveMinecraftFlavor) {
            this.#data.lastActiveMinecraftFlavor = lastActiveMinecraftFlavor;
        }
    }
    get remoteServerUrl() {
        if (this.#data.remoteServerUrl === undefined && CartoApp_1.default.baseUrl) {
            return Utilities_1.default.getBaseUrl(CartoApp_1.default.baseUrl);
        }
        return this.#data.remoteServerUrl;
    }
    get fullRemoteServerUrl() {
        if (!this.remoteServerUrl) {
            return undefined;
        }
        let url = this.remoteServerUrl.toLowerCase();
        if (!url.startsWith("http") && url.indexOf("//") < 0) {
            if (url.indexOf("localhost") >= 0) {
                url = "http://" + url;
            }
            else {
                url = "https://" + url;
            }
        }
        url = Utilities_1.default.ensureEndsWithSlash(url);
        return url;
    }
    set remoteServerUrl(newPath) {
        if (newPath !== this.#data.remoteServerUrl) {
            this.#data.remoteServerUrl = newPath;
        }
    }
    get iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula() {
        return this.#data.iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula;
    }
    set iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula(newPort) {
        this.#data.iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula = newPort;
    }
    get dedicatedServerSlotCount() {
        return this.#data.dedicatedServerSlotCount;
    }
    set dedicatedServerSlotCount(newPort) {
        this.#data.dedicatedServerSlotCount = newPort;
    }
    get dedicatedServerMode() {
        if (this.#data.dedicatedServerMode === undefined) {
            return ICartoData_1.DedicatedServerMode.auto;
        }
        return this.#data.dedicatedServerMode;
    }
    set dedicatedServerMode(newMode) {
        this.#data.dedicatedServerMode = newMode;
    }
    get dedicatedServerPath() {
        return this.#data.dedicatedServerPath;
    }
    set minecraftGameMode(newMode) {
        this.#data.webSocketMode = newMode;
    }
    get minecraftGameMode() {
        return this.#data.webSocketMode;
    }
    set dedicatedServerPath(newPath) {
        this.#data.dedicatedServerPath = newPath;
    }
    get remoteServerPort() {
        if (this.#data.remoteServerPort === undefined) {
            return 0;
        }
        return this.#data.remoteServerPort;
    }
    set remoteServerPort(newPort) {
        this.#data.remoteServerPort = newPort;
    }
    get remoteServerAccessLevel() {
        return this.#data.remoteServerAccessLevel;
    }
    set remoteServerAccessLevel(newAccessLevel) {
        this.#data.remoteServerAccessLevel = newAccessLevel;
    }
    get remoteServerPasscode() {
        return this.#data.remoteServerPasscode;
    }
    set remoteServerPasscode(newPath) {
        this.#data.remoteServerPasscode = newPath;
    }
    get remoteServerAuthToken() {
        return this.#data.remoteServerAuthToken;
    }
    set remoteServerAuthToken(newPath) {
        this.#data.remoteServerAuthToken = newPath;
    }
    get editorViewMode() {
        if (this.#data.editorViewMode === undefined) {
            return ICartoData_1.CartoEditorViewMode.itemsOnLeft;
        }
        return this.#data.editorViewMode;
    }
    set editorViewMode(newViewMode) {
        this.#data.editorViewMode = newViewMode;
    }
    get gallery() {
        return this._gallery;
    }
    get galleryLoaded() {
        return this._galleryLoaded;
    }
    get userGitHub() {
        if (this._userGitHub === undefined) {
            this._userGitHub = new GitHubManager_1.default(this.prefsStorage.rootFolder.ensureFile("github.json"));
        }
        return this._userGitHub;
    }
    get anonGitHub() {
        if (this._anonGitHub === undefined) {
            this._anonGitHub = new GitHubManager_1.default();
        }
        return this._anonGitHub;
    }
    get onLoaded() {
        return this._onLoaded.asEvent();
    }
    get onGalleryLoaded() {
        return this._onGalleryLoaded.asEvent();
    }
    get onPropertyChanged() {
        return this._onPropertyChanged.asEvent();
    }
    get onStatusAdded() {
        return this._onStatusAdded.asEvent();
    }
    get onOperationCompleted() {
        return this._onOperationCompleted.asEvent();
    }
    get successfullyConnectedWebSocketToMinecraft() {
        if (this.#data.successfullyConnectedWebSocketToMinecraft === undefined) {
            return false;
        }
        return this.#data.successfullyConnectedWebSocketToMinecraft;
    }
    set successfullyConnectedWebSocketToMinecraft(newValue) {
        this.#data.successfullyConnectedWebSocketToMinecraft = newValue;
    }
    get successfullyStartedMinecraftServer() {
        if (this.#data.successfullyStartedMinecraftServer === undefined) {
            return false;
        }
        return this.#data.successfullyStartedMinecraftServer;
    }
    set successfullyStartedMinecraftServer(newValue) {
        this.#data.successfullyStartedMinecraftServer = newValue;
    }
    get successfullyConnectedToRemoteMinecraft() {
        if (this.#data.successfullyConnectedToRemoteMinecraft === undefined) {
            return false;
        }
        return this.#data.successfullyConnectedToRemoteMinecraft;
    }
    set successfullyConnectedToRemoteMinecraft(newValue) {
        this.#data.successfullyConnectedToRemoteMinecraft = newValue;
    }
    get defaultMinecraftFlavor() {
        if (this.#data.lastActiveMinecraftFlavor === undefined) {
            if (CartoApp_1.default.isAppServiceWeb) {
                return ICartoData_1.MinecraftFlavor.processHostedProxy;
            }
            else {
                return ICartoData_1.MinecraftFlavor.remote;
            }
        }
        return this.#data.lastActiveMinecraftFlavor;
    }
    subscribeStatusAddedAsync(fn) {
        this._onStatusAddedAsync.push(fn);
    }
    unsubscribeStatusAddedAsync(fn) {
        let newStatusAddedArr = [];
        for (let i = 0; i < this._onStatusAddedAsync.length; i++) {
            if (this._onStatusAddedAsync[i] !== fn) {
                newStatusAddedArr.push(this._onStatusAddedAsync[i]);
            }
        }
        this._onStatusAddedAsync = newStatusAddedArr;
    }
    setMinecraftFlavor(newValue) {
        this.ensureMinecraft(newValue);
    }
    get autoStartMinecraft() {
        if (this.#data.autoStartMinecraft === undefined) {
            return true;
        }
        return this.#data.autoStartMinecraft;
    }
    set autoStartMinecraft(newValue) {
        this.#data.autoStartMinecraft = newValue;
    }
    get file() {
        return this.prefsStorage.rootFolder.ensureFile("mctools.json");
    }
    get prefsProjectsFolder() {
        return this.prefsStorage.rootFolder.ensureFolder("projects");
    }
    get deployBehaviorPacksFolder() {
        return this._deployBehaviorPacksFolder;
    }
    get deployResourcePacksFolder() {
        return this._deployResourcePacksFolder;
    }
    constructor(settingsStorage, projectsStorage, deploymentsStorage, previewDeploymentsStorage, worldStorage, packStorage, workingStorage, contentRoot) {
        this.prefsStorage = settingsStorage;
        this.projectsStorage = projectsStorage;
        this.deploymentStorage = deploymentsStorage;
        this.previewDeploymentStorage = previewDeploymentsStorage;
        this.packStorage = packStorage;
        this.worldStorage = worldStorage;
        this.workingStorage = workingStorage;
        this._deployBehaviorPacksFolder = null;
        this._deployResourcePacksFolder = null;
        if (contentRoot) {
            this.contentRoot = contentRoot;
        }
        this._handleMessageFromAppService = this._handleMessageFromAppService.bind(this);
        this._bubbleMinecraftStateChanged = this._bubbleMinecraftStateChanged.bind(this);
        this._bubbleMinecraftRefreshed = this._bubbleMinecraftRefreshed.bind(this);
        AppServiceProxy_1.default.onMessage.subscribe(this._handleMessageFromAppService);
        this.#data = {
            successfullyConnectedWebSocketToMinecraft: false,
            successfullyStartedMinecraftServer: false,
            successfullyConnectedToRemoteMinecraft: false,
            autoStartMinecraft: true,
            showScreenOnConnect: true,
            customTools: [],
        };
        // in the case of a Minecraft Http Server self-hosted web page, assume all we want to do is connect back to our server
        if (CartoApp_1.default.baseUrl) {
            this.setMinecraftFlavor(ICartoData_1.MinecraftFlavor.remote);
            this.successfullyConnectedWebSocketToMinecraft = true;
        }
        this._isLoaded = false;
        this.projects = [];
        this.status = [];
        this.activeOperations = [];
        this.updateDeploymentStorage(deploymentsStorage);
    }
    updateDeploymentStorage(deploymentStorage) {
        this.deploymentStorage = deploymentStorage;
        if (this.deploymentStorage != null) {
            this._deployBehaviorPacksFolder = this.deploymentStorage.rootFolder.ensureFolder("development_behavior_packs");
            this._deployResourcePacksFolder = this.deploymentStorage.rootFolder.ensureFolder("development_resource_packs");
        }
        else {
            this._deployBehaviorPacksFolder = null;
            this._deployResourcePacksFolder = null;
        }
        this._onDeploymentStorageChanged.dispatch(this, deploymentStorage);
    }
    initializeWorldSettings() {
        if (this.#data.worldSettings === undefined) {
            this.#data.worldSettings = {
                gameType: WorldLevelDat_1.GameType.creative,
                generator: WorldLevelDat_1.Generator.infinite,
                randomSeed: "2000",
                backupType: IWorldSettings_1.BackupType.every5Minutes,
                useCustomSettings: false,
                isEditor: false,
                packageReferences: [],
            };
            this.ensureDefaultWorldName();
        }
    }
    ensureDefaultWorldName() {
        if (this.worldSettings && this.worldSettings.name === undefined) {
            this.worldSettings.name = "world " + Utilities_1.default.getDateStr(new Date());
        }
    }
    initializeEditorWorldSettings() {
        if (this.#data.editorWorldSettings === undefined) {
            this.#data.editorWorldSettings = {
                gameType: WorldLevelDat_1.GameType.creative,
                generator: WorldLevelDat_1.Generator.infinite,
                backupType: IWorldSettings_1.BackupType.every5Minutes,
                useCustomSettings: false,
                isEditor: true,
                packageReferences: [],
            };
            this.ensureDefaultEditorWorldName();
        }
    }
    ensureDefaultEditorWorldName() {
        if (this.editorWorldSettings && this.editorWorldSettings.name === undefined) {
            this.editorWorldSettings.name = "editor project " + Utilities_1.default.getDateStr(new Date());
        }
    }
    getCustomTool(index) {
        if (this.#data.customTools === undefined) {
            this.#data.customTools = [];
        }
        while (this.#data.customTools.length <= index) {
            this.#data.customTools.push({
                name: "",
                type: 0,
                text: undefined,
                lastRunResult: undefined,
            });
        }
        return this.#data.customTools[index];
    }
    get defaultFunction() {
        if (this.#data === undefined || this.#data.defaultFunction === undefined) {
            return "";
        }
        return this.#data.defaultFunction;
    }
    set defaultFunction(newFunction) {
        if (this.#data === undefined) {
            return;
        }
        this.#data.defaultFunction = Utilities_1.default.makeSafeForJson(newFunction);
    }
    async runCommand(command, project) {
        return await CommandRegistry_1.default.main.runCommand({
            carto: this,
            project: project,
            minecraft: this.activeMinecraft,
            host: CartoApp_1.default.hostManager,
        }, command);
    }
    async runMinecraftCommand(command) {
        if (this.activeMinecraft === undefined) {
            throw new Error("No minecraft active.");
        }
        const result = await this.activeMinecraft.runCommand(command);
        return result;
    }
    async loadPacks() {
        if (!this.packStorage) {
            throw new Error("Could not find pack storage");
        }
        if (this._arePacksLoading) {
            const pendingLoad = this._pendingPackLoadRequests;
            const prom = (resolve, reject) => {
                pendingLoad.push(resolve);
            };
            await new Promise(prom);
        }
        else {
            this._arePacksLoading = true;
            this.packs = [];
            // console.log("Loading packs from '" + this.packStorage.rootFolder.fullPath + "'");
            await this.loadPacksFromFolder(this.packStorage.rootFolder);
            this._arePacksLoading = false;
            const pendingLoad = this._pendingPackLoadRequests;
            this._pendingPackLoadRequests = [];
            for (const prom of pendingLoad) {
                prom(undefined);
            }
        }
    }
    ensureAllTypesCollapsedExcept(itemType) {
        const newCollapsedTypes = [];
        for (let i = 0; i < IProjectItemData_1.MaxItemTypes; i++) {
            if (i !== itemType) {
                newCollapsedTypes.push(i);
            }
        }
        this.collapsedTypes = newCollapsedTypes;
    }
    ensureTypeIsCollapsed(itemType) {
        for (const itemTypeCollapsed of this.collapsedTypes) {
            if (itemTypeCollapsed === itemType) {
                return;
            }
        }
        this.collapsedTypes.push(itemType);
    }
    ensureTypeIsNotCollapsed(itemType) {
        const newCollapsedTypes = [];
        for (const itemTypeCollapsed of this.collapsedTypes) {
            if (itemTypeCollapsed !== itemType) {
                newCollapsedTypes.push(itemTypeCollapsed);
            }
        }
        this.#data.collapsedTypes = newCollapsedTypes;
    }
    async loadPacksFromFolder(folder) {
        await folder.load();
        for (let fileName in folder.files) {
            const file = folder.files[fileName];
            if (file && StorageUtilities_1.default.isContainerFile(file.storageRelativePath)) {
                await this.ensurePackForFile(file);
            }
        }
    }
    getPackByName(packName, isWorldFocused) {
        if (!this.packs) {
            Log_1.default.unexpectedUndefined("GPN");
            return;
        }
        for (let i = 0; i < this.packs.length; i++) {
            if (this.packs[i].matches(packName, isWorldFocused)) {
                return this.packs[i];
            }
        }
        return undefined;
    }
    getPackageByNameAndHash(packName, hash) {
        if (!this.packs) {
            Log_1.default.unexpectedUndefined("GPNH");
            return;
        }
        for (let i = 0; i < this.packs.length; i++) {
            if (this.packs[i].matches(packName, false) && (hash === undefined || hash === this.packs[i].data?.sourceHash)) {
                return this.packs[i];
            }
        }
        return this.getPackByName(packName);
    }
    async ensurePackForFile(file) {
        const pack = this._ensurePack(file.storageRelativePath);
        await pack.ensureData(this, file);
        return pack;
    }
    _ensurePack(storagePath) {
        if (this.packs === undefined) {
            this.packs = [];
        }
        for (let i = 0; i < this.packs.length; i++) {
            if (this.packs[i].storagePath === storagePath) {
                return this.packs[i];
            }
        }
        const pack = new Package_1.default(StorageUtilities_1.default.getLeafName(storagePath), storagePath);
        this.packs.push(pack);
        return pack;
    }
    _handleMessageFromAppService(command, data) {
        switch (command) {
            case "externalKeyPress":
                if (data.startsWith("command")) {
                    const commandIndex = parseInt(data.substring(7, data.length));
                    CommandRunner_1.default.runCustomTool(this, commandIndex);
                }
                break;
            case "statusMessage":
                const firstPipe = data.indexOf("|");
                const content = data.substring(firstPipe + 1, data.length);
                try {
                    const contentO = JSON.parse(content);
                    this.notifyExternalStatus(contentO);
                }
                catch (e) { }
                break;
            case "mctSavedInAppService":
                this.load(true);
                break;
            case "logFileUpdated":
                try {
                    if (data) {
                        const firstPipe = data.indexOf("|");
                        if (firstPipe > 0) {
                            const fileName = data.substring(0, firstPipe);
                            const content = data.substring(firstPipe + 1, data.length);
                            this._handleLogFileUpdated(fileName, content);
                        }
                    }
                }
                catch (e) {
                    Log_1.default.fail("Error parsing inbound log: " + e);
                }
                break;
            default:
                if (this.deploymentStorageMinecraft) {
                    this.deploymentStorageMinecraft.processExternalMessage(command, data);
                }
                if (this.processHostedMinecraft) {
                    this.processHostedMinecraft.processExternalMessage(command, data);
                }
                if (this.gameMinecraft) {
                    this.gameMinecraft.processExternalMessage(command, data);
                }
                if (this.remoteMinecraft) {
                    this.remoteMinecraft.processExternalMessage(command, data);
                }
                break;
        }
    }
    _handleLogFileUpdated(fileName, contents) {
        if (contents === null || contents === undefined || fileName === null || fileName === undefined) {
            return;
        }
        let arr = this.mcLogs[fileName];
        if (arr === undefined) {
            arr = [];
            if (Utilities_1.default.isUsableAsObjectKey(fileName)) {
                this.mcLogs[fileName] = arr;
            }
        }
        const logItems = contents.split("\r");
        for (let i = logItems.length - 1; i >= 0; i--) {
            const logItem = logItems[i];
            if (logItem !== undefined && logItem.length > 3) {
                if (!arr.includes(logItem)) {
                    arr.push(logItem);
                    this.notifyStatusUpdate(logItem);
                }
            }
        }
    }
    async notifyStatusUpdate(message, topic) {
        const messageCanon = message.trim().toLowerCase();
        if (messageCanon.length > 1) {
            const status = {
                time: new Date(),
                message: message,
                topic: topic,
                type: Status_1.StatusType.message,
            };
            this.status.push(status);
            await this.callStatusAddedListeners(status);
            this.ensureStatusArrayIsTrimmed();
        }
    }
    ensureStatusArrayIsTrimmed() {
        if (this.status.length > 10000) {
            const newStatusArr = [];
            for (let i = this.status.length - 9000; i < this.status.length; i++) {
                newStatusArr.push(this.status[i]);
            }
            this.status = newStatusArr;
        }
    }
    async callStatusAddedListeners(status) {
        this._onStatusAdded.dispatch(this, status);
        if (this._onStatusAddedAsync.length > 0) {
            let promises = [];
            for (let i = 0; i < this._onStatusAddedAsync.length; i++) {
                promises.push(this._onStatusAddedAsync[i](this, status));
            }
            await Promise.all(promises);
        }
    }
    notifyExternalStatus(status) {
        this.status.push(status);
        if (status.type === Status_1.StatusType.operationStarted) {
            this.activeOperations.push(status);
        }
        else if ((status.type === Status_1.StatusType.operationEndedComplete || status.type === Status_1.StatusType.operationEndedErrors) &&
            status.operationId !== null &&
            status.operationId !== undefined) {
            this.removeOperation(status.operationId);
        }
        this.callStatusAddedListeners(status);
        return status.operationId;
    }
    async notifyOperationUpdate(updateOperationId, message, topic) {
        this.ensureStatusArrayIsTrimmed();
        this.updateOperation(updateOperationId, message);
        const status = {
            message: message,
            operationId: updateOperationId,
            type: Status_1.StatusType.operationUpdate,
            time: new Date(),
            topic: topic,
        };
        this.status.push(status);
        await this.callStatusAddedListeners(status);
    }
    async notifyOperationStarted(message, topic) {
        const status = {
            time: new Date(),
            message: message,
            type: Status_1.StatusType.operationStarted,
            topic: topic,
        };
        status.operationId = status.time.getTime();
        this.status.push(status);
        this.activeOperations.push(status);
        this.ensureStatusArrayIsTrimmed();
        await this.callStatusAddedListeners(status);
        return status.operationId;
    }
    async notifyOperationEnded(endedOperationId, message, topic, endedWithErrors) {
        this.ensureStatusArrayIsTrimmed();
        this.removeOperation(endedOperationId);
        this._onOperationCompleted.dispatch(this, endedOperationId);
        if (!message) {
            return;
        }
        const status = {
            message: message,
            operationId: endedOperationId,
            type: endedWithErrors ? Status_1.StatusType.operationEndedErrors : Status_1.StatusType.operationEndedComplete,
            time: new Date(),
            topic: topic,
        };
        this.status.push(status);
        await this.callStatusAddedListeners(status);
    }
    updateOperation(id, newMessage) {
        for (let i = 0; i < this.activeOperations.length; i++) {
            const oper = this.activeOperations[i];
            if (oper.operationId === id) {
                oper.message = newMessage;
            }
        }
    }
    removeOperation(id) {
        // remove operation from list of active operations.
        const newActiveOperations = [];
        for (let i = 0; i < this.activeOperations.length; i++) {
            const oper = this.activeOperations[i];
            if (oper.operationId !== id) {
                newActiveOperations.push(oper);
            }
        }
        this.activeOperations = newActiveOperations;
    }
    async save() {
        const configFile = this.file;
        configFile.setContent(JSON.stringify(this.#data, null, 2));
        await configFile.saveContent();
        if (AppServiceProxy_1.default.hasAppService) {
            AppServiceProxy_1.default.sendAsync(AppServiceProxy_1.AppServiceProxyCommands.reloadMct, "");
        }
    }
    async loadGallery() {
        let result = null;
        // @ts-ignore
        if (typeof window !== "undefined") {
            const url = this.contentRoot + "data/gallery.json";
            try {
                result = await axios_1.default.get(url);
                if (result) {
                    this._gallery = result.data;
                }
            }
            catch (e) {
                Log_1.default.fail("Could not load gallery: " + e + " from '" + url + "'");
            }
        }
        else if (this.local) {
            try {
                result = await this.local.readJsonFile("data/gallery.json");
            }
            catch (e) {
                Log_1.default.fail("Could not load local file: " + e + " from 'data/gallery.json'");
            }
            if (result !== null) {
                this._gallery = result;
            }
        }
        if (this._gallery) {
            for (const item of this._gallery.items) {
                if (item.sampleSet) {
                    item.gitHubOwner = "microsoft";
                    item.gitHubRepoName = "minecraft-scripting-samples";
                    if (item.type === IGalleryItem_1.GalleryItemType.editorCodeSample) {
                        item.gitHubFolder = "/editor-script-box";
                        if (!item.tags) {
                            item.tags = ["editor"];
                        }
                    }
                    else {
                        item.gitHubFolder = "/script-box";
                    }
                }
                if (item.fileList) {
                    const newFileList = [];
                    for (const fileItem of item.fileList) {
                        newFileList.push(MinecraftUtilities_1.default.replaceMinecraftPathTokens(fileItem));
                    }
                    item.fileList = newFileList;
                }
            }
        }
        this._galleryLoaded = true;
        this._onGalleryLoaded.dispatch(this, this._gallery);
        return this._gallery;
    }
    async getGalleryProjectByGitHub(repoName, owner, branch, folder, withFiles) {
        if (!this._galleryLoaded) {
            await this.loadGallery();
        }
        if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) {
            return undefined;
        }
        repoName = repoName.toLowerCase();
        owner = owner.toLowerCase();
        for (const galProj of this._gallery.items) {
            if (galProj.gitHubRepoName &&
                galProj.gitHubOwner &&
                galProj.gitHubRepoName.toLowerCase() === repoName &&
                galProj.gitHubOwner.toLowerCase() === owner &&
                branch === galProj.gitHubBranch &&
                folder === galProj.gitHubFolder &&
                (!withFiles || galProj.fileList)) {
                return galProj;
            }
        }
        return undefined;
    }
    async getGalleryProjectById(galleryProjectId) {
        if (!this._galleryLoaded) {
            await this.loadGallery();
        }
        if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) {
            return undefined;
        }
        galleryProjectId = galleryProjectId.toLowerCase();
        galleryProjectId = galleryProjectId.replace(/-/gi, "");
        for (const galProj of this._gallery.items) {
            if (galProj.id.toLowerCase() === galleryProjectId) {
                return galProj;
            }
        }
        return undefined;
    }
    async getGalleryProjectByCaption(galleryProjectTitle) {
        if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) {
            return undefined;
        }
        for (const galProj of this._gallery.items) {
            if (galProj.title === galleryProjectTitle) {
                return galProj;
            }
        }
        return undefined;
    }
    getGalleryProjectByType(itemType) {
        if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) {
            return undefined;
        }
        const galleryItems = [];
        for (const galProj of this._gallery.items) {
            if (galProj.type === itemType) {
                galleryItems.push(galProj);
            }
        }
        return galleryItems;
    }
    async getNewProjectName(seedName) {
        await this.load();
        let newProjectName = seedName;
        let counter = 0;
        while (this.prefsProjectsFolder.fileExists(newProjectName + ".json") ||
            this.projectsStorage.rootFolder.folderExists(newProjectName)) {
            counter++;
            newProjectName = seedName + " " + counter;
        }
        return newProjectName;
    }
    async createNewProject(newProjectName, newProjectPath, newProjectFolder, newProjectFolderTitle, focus, includeDefaultItems, projectLanguage) {
        await this.load();
        const targetProjectName = await this.getNewProjectName(newProjectName);
        let projectPrefs;
        if (newProjectFolder) {
            projectPrefs = await newProjectFolder.ensureFileFromRelativePath("/.mct/prefs.mctp.json");
        }
        else {
            projectPrefs = await this.prefsProjectsFolder.createFile(targetProjectName + ".json");
        }
        const newProject = new Project_1.default(this, targetProjectName, projectPrefs);
        if (newProjectPath && !newProjectFolder) {
            newProject.localFolderPath = newProjectPath;
        }
        if (projectLanguage) {
            newProject.preferredScriptLanguage = projectLanguage;
        }
        if (newProjectFolder) {
            newProject.setProjectFolder(newProjectFolder);
            newProject.projectFolderTitle = newProjectFolderTitle;
        }
        else {
            await newProject.ensureProjectFolder();
        }
        newProject.focus = focus;
        if (includeDefaultItems) {
            await ProjectUtilities_1.default.ensureDefaultItems(newProject);
        }
        this.projects.push(newProject);
        return newProject;
    }
    async createNewProjectFromFolder(path) {
        await this.load();
        let newProjectName = StorageUtilities_1.default.convertFolderPlaceholdersComplete(StorageUtilities_1.default.getLeafName(path));
        let counter = 0;
        while (this.prefsProjectsFolder.fileExists(newProjectName + ".json") ||
            this.projectsStorage.rootFolder.folderExists(newProjectName)) {
            counter++;
            newProjectName = StorageUtilities_1.default.getLeafName(path) + " " + counter;
        }
        const projectPrefs = await this.prefsProjectsFolder.createFile(newProjectName + ".json");
        const newProject = new Project_1.default(this, newProjectName, projectPrefs);
        newProject.localFolderPath = path;
        await newProject.ensureProjectFolder();
        await newProject.ensureInflated();
        await newProject.inferProjectItemsFromFiles();
        this.projects.push(newProject);
        return newProject;
    }
    async deleteProjectByName(projectName) {
        let projectToDelete;
        for (let i = 0; i < this.projects.length; i++) {
            const proj = this.projects[i];
            if (proj.name === projectName) {
                if (projectToDelete !== undefined) {
                    Log_1.default.fail("Encountered multiple projects with the same name.");
                    return;
                }
                projectToDelete = proj;
            }
        }
        if (projectToDelete) {
            await projectToDelete.deleteThisProject();
            this.removeProject(projectToDelete);
            projectToDelete.dispose();
        }
    }
    async removeProjectByName(projectName) {
        let projectToRemove;
        for (let i = 0; i < this.projects.length; i++) {
            const proj = this.projects[i];
            if (proj.name === projectName) {
                if (projectToRemove !== undefined) {
                    Log_1.default.fail("Encountered multiple projects with the same name.");
                    return;
                }
                projectToRemove = proj;
            }
        }
        if (!projectToRemove) {
            Log_1.default.fail("Could not find project to remove: " + projectName);
            return;
        }
        await projectToRemove.deletePreferencesFile();
        this.removeProject(projectToRemove);
        projectToRemove.dispose();
    }
    removeProject(project) {
        const newProjects = [];
        for (let i = 0; i < this.projects.length; i++) {
            const proj = this.projects[i];
            if (proj !== project) {
                newProjects.push(proj);
            }
        }
        this.projects = newProjects;
    }
    getProjectByName(projectName) {
        for (let i = 0; i < this.projects.length; i++) {
            const proj = this.projects[i];
            if (proj.name === projectName) {
                return proj;
            }
        }
        return undefined;
    }
    getProjectByMainLocalFolderPath(localPath) {
        localPath = StorageUtilities_1.default.canonicalizePath(localPath);
        for (let i = 0; i < this.projects.length; i++) {
            const proj = this.projects[i];
            if (proj.localFolderPath && StorageUtilities_1.default.canonicalizePath(proj.localFolderPath) === localPath) {
                return proj;
            }
        }
        return undefined;
    }
    async ensureProjectFromLocalStoragePath(messageProjectPath) {
        if (!this.local) {
            Log_1.default.fail("Could not find local utilities.");
            return;
        }
        await this.load();
        let desiredProjectName = StorageUtilities_1.default.canonicalizePathAsFileName(messageProjectPath);
        // check to see if the expected project with the expected name exists, and use that if possible.
        const project = this.getProjectByName(desiredProjectName);
        const canonPath = StorageUtilities_1.default.canonicalizePath(messageProjectPath);
        if (project !== undefined) {
            await project.ensurePreferencesAndFolderLoadedFromFile();
            if (project.localFolderPath !== undefined) {
                if (canonPath === StorageUtilities_1.default.canonicalizePath(project.localFolderPath)) {
                    await project.ensureInflated();
                    await project.inferProjectItemsFromFiles();
                    return project;
                }
            }
        }
        // OK, a project doesn't exist, let's create one.
        let counter = 0;
        while (this.prefsProjectsFolder.fileExists(desiredProjectName + ".json") ||
            this.projectsStorage.rootFolder.folderExists(desiredProjectName)) {
            counter++;
            desiredProjectName = StorageUtilities_1.default.canonicalizePathAsFileName(messageProjectPath) + " " + counter;
        }
        const projectPrefs = await this.prefsProjectsFolder.createFile(desiredProjectName + ".json");
        const newProject = new Project_1.default(this, desiredProjectName, projectPrefs);
        newProject.localFolderPath = messageProjectPath;
        newProject.originalFullPath = messageProjectPath;
        const localStorage = await this.local.createStorage(messageProjectPath);
        if (localStorage === null) {
            Log_1.default.fail("Could not create local storage.");
            return;
        }
        newProject.setProjectFolder(localStorage.rootFolder);
        await newProject.ensureProjectFolder();
        await newProject.inferProjectItemsFromFiles();
        this.projects.push(newProject);
        return newProject;
    }
    async ensureProjectForLocalFolder(folderPath, newProjectName, openDirect) {
        await this.load();
        folderPath = StorageUtilities_1.default.convertFolderPlaceholders(folderPath);
        let desiredProjectName = "";
        if (newProjectName !== undefined) {
            desiredProjectName = newProjectName;
        }
        else {
            desiredProjectName = StorageUtilities_1.default.getLeafName(folderPath);
        }
        desiredProjectName = StorageUtilities_1.default.convertFolderPlaceholdersComplete(desiredProjectName);
        let canonPath = StorageUtilities_1.default.canonicalizePath(folderPath);
        let project = this.getProjectByMainLocalFolderPath(folderPath);
        // check to see if the expected project with the expected name exists, and use that if possible.
        if (!project) {
            project = this.getProjectByName(desiredProjectName);
        }
        if (project !== undefined && !openDirect) {
            await project.ensurePreferencesAndFolderLoadedFromFile();
            if (project.localFolderPath !== undefined) {
                if (canonPath === StorageUtilities_1.default.canonicalizePath(project.localFolderPath)) {
                    await project.ensureInflated();
                    await project.inferProjectItemsFromFiles();
                    return project;
                }
            }
        }
        else if (project && openDirect) {
            for (let i = 1; i < 99; i++) {
                desiredProjectName = StorageUtilities_1.default.getLeafName(folderPath) + " " + i;
                canonPath = StorageUtilities_1.default.canonicalizePath(folderPath) + " " + i;
                if (newProjectName !== undefined) {
                    desiredProjectName = newProjectName;
                }
                // check to see if the expected project with the expected name exists, and use that if possible.
                let project = this.getProjectByName(desiredProjectName);
                if (!project) {
                    break;
                }
            }
        }
        // now check all other projects to see if one exists.
        for (let i = 0; i < this.projects.length; i++) {
            project = this.projects[i];
            await project.ensurePreferencesAndFolderLoadedFromFile();
            if (project.localFolderPath !== undefined) {
                if (canonPath === StorageUtilities_1.default.canonicalizePath(project.localFolderPath)) {
                    await project.ensureInflated();
                    await project.inferProjectItemsFromFiles();
                    return project;
                }
            }
        }
        // OK, a project doesn't exist, let's create one.
        let counter = 0;
        while (this.prefsProjectsFolder.fileExists(desiredProjectName + ".json") ||
            this.projectsStorage.rootFolder.folderExists(desiredProjectName)) {
            counter++;
            desiredProjectName = StorageUtilities_1.default.getLeafName(folderPath) + " " + counter;
        }
        const projectPrefs = await this.prefsProjectsFolder.createFile(desiredProjectName + ".json");
        const newProject = new Project_1.default(this, desiredProjectName, projectPrefs, true);
        newProject.localFolderPath = folderPath;
        newProject.originalFullPath = folderPath;
        await newProject.ensureProjectFolder();
        await newProject.inferProjectItemsFromFiles();
        this.projects.push(newProject);
        return newProject;
    }
    async getExportZip() {
        const zs = new ZipStorage_1.default();
        const zipRoot = zs.rootFolder;
        await this.load();
        for (let i = 0; i < this.projects.length; i++) {
            const project = this.projects[i];
            await project.ensurePreferencesAndFolderLoadedFromFile();
            await project.ensureProjectFolder();
            if (project.projectFolder) {
                const projectTargetFolder = zipRoot.ensureFolder(project.name);
                await StorageUtilities_1.default.syncFolderTo(project.projectFolder, projectTargetFolder, true, true, false);
            }
        }
        await zipRoot.saveAll();
        return zs;
    }
    async load(force) {
        if (this._isLoaded && !force) {
            return;
        }
        const configFile = this.file;
        if (!configFile.isContentLoaded) {
            await configFile.loadContent(false);
        }
        if (configFile.content !== null && configFile.content !== undefined && typeof configFile.content === "string") {
            this.#data = JSON.parse(configFile.content);
        }
        const projectsFolder = this.prefsProjectsFolder;
        await projectsFolder.load();
        await this.projectsStorage.rootFolder.load();
        this.projects = [];
        for (const fileName in projectsFolder.files) {
            const projectName = StorageUtilities_1.default.getBaseFromName(fileName);
            const projectFile = projectsFolder.files[fileName];
            if (projectFile !== undefined) {
                const project = new Project_1.default(this, projectName, projectFile);
                this.projects.push(project);
            }
        }
        this.considerStartingMinecraft(); // explicitly not awaiting this since this might take a while and is not strictly necessary.
        this.initializeWorldSettings();
        this.ensureDefaultWorldName();
        this._onLoaded.dispatch(this, this);
        this._isLoaded = true;
    }
    async considerStartingMinecraft() {
        if (CartoApp_1.default.hostType === CartoApp_1.HostType.webPlusServices || CartoApp_1.default.hostType === CartoApp_1.HostType.web) {
            if (this.successfullyConnectedWebSocketToMinecraft &&
                (this.autoStartMinecraft || this.lastActiveMinecraftFlavor === ICartoData_1.MinecraftFlavor.remote)) {
                this.ensureRemoteMinecraft();
                if (this.remoteMinecraft &&
                    CartoApp_1.default.hostType === CartoApp_1.HostType.web &&
                    CartoApp_1.default.carto &&
                    CartoApp_1.default.carto.remoteServerUrl &&
                    CartoApp_1.default.carto.remoteServerUrl.length > 4) {
                    this.remoteMinecraft.initialize();
                    if (!this.activeMinecraft && this.lastActiveMinecraftFlavor === ICartoData_1.MinecraftFlavor.remote) {
                        this.activeMinecraft = this.remoteMinecraft;
                        this.lastActiveMinecraftFlavor = ICartoData_1.MinecraftFlavor.remote;
                    }
                }
                if (CartoApp_1.default.isAppServiceWeb) {
                    this.ensureGameMinecraft();
                    if (this.gameMinecraft) {
                        this.gameMinecraft.initialize();
                    }
                    if (!this.activeMinecraft && this.lastActiveMinecraftFlavor === ICartoData_1.MinecraftFlavor.minecraftGameProxy) {
                        this.activeMinecraft = this.gameMinecraft;
                        this.lastActiveMinecraftFlavor = ICartoData_1.MinecraftFlavor.minecraftGameProxy;
                    }
                }
            }
            if (this.successfullyStartedMinecraftServer &&
                this.lastActiveMinecraftFlavor === ICartoData_1.MinecraftFlavor.processHostedProxy &&
                this.autoStartMinecraft) {
                await this.connectToMinecraft();
            }
        }
    }
    async connectToMinecraft() {
        if (!this.lastActiveMinecraftFlavor) {
            return;
        }
        this.ensureMinecraft(this.lastActiveMinecraftFlavor);
        if (this.activeMinecraft) {
            const status = await this.activeMinecraft.updateStatus();
            if (status === CartoMinecraftState.disconnected ||
                status === CartoMinecraftState.error ||
                status === CartoMinecraftState.none ||
                status === CartoMinecraftState.stopped ||
                status === CartoMinecraftState.stopping ||
                status === CartoMinecraftState.newMinecraft) {
                await this.activeMinecraft.initialize();
            }
        }
    }
    canPrepareAndStartMinecraft() {
        if (!this.activeMinecraft) {
            return false;
        }
        if (this.activeMinecraftState !== CartoMinecraftState.initialized &&
            this.activeMinecraftState !== CartoMinecraftState.preparing &&
            this.activeMinecraftState !== CartoMinecraftState.prepared &&
            this.activeMinecraftState !== CartoMinecraftState.starting &&
            this.activeMinecraftState !== CartoMinecraftState.started) {
            return false;
        }
        return true;
    }
    async prepareAndStartToMinecraft(push) {
        if (!this.canPrepareAndStartMinecraft || !this.activeMinecraft) {
            return;
        }
        await this.activeMinecraft.prepareAndStart(push);
    }
    _bubbleMinecraftRefreshed(minecraft, newState) {
        if (minecraft !== this.activeMinecraft) {
            return;
        }
        this._onMinecraftRefreshed.dispatch(minecraft, newState);
    }
    _bubbleMinecraftStateChanged(minecraft, newState) {
        if (minecraft !== this.activeMinecraft) {
            return;
        }
        if (!this.successfullyConnectedWebSocketToMinecraft && newState === CartoMinecraftState.initialized) {
            this.successfullyConnectedWebSocketToMinecraft = true;
            this.save();
        }
        if (newState === CartoMinecraftState.prepared) {
            Log_1.default.message("Minecraft is ready.");
        }
        else if (newState === CartoMinecraftState.started) {
            // Log.message("Connected to Minecraft.");
        }
        else if (newState === CartoMinecraftState.stopping) {
            Log_1.default.message("Disconnecting from Minecraft.");
        }
        this._onMinecraftStateChanged.dispatch(minecraft, newState);
    }
    ensureRemoteMinecraft() {
        return this.remoteMinecraft;
    }
    ensureProcessHostedMinecraft() {
        return this.processHostedMinecraft;
    }
    ensureDeploymentStorageMinecraft() {
        if (this.deploymentStorageMinecraft === undefined) {
            this.deploymentStorageMinecraft = new DeploymentStorageMinecraft_1.default(this);
        }
        return this.deploymentStorageMinecraft;
    }
    ensureGameMinecraft() {
        return this.gameMinecraft;
    }
    ensureMinecraft(flavor) {
        if (flavor === ICartoData_1.MinecraftFlavor.none) {
            return undefined;
        }
        if (this.activeMinecraft && this.lastActiveMinecraftFlavor === flavor) {
            return this.activeMinecraft;
        }
        this.ensureRemoteMinecraft();
        if (flavor === ICartoData_1.MinecraftFlavor.processHostedProxy) {
            this.ensureProcessHostedMinecraft();
        }
        else if (flavor === ICartoData_1.MinecraftFlavor.deploymentStorage) {
            this.ensureDeploymentStorageMinecraft();
        }
        else if (flavor === ICartoData_1.MinecraftFlavor.minecraftGameProxy) {
            this.ensureGameMinecraft();
        }
        if (this.activeMinecraft) {
            this.activeMinecraft.onStateChanged.unsubscribe(this._bubbleMinecraftStateChanged);
            this.activeMinecraft.onRefreshed.unsubscribe(this._bubbleMinecraftRefreshed);
        }
        if (flavor === ICartoData_1.MinecraftFlavor.remote) {
            if (!this.remoteMinecraft) {
                Log_1.default.throwUnexpectedUndefined("EMR");
                return;
            }
            this.activeMinecraft = this.remoteMinecraft;
            if (this.lastActiveMinecraftFlavor !== ICartoData_1.MinecraftFlavor.remote) {
                this.lastActiveMinecraftFlavor = ICartoData_1.MinecraftFlavor.remote;
                this.save();
            }
            this._onMinecraftStateChanged.dispatch(this.activeMinecraft, CartoMinecraftState.newMinecraft);
            if (this.activeMinecraft === undefined) {
                Log_1.default.unexpectedUndefined("EMA");
                return undefined;
            }
            this.activeMinecraft.onStateChanged.subscribe(this._bubbleMinecraftStateChanged);
            this.activeMinecraft.onRefreshed.subscribe(this._bubbleMinecraftRefreshed);
        }
        else if (flavor === ICartoData_1.MinecraftFlavor.minecraftGameProxy) {
            if (!this.gameMinecraft) {
                Log_1.default.throwUnexpectedUndefined("EMG");
                return undefined;
            }
            this.activeMinecraft = this.gameMinecraft;
            if (this.lastActiveMinecraftFlavor !== ICartoData_1.MinecraftFlavor.minecraftGameProxy) {
                this.lastActiveMinecraftFlavor = ICartoData_1.MinecraftFlavor.minecraftGameProxy;
                this.save();
            }
            this._onMinecraftStateChanged.dispatch(this.activeMinecraft, CartoMinecraftState.newMinecraft);
            if (this.activeMinecraft === undefined) {
                Log_1.default.unexpectedUndefined("EMB");
                return undefined;
            }
            this.activeMinecraft.onStateChanged.subscribe(this._bubbleMinecraftStateChanged);
            this.activeMinecraft.onRefreshed.subscribe(this._bubbleMinecraftRefreshed);
        }
        else if (flavor === ICartoData_1.MinecraftFlavor.processHostedProxy) {
            if (!this.processHostedMinecraft) {
                Log_1.default.throwUnexpectedUndefined("EMC");
                return undefined;
            }
            this.activeMinecraft = this.processHostedMinecraft;
            if (this.lastActiveMinecraftFlavor !== ICartoData_1.MinecraftFlavor.processHostedProxy) {
                this.lastActiveMinecraftFlavor = ICartoData_1.MinecraftFlavor.processHostedProxy;
                this.save();
            }
            this._onMinecraftStateChanged.dispatch(this.activeMinecraft, CartoMinecraftState.newMinecraft);
            if (this.activeMinecraft === undefined) {
                Log_1.default.unexpectedUndefined("EMD");
                return undefined;
            }
            this.activeMinecraft.onStateChanged.subscribe(this._bubbleMinecraftStateChanged);
            this.activeMinecraft.onRefreshed.subscribe(this._bubbleMinecraftRefreshed);
        }
        else if (flavor === ICartoData_1.MinecraftFlavor.deploymentStorage) {
            if (!this.deploymentStorageMinecraft) {
                Log_1.default.throwUnexpectedUndefined("EMCD");
                return undefined;
            }
            this.activeMinecraft = this.deploymentStorageMinecraft;
            if (this.lastActiveMinecraftFlavor !== ICartoData_1.MinecraftFlavor.deploymentStorage) {
                this.lastActiveMinecraftFlavor = ICartoData_1.MinecraftFlavor.deploymentStorage;
                this.save();
            }
            this._onMinecraftStateChanged.dispatch(this.activeMinecraft, CartoMinecraftState.newMinecraft);
            if (this.activeMinecraft === undefined) {
                Log_1.default.unexpectedUndefined("EMDD");
                return undefined;
            }
            this.activeMinecraft.onStateChanged.subscribe(this._bubbleMinecraftStateChanged);
            this.activeMinecraft.onRefreshed.subscribe(this._bubbleMinecraftRefreshed);
        }
        else if (this.createMinecraft === undefined ||
            this.canCreateMinecraft === undefined ||
            !this.canCreateMinecraft(flavor)) {
            Log_1.default.debugAlert("Requested creation of an unavailable Minecraft.");
            flavor = ICartoData_1.MinecraftFlavor.none;
        }
        else {
            const newMinecraft = this.createMinecraft(flavor, this);
            if (!newMinecraft) {
                Log_1.default.debugAlert("Could not create a requested Minecraft.");
                flavor = ICartoData_1.MinecraftFlavor.none;
            }
            else {
                this.activeMinecraft = newMinecraft;
                this.lastActiveMinecraftFlavor = flavor;
                this._onMinecraftStateChanged.dispatch(this.activeMinecraft, CartoMinecraftState.newMinecraft);
                this.activeMinecraft.onStateChanged.subscribe(this._bubbleMinecraftStateChanged);
                this.activeMinecraft.onRefreshed.subscribe(this._bubbleMinecraftRefreshed);
            }
        }
        this.lastActiveMinecraftFlavor = flavor;
        this.save();
        return this.activeMinecraft;
    }
}
exports.default = Carto;

//# sourceMappingURL=../maps/app/Carto.js.map
