"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const BlockType_1 = require("./BlockType");
const BlockBaseType_1 = require("./BlockBaseType");
const MinecraftUtilities_1 = require("./MinecraftUtilities");
const Log_1 = require("./../core/Log");
const HttpStorage_1 = require("../storage/HttpStorage");
const CartoApp_1 = require("./../app/CartoApp");
const Utilities_1 = require("../core/Utilities");
const NpmModule_1 = require("../devproject/NpmModule");
const ContentIndex_1 = require("../core/ContentIndex");
const Project_1 = require("../app/Project");
const IProjectItemData_1 = require("../app/IProjectItemData");
const StorageUtilities_1 = require("../storage/StorageUtilities");
const ICartoData_1 = require("../app/ICartoData");
const ZipStorage_1 = require("../storage/ZipStorage");
class Database {
    static isLoaded = false;
    static isScriptTypesLoaded = false;
    static catalog = null;
    static loadedFormCount = 0;
    static _creatorToolsIngameProject = null;
    static uxCatalog = {};
    static formsFolders = {};
    static stable20TypeDefs = null;
    static libs = null;
    static stable10TypeDefs = null;
    static contentFolder = null;
    static snippetsFolder = null;
    static previewMetadataFolder = null;
    static releaseMetadataFolder = null;
    static previewVanillaFolder = null;
    static releaseVanillaFolder = null;
    static samplesFolder = null;
    static releaseVanillaBehaviorPackFolder = null;
    static releaseVanillaResourcePackFolder = null;
    static local = null;
    static vanillaInfoData = null;
    static vanillaContentIndex = null;
    static previewVanillaInfoData = null;
    static previewVanillaContentIndex = null;
    static samplesInfoData = null;
    static samplesContentIndex = null;
    static addonsDocs = null;
    static biomesMetadata = null;
    static blocksMetadata = null;
    static entitiesMetadata = null;
    static itemsMetadata = null;
    static latestVersion;
    static latestPreviewVersion;
    static _isLoadingSnippets = false;
    static _pendingLoadSnippetsRequests = [];
    static _isLoadingVanilla = false;
    static _pendingLoadVanillaRequests = [];
    static _isLoadingPreviewVanilla = false;
    static _pendingLoadPreviewVanillaRequests = [];
    static _isLoadingSamples = false;
    static _pendingLoadSamplesRequests = [];
    static dataPath = "res/latest/";
    static minecraftEduVersion = "1.21.0";
    static minecraftEduPreviewVersion = "1.21.0";
    static fallbackMinecraftVersion = "1.21.80"; // used if we fail to retrieve the latest version from the network
    static fallbackMinecraftPreviewVersion = "1.21.90.26"; // should be occasionally statically updated.
    static minecraftModuleNames = [
        "@minecraft/server-gametest",
        "@minecraft/server",
        "@minecraft/server-ui",
        "@minecraft/server-net",
        "@minecraft/server-admin",
        "@minecraft/server-editor",
    ];
    static maxMinecraftPatchVersions = {
        "1.19": "80",
        "1.20": "80",
        "1.21": "80",
    };
    static moduleDescriptors = {};
    static blockTypes = {};
    static schemaContents = {};
    static blockBaseTypes = {};
    static _blockTypesByLegacyId;
    static javaBlockTypeData = {};
    static _defaultBlockBaseType;
    static get defaultBlockBaseType() {
        if (Database._defaultBlockBaseType === undefined) {
            Database._defaultBlockBaseType = Database.ensureBlockBaseType("block");
        }
        return Database._defaultBlockBaseType;
    }
    static async ensureFormLoadedByPath(path) {
        const lastSlash = path.lastIndexOf("/");
        if (lastSlash >= 0) {
            return await Database.ensureFormLoaded(path.substring(0, lastSlash), path.substring(lastSlash + 1));
        }
        return undefined;
    }
    static async ensureFormLoaded(subFolder, name) {
        name = name.toLowerCase();
        const extendedName = (subFolder ? subFolder + "." : "") + name;
        const expectedPath = (subFolder ? subFolder + "/" : "") + name;
        if (Database.uxCatalog[extendedName] !== undefined) {
            return Database.uxCatalog[extendedName];
        }
        let path = "data/forms/";
        if (subFolder) {
            path += subFolder + "/";
        }
        if (Database.local) {
            const storage = await Database.local.createStorage(path);
            if (storage) {
                await storage.rootFolder.load();
                const file = storage.rootFolder.files[name + ".form.json"];
                if (file) {
                    await file.loadContent();
                    return StorageUtilities_1.default.getJsonObject(file);
                }
                Log_1.default.fail("Could not load file locally for '" + expectedPath + "'.");
                return undefined;
            }
        }
        else {
            path = CartoApp_1.default.contentRoot + path + name + ".form.json";
            try {
                const response = await axios_1.default.get(path);
                Database.uxCatalog[extendedName] = response.data;
                Database.loadedFormCount++;
                return response.data;
            }
            catch {
                Log_1.default.fail("Could not load UX file for '" + expectedPath + "/" + path + "'.");
            }
        }
        return undefined;
    }
    static async getFormsFolder(subFolder) {
        if (this.formsFolders[subFolder]) {
            return this.formsFolders[subFolder];
        }
        const folderPath = "data/forms/" + subFolder + "/";
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage(folderPath);
            if (storage) {
                await storage.rootFolder.load();
                this.formsFolders[subFolder] = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + folderPath);
            await storage.rootFolder.load();
            this.formsFolders[subFolder] = storage.rootFolder;
        }
        return this.formsFolders[subFolder];
    }
    static getFormByPath(path) {
        const lastSlash = path.lastIndexOf("/");
        if (lastSlash >= 0) {
            return Database.getForm(path.substring(0, lastSlash), path.substring(lastSlash + 1));
        }
        return undefined;
    }
    static getForm(subFolder, name) {
        name = name.toLowerCase();
        return Database.uxCatalog[(subFolder ? subFolder + "." : "") + name];
    }
    static getBlockType(name) {
        name = MinecraftUtilities_1.default.canonicalizeName(name);
        const blockType = Database.blockTypes[name];
        return blockType;
    }
    static populateBlockTypesByLegacyId() {
        this._blockTypesByLegacyId = [];
        for (const blockTypeName in this.blockTypes) {
            const blockType = this.blockTypes[blockTypeName];
            if (blockType.numericId !== undefined) {
                Log_1.default.assert(this._blockTypesByLegacyId[blockType.numericId] === undefined, "Multiple block types registered for the same ID:" + blockType.id);
                this._blockTypesByLegacyId[blockType.numericId] = blockType;
            }
        }
    }
    static async getModuleDescriptor(moduleId) {
        if (Database.moduleDescriptors[moduleId]) {
            return Database.moduleDescriptors[moduleId];
        }
        const url = "https://registry.npmjs.org/" + moduleId;
        try {
            const response = await axios_1.default.get(url);
            Database.moduleDescriptors[moduleId] = new NpmModule_1.default(response.data);
        }
        catch (e) {
            if (e && (!e.message || !e.message.indexOf || e.message.indexOf("etwork ") < 0)) {
                Log_1.default.debugAlert("Could not load registry for '" + moduleId + "': " + e.toString());
            }
            else {
                CartoApp_1.default.carto?.notifyStatusUpdate("Could not connect to network to retrieve latest script package details. Url: " + url);
            }
        }
        return Database.moduleDescriptors[moduleId];
    }
    static async getNextMinecraftPreviewVersion() {
        const latestMinecraftPreviewVersion = await Database.getLatestMinecraftPreviewVersion();
        if (!latestMinecraftPreviewVersion) {
            return undefined;
        }
        const ver = latestMinecraftPreviewVersion.split(".");
        if (!ver || !ver.length || ver.length < 3) {
            return undefined;
        }
        let triplet = undefined;
        try {
            triplet = [parseInt(ver[0]), parseInt(ver[1]), parseInt(ver[2]) + 10];
        }
        catch (e) {
            return undefined;
        }
        return triplet.join(".");
    }
    static async getLatestMinecraftPreviewVersion() {
        const moduleDescriptor = await this.getModuleDescriptor("@minecraft/server");
        if (!moduleDescriptor || !moduleDescriptor.latestPreviewVersion) {
            return "1.21.0";
        }
        return moduleDescriptor.latestPreviewVersion;
    }
    static async getLatestMinecraftRetailVersion() {
        const moduleDescriptor = await this.getModuleDescriptor("@minecraft/server");
        if (!moduleDescriptor || !moduleDescriptor.latestRetailVersion) {
            return "1.21.0";
        }
        return moduleDescriptor.latestRetailVersion;
    }
    static async isRecentVersionFromVersionArray(version) {
        if (version === undefined || version.length !== 3) {
            return false;
        }
        const ver = await this.getLatestVersionInfo(ICartoData_1.MinecraftTrack.main);
        if (!ver) {
            return false;
        }
        const verArr = MinecraftUtilities_1.default.getVersionArrayFrom(ver);
        if (!verArr || verArr.length < 3) {
            return false;
        }
        const majorVersion = verArr[0];
        const minorVersion = verArr[1];
        if (version[0] < majorVersion) {
            return false;
        }
        if (majorVersion === version[0] && minorVersion - version[1] > 1) {
            return false;
        }
        return true;
    }
    static async getContentFolderContent(fileRelativePath) {
        if (Database._creatorToolsIngameProject) {
            return Database._creatorToolsIngameProject;
        }
        await Database.loadContent();
        if (Database.contentFolder === null || !CartoApp_1.default.carto) {
            Log_1.default.unexpectedContentState();
            return undefined;
        }
        fileRelativePath = StorageUtilities_1.default.ensureStartsWithDelimiter(fileRelativePath);
        const file = await Database.contentFolder.ensureFileFromRelativePath(fileRelativePath);
        await file.loadContent();
        return file.content;
    }
    static async ensureCreatorToolsIngameProject() {
        if (Database._creatorToolsIngameProject) {
            return Database._creatorToolsIngameProject;
        }
        await Database.loadContent();
        if (Database.contentFolder === null || !CartoApp_1.default.carto) {
            Log_1.default.unexpectedContentState();
            return undefined;
        }
        const file = Database.contentFolder.ensureFile("creator_tools_ingame.mcaddon");
        await file.loadContent();
        if (file.content instanceof Uint8Array) {
            Database._creatorToolsIngameProject = new Project_1.default(CartoApp_1.default.carto, "Creator Tools", null);
            const projectFolder = await Database._creatorToolsIngameProject.ensureProjectFolder();
            const folder = projectFolder.ensureFolder("addons");
            const contentFile = folder.ensureFile(file.name);
            contentFile.setContent(file.content);
            contentFile.saveContent();
            const relPath = contentFile.getFolderRelativePath(Database._creatorToolsIngameProject.projectFolder);
            if (relPath !== undefined) {
                Database._creatorToolsIngameProject.ensureItemByProjectPath(relPath, IProjectItemData_1.ProjectItemStorageType.singleFile, file.name, IProjectItemData_1.ProjectItemType.MCAddon, Project_1.FolderContext.unknown, undefined, IProjectItemData_1.ProjectItemCreationType.normal);
                await Database._creatorToolsIngameProject.inferProjectItemsFromZipFile(relPath, contentFile, false);
            }
            return Database._creatorToolsIngameProject;
        }
        return undefined;
    }
    static async getLatestVersionInfo(track, force) {
        if (track === ICartoData_1.MinecraftTrack.edu) {
            return Database.minecraftEduVersion;
        }
        if (track === ICartoData_1.MinecraftTrack.eduPreview) {
            return Database.minecraftEduPreviewVersion;
        }
        if (track === ICartoData_1.MinecraftTrack.preview && Database.latestPreviewVersion && !force) {
            return Database.latestPreviewVersion;
        }
        if (track === ICartoData_1.MinecraftTrack.main && Database.latestVersion && !force) {
            return Database.latestVersion;
        }
        let minecraftInfoResponse = undefined;
        let versionUrl = "https://raw.githubusercontent.com/Mojang/bedrock-samples/main/version.json";
        if (track === ICartoData_1.MinecraftTrack.preview) {
            versionUrl = "https://raw.githubusercontent.com/Mojang/bedrock-samples/preview/version.json";
        }
        try {
            minecraftInfoResponse = await axios_1.default.get(versionUrl);
        }
        catch (e) {
            if (e && (!e.message || !e.message.indexOf || e.message.indexOf("etwork ") < 0)) {
                Log_1.default.debugAlert("Could not load version info from '" + versionUrl + "': " + e.toString());
            }
            else {
                CartoApp_1.default.carto?.notifyStatusUpdate("Could not connect to network to retrieve latest Minecraft version details. Url: " + versionUrl);
            }
            if (track === ICartoData_1.MinecraftTrack.preview) {
                Database.latestPreviewVersion = Database.fallbackMinecraftPreviewVersion;
                return Database.fallbackMinecraftPreviewVersion;
            }
            if (track === ICartoData_1.MinecraftTrack.main) {
                Database.latestVersion = Database.fallbackMinecraftVersion;
                return Database.fallbackMinecraftVersion;
            }
        }
        let latestVersionIndex = 0;
        let latestVerStr = "";
        try {
            if (minecraftInfoResponse === undefined || minecraftInfoResponse.data === undefined) {
                throw new Error("Unexpected empty response.");
            }
            const responseJson = JSON.parse(JSON.stringify(minecraftInfoResponse.data));
            if (!responseJson) {
                throw new Error("Improperly formatted response (source: " + minecraftInfoResponse.data + ")");
            }
            for (const verId in responseJson) {
                const ver = responseJson[verId].version;
                if (ver) {
                    const isPreview = Database.getVersionIsPreview(ver); // version ends with .20 or higher
                    const versionIndex = Database.getVersionIndexFromVersionStr(ver);
                    if (versionIndex > 0 &&
                        versionIndex > latestVersionIndex &&
                        ((isPreview && track === ICartoData_1.MinecraftTrack.preview) || (!isPreview && track === ICartoData_1.MinecraftTrack.main))) {
                        latestVersionIndex = versionIndex;
                        // re-constitute the version number ourselves
                        const verNums = ver.split(".");
                        latestVerStr =
                            verNums[0] + "." + verNums[1] + "." + verNums[2] + "." + Utilities_1.default.frontPadToLength(verNums[3], 2, "0");
                    }
                }
            }
        }
        catch (e) {
            Log_1.default.error("Could not process Minecraft version details." + e);
            throw new Error(e.toString());
        }
        if (latestVerStr && latestVerStr.length > 0) {
            if (track === ICartoData_1.MinecraftTrack.preview) {
                Database.latestPreviewVersion = latestVerStr;
            }
            else {
                Database.latestVersion = latestVerStr;
            }
        }
        return latestVerStr;
    }
    static getVersionIsPreview(ver) {
        const verNums = ver.split(".");
        if (verNums.length !== 4) {
            return false;
        }
        for (let j = 0; j < verNums.length; j++) {
            if (!Utilities_1.default.isNumeric(verNums[j])) {
                return false;
            }
        }
        return parseInt(verNums[3]) > 19;
    }
    static getVersionIndexFromVersionStr(ver) {
        const verNums = ver.split(".");
        if (verNums.length !== 4 && verNums.length !== 3) {
            return -1;
        }
        for (let j = 0; j < verNums.length; j++) {
            if (!Utilities_1.default.isNumeric(verNums[j])) {
                return -1;
            }
        }
        let versionIndex = parseInt(verNums[0]) * 16777216 + parseInt(verNums[1]) * 65536 + parseInt(verNums[2]) * 256;
        if (verNums.length === 4) {
            versionIndex += parseInt(verNums[3]);
        }
        return versionIndex;
    }
    static getBlockTypeByLegacyId(id) {
        if (!this._blockTypesByLegacyId) {
            this._blockTypesByLegacyId = [];
            this.populateBlockTypesByLegacyId();
        }
        const result = this._blockTypesByLegacyId[id];
        // Log.assert(result !== undefined);
        return result;
    }
    static getMatchingBlocks(searchTerm) {
        searchTerm = searchTerm.toLowerCase().trim();
        searchTerm = searchTerm.replace(/_/gi, " ");
        const searchTerms = searchTerm.split(" ");
        let exactMatch = undefined;
        let matches = [];
        for (const name in this.blockTypes) {
            const bt = this.blockTypes[name];
            const titleCanon = bt.title.toLowerCase();
            if (titleCanon === searchTerm && exactMatch === undefined) {
                exactMatch = bt;
            }
            else {
                for (let i = 0; i < searchTerms.length; i++) {
                    if (titleCanon.indexOf(searchTerms[i]) >= 0) {
                        matches.push(bt);
                        break;
                    }
                }
            }
        }
        if (exactMatch !== undefined) {
            const newMatches = [exactMatch];
            for (let i = 0; i < matches.length; i++) {
                newMatches.push(matches[i]);
            }
            matches = newMatches;
        }
        return matches;
    }
    static ensureBlockType(name) {
        name = MinecraftUtilities_1.default.canonicalizeName(name);
        let blockType = Database.blockTypes[name];
        if (blockType == null && Database.catalog != null) {
            blockType = new BlockType_1.default(name);
            Database.blockTypes[name] = blockType;
            blockType.data = {
                name: name,
            };
        }
        return blockType;
    }
    static ensureBlockBaseType(name) {
        name = MinecraftUtilities_1.default.canonicalizeName(name);
        let blockBaseType = Database.blockBaseTypes[name];
        if (blockBaseType == null) {
            blockBaseType = new BlockBaseType_1.default(name);
            Database.blockBaseTypes[name] = blockBaseType;
        }
        return blockBaseType;
    }
    static async loadContent() {
        if (Database.contentFolder !== null) {
            return;
        }
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage("data/content/");
            if (storage) {
                await storage.rootFolder.load();
                Database.contentFolder = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "data/content/");
            await storage.rootFolder.load();
            Database.contentFolder = storage.rootFolder;
        }
    }
    static async initSnippetsFolder() {
        if (Database.snippetsFolder !== null) {
            return;
        }
        if (this._isLoadingSnippets) {
            const pendingLoad = this._pendingLoadSnippetsRequests;
            const prom = (resolve, reject) => {
                pendingLoad.push(resolve);
            };
            await new Promise(prom);
            return;
        }
        else {
            this._isLoadingSnippets = true;
            let folder;
            if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
                const storage = await Database.local.createStorage("data/snippets/");
                if (storage) {
                    folder = storage.rootFolder;
                }
            }
            else {
                const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "data/snippets/");
                folder = storage.rootFolder;
            }
            if (folder === undefined) {
                return;
            }
            await folder.load();
            for (const fileName in folder.files) {
                const file = folder.files[fileName];
                if (file) {
                    await file.loadContent();
                }
            }
            Database.snippetsFolder = folder;
            this._isLoadingSnippets = false;
            const pendingLoad = this._pendingLoadSnippetsRequests;
            this._pendingLoadSnippetsRequests = [];
            for (const prom of pendingLoad) {
                prom(undefined);
            }
        }
    }
    static itemMatchesSearch(item, searchString) {
        if (!searchString || searchString.length < 3) {
            return true;
        }
        const searchKey = searchString.toLowerCase();
        if ((item.title && item.title.toLowerCase().indexOf(searchKey) >= 0) ||
            (item.description && item.description.toLowerCase().indexOf(searchKey) >= 0)) {
            return true;
        }
        if (item.topics) {
            for (const topic of item.topics) {
                if (topic.toLowerCase().indexOf(searchKey) >= 0) {
                    return true;
                }
            }
        }
        return false;
    }
    static async getSnippet(sampleSet, snippetId) {
        if (!Database.snippetsFolder) {
            await Database.initSnippetsFolder();
        }
        if (!Database.snippetsFolder) {
            return undefined;
        }
        if (Database.snippetsFolder !== null && Database.snippetsFolder.files) {
            const file = Database.snippetsFolder.files[sampleSet + ".json"];
            if (file) {
                const snipSet = StorageUtilities_1.default.getJsonObject(file);
                if (snipSet && snipSet[snippetId]) {
                    return snipSet[snippetId];
                }
            }
        }
        return undefined;
    }
    static async getAddonsDocs() {
        if (!Database.addonsDocs) {
            Database.addonsDocs = await Database.getMetadataObject("/doc_modules/addons.json");
        }
        return Database.addonsDocs;
    }
    static async getBiomesMetadata() {
        if (!Database.biomesMetadata) {
            Database.biomesMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-biomes.json");
        }
        return Database.biomesMetadata;
    }
    static async getBlocksMetadata() {
        if (!Database.blocksMetadata) {
            Database.blocksMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-blocks.json");
        }
        return Database.blocksMetadata;
    }
    static async getEntitiesMetadata() {
        if (!Database.entitiesMetadata) {
            Database.entitiesMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-entities.json");
        }
        return Database.entitiesMetadata;
    }
    static async getItemsMetadata() {
        if (!Database.itemsMetadata) {
            Database.itemsMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-items.json");
        }
        return Database.itemsMetadata;
    }
    static async getMetadataObject(metaPath) {
        await Database.loadPreviewMetadataFolder();
        if (!Database.previewMetadataFolder) {
            return null;
        }
        const jsonFile = await Database.previewMetadataFolder.getFileFromRelativePath(metaPath);
        if (!jsonFile) {
            Log_1.default.unexpectedUndefined();
            return null;
        }
        await jsonFile.loadContent();
        const jsonObj = StorageUtilities_1.default.getJsonObject(jsonFile);
        if (!jsonObj) {
            Log_1.default.unexpectedUndefined();
            return null;
        }
        return jsonObj;
    }
    static async getPreviewVanillaFile(filePath) {
        const vanillaFolder = await Database.getPreviewVanillaFolder();
        if (!vanillaFolder) {
            return null;
        }
        const jsonFile = await vanillaFolder.getFileFromRelativePath(filePath);
        if (!jsonFile) {
            Log_1.default.unexpectedUndefined();
            return null;
        }
        await jsonFile.loadContent();
        return jsonFile;
    }
    static async getPreviewVanillaObject(filePath) {
        const jsonFile = await Database.getPreviewVanillaFile(filePath);
        if (!jsonFile) {
            Log_1.default.unexpectedUndefined();
            return null;
        }
        const jsonObj = StorageUtilities_1.default.getJsonObject(jsonFile);
        if (!jsonObj) {
            Log_1.default.unexpectedUndefined();
            return null;
        }
        return jsonObj;
    }
    static async loadPreviewMetadataFolder() {
        if (!this.previewMetadataFolder) {
            if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
                const storage = await Database.local.createStorage("res/latest/van/preview/metadata/");
                if (storage) {
                    this.previewMetadataFolder = storage.rootFolder;
                }
            }
            else {
                const metadataStorage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/latest/van/preview/metadata/");
                await metadataStorage.rootFolder.load();
                this.previewMetadataFolder = metadataStorage.rootFolder;
            }
        }
        return this.previewMetadataFolder;
    }
    static async loadReleaseMetadataFolder() {
        if (!this.releaseMetadataFolder) {
            if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
                const storage = await Database.local.createStorage("res/latest/van/release/metadata/");
                if (storage) {
                    this.releaseMetadataFolder = storage.rootFolder;
                }
            }
            else {
                const metadataStorage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/latest/van/release/metadata/");
                await metadataStorage.rootFolder.load();
                this.releaseMetadataFolder = metadataStorage.rootFolder;
            }
        }
        return this.releaseMetadataFolder;
    }
    static async getReleaseVanillaFolder() {
        if (Database.releaseVanillaFolder !== null) {
            return Database.releaseVanillaFolder;
        }
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage("res/latest/van/release/");
            if (storage) {
                Database.releaseVanillaFolder = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/latest/van/release/");
            Database.releaseVanillaFolder = storage.rootFolder;
        }
        if (Database.releaseVanillaFolder) {
            await Database.releaseVanillaFolder.load();
        }
        return Database.releaseVanillaFolder;
    }
    static async getPreviewVanillaFolder() {
        if (Database.previewVanillaFolder !== null) {
            return Database.previewVanillaFolder;
        }
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage("res/latest/van/preview/");
            if (storage) {
                Database.previewVanillaFolder = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/latest/van/preview/");
            Database.previewVanillaFolder = storage.rootFolder;
        }
        if (Database.previewVanillaFolder) {
            await Database.previewVanillaFolder.load();
        }
        return Database.previewVanillaFolder;
    }
    static async getSamplesFolder() {
        if (Database.samplesFolder !== null) {
            return Database.samplesFolder;
        }
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage("res/samples/microsoft/minecraft-samples-main/");
            if (storage) {
                Database.samplesFolder = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/samples/microsoft/minecraft-samples-main/");
            Database.samplesFolder = storage.rootFolder;
        }
        if (Database.samplesFolder) {
            await Database.samplesFolder.load();
        }
        return Database.samplesFolder;
    }
    static async getReleaseVanillaBehaviorPackFolder() {
        if (Database.releaseVanillaBehaviorPackFolder !== null) {
            return Database.releaseVanillaBehaviorPackFolder;
        }
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage("res/latest/van/release/behavior_pack/");
            if (storage) {
                Database.releaseVanillaBehaviorPackFolder = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/latest/van/release/behavior_pack/");
            Database.releaseVanillaBehaviorPackFolder = storage.rootFolder;
        }
        if (Database.releaseVanillaBehaviorPackFolder) {
            await Database.releaseVanillaBehaviorPackFolder.load();
        }
        return Database.releaseVanillaBehaviorPackFolder;
    }
    static async getReleaseVanillaResourcePackFolder() {
        if (Database.releaseVanillaResourcePackFolder !== null) {
            return Database.releaseVanillaResourcePackFolder;
        }
        if (Database.local && (CartoApp_1.default.fullLocalStorage || !CartoApp_1.default.contentRoot)) {
            const storage = await Database.local.createStorage("res/latest/van/release/resource_pack/");
            if (storage) {
                Database.releaseVanillaResourcePackFolder = storage.rootFolder;
            }
        }
        else {
            const storage = new HttpStorage_1.default(CartoApp_1.default.contentRoot + "res/latest/van/release/resource_pack/");
            Database.releaseVanillaResourcePackFolder = storage.rootFolder;
        }
        if (Database.releaseVanillaResourcePackFolder) {
            await Database.releaseVanillaResourcePackFolder.load();
        }
        return Database.releaseVanillaResourcePackFolder;
    }
    static async loadUx() { }
    static getComponentFormField(propertyName) {
        if (Database.uxCatalog === undefined || Database.uxCatalog === null) {
            Log_1.default.unexpectedNull();
            return undefined;
        }
        throw new Error("Not implemented.");
        /*
        const componentForms = Database.uxCatalog.forms;
    
        for (const formName in componentForms) {
          const form = componentForms[formName];
    
          if (form === undefined) {
            break;
          }
    
          for (let i = 0; i < form.fields.length; i++) {
            const field = form.fields[i];
    
            if (field.id === propertyName || field.altId === propertyName) {
              return {
                form: form,
                field: field,
              };
            }
          }
        }
    */
    }
    static async getSchema(path) {
        path = path.toLowerCase();
        if (Database.schemaContents[path]) {
            return Database.schemaContents[path];
        }
        const schemaPath = "/" + Database.dataPath + "schemas/" + path;
        try {
            // @ts-ignore
            if (typeof window !== "undefined") {
                const response = await axios_1.default.get(Utilities_1.default.ensureEndsWithSlash(CartoApp_1.default.contentRoot) + Utilities_1.default.ensureNotStartsWithSlash(schemaPath));
                Database.schemaContents[path] = response.data;
                if (!Database.schemaContents[path]) {
                    Log_1.default.fail("Could not load schema '" + schemaPath + "'");
                }
                return Database.schemaContents[path];
            }
            else if (Database.local) {
                const result = await Database.local.readJsonFile(schemaPath);
                if (result !== null) {
                    Database.schemaContents[path] = result;
                    return Database.schemaContents[path];
                }
                else {
                    Log_1.default.fail("Could not load schema '" + schemaPath + "'");
                    return undefined;
                }
            }
            else {
                Log_1.default.fail("Unexpected database config (no database available) when trying to load schema for:" + path);
                return undefined;
            }
        }
        catch (e) {
            Log_1.default.fail("Could not load Minecraft schema catalog: " + CartoApp_1.default.contentRoot + " - " + schemaPath + " " + e.toString());
            return undefined;
        }
    }
    static async loadStable20ScriptTypes() {
        if (Database.stable20TypeDefs) {
            return;
        }
        try {
            // @ts-ignore
            if (typeof window !== "undefined") {
                const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/typedefs.stable20.json");
                Database.stable20TypeDefs = response.data;
            }
            else if (Database.local) {
                const result = await Database.local.readJsonFile("data/typedefs.stable20.json");
                if (result !== null) {
                    Database.stable20TypeDefs = result;
                }
            }
        }
        catch {
            Log_1.default.fail("Could not load stable 2.0 Minecraft types catalog.");
        }
    }
    static async loadStable10ScriptTypes() {
        if (Database.stable10TypeDefs) {
            return;
        }
        try {
            // @ts-ignore
            if (typeof window !== "undefined") {
                const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/typedefs.stable10.json");
                Database.stable10TypeDefs = response.data;
            }
            else if (Database.local) {
                const result = await Database.local.readJsonFile("data/typedefs.stable10.json");
                if (result !== null) {
                    Database.stable10TypeDefs = result;
                }
            }
        }
        catch {
            Log_1.default.fail("Could not load stable 1.0 Minecraft types catalog.");
        }
    }
    static async getLibs() {
        if (Database.libs) {
            return Database.libs;
        }
        try {
            // @ts-ignore
            if (typeof window !== "undefined") {
                const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/libs.json");
                Database.libs = response.data;
            }
            else if (Database.local) {
                const result = await Database.local.readJsonFile("data/libs.json");
                if (result !== null) {
                    Database.libs = result;
                }
            }
        }
        catch {
            Log_1.default.fail("Could not load libraries catalog.");
        }
        return Database.libs;
    }
    static async matchesVanillaPath(path) {
        const rpFolder = await Database.getReleaseVanillaResourcePackFolder();
        if (rpFolder && rpFolder.folderCount > 0) {
            path = Utilities_1.default.ensureStartsWithSlash(path);
            const folder = await rpFolder.getFolderFromRelativePath(StorageUtilities_1.default.getFolderPath(path));
            if (!folder) {
                return false;
            }
            const exists = await folder.exists();
            if (!exists) {
                return false;
            }
            const itemName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(path)).toLowerCase();
            await folder.load();
            for (let fileName in folder.files) {
                if (fileName && StorageUtilities_1.default.getBaseFromName(fileName).toLowerCase() === itemName) {
                    return true;
                }
            }
        }
        else {
            let res = await Database.matchesVanillaPathFromIndex(path);
            return res;
        }
        return false;
    }
    static async matchesVanillaPathFromIndex(path) {
        if (!Database.vanillaContentIndex) {
            await this.loadVanillaInfoData();
        }
        if (!Database.vanillaContentIndex) {
            return false;
        }
        let result = Database.vanillaContentIndex.hasPathMatches(path);
        if (!result) {
            result = await Database.isVanillaToken(path);
        }
        if (!result) {
            path = Utilities_1.default.ensureStartsWithSlash(path);
            if (!path.startsWith("/resource_pack/")) {
                result = Database.vanillaContentIndex.hasPathMatches("/resource_pack" + path);
                if (!result) {
                    result = await Database.isVanillaToken("/resource_pack" + path);
                    if (!result) {
                        if (!path.startsWith("/behavior_pack/")) {
                            result = Database.vanillaContentIndex.hasPathMatches("/behavior_pack" + path);
                            if (!result) {
                                result = await Database.isVanillaToken("/behavior_pack" + path);
                            }
                        }
                    }
                }
            }
        }
        return result;
    }
    static async isVanillaToken(path) {
        const matches = await Database.getVanillaMatches(path, true);
        if (matches && matches.length > 0) {
            return true;
        }
        return false;
    }
    static async getVanillaAll(withAnnotation) {
        if (!Database.vanillaContentIndex) {
            await this.loadVanillaInfoData();
        }
        if (!Database.vanillaContentIndex) {
            return undefined;
        }
        return Database.vanillaContentIndex.getAll(withAnnotation);
    }
    static async getVanillaMatches(path, wholeTermSearch, withAnnotation) {
        if (!Database.vanillaContentIndex) {
            await this.loadVanillaInfoData();
        }
        if (!Database.vanillaContentIndex) {
            return undefined;
        }
        return await Database.vanillaContentIndex.getMatches(path, wholeTermSearch, withAnnotation);
    }
    static async getPreviewVanillaMatches(path, wholeTermSearch, withAnnotation) {
        if (!Database.previewVanillaContentIndex) {
            await this.loadPreviewVanillaInfoData();
        }
        if (!Database.previewVanillaContentIndex) {
            return undefined;
        }
        return await Database.previewVanillaContentIndex.getMatches(path, wholeTermSearch, withAnnotation);
    }
    static async getSamplesMatches(path, wholeTermSearch, withAnnotation) {
        if (!Database.samplesContentIndex) {
            await this.loadSampleInfoData();
        }
        if (!Database.samplesContentIndex) {
            return undefined;
        }
        return await Database.samplesContentIndex.getMatches(path, wholeTermSearch, withAnnotation);
    }
    static async loadVanillaInfoData() {
        if (Database.vanillaInfoData) {
            return;
        }
        if (this._isLoadingVanilla) {
            const pendingLoad = this._pendingLoadVanillaRequests;
            const prom = (resolve, reject) => {
                pendingLoad.push(resolve);
            };
            await new Promise(prom);
        }
        else {
            this._isLoadingVanilla = true;
            try {
                // @ts-ignore
                if (typeof window !== "undefined") {
                    const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/mci/release.mci.json.zip", {
                        responseType: "arraybuffer",
                        headers: {
                            Accept: "application/octet-stream, application/json, text/plain, */*",
                        },
                    });
                    if (response) {
                        Database.vanillaInfoData = await ZipStorage_1.default.fromZipBytesToJsonObject(response.data);
                    }
                }
                else if (Database.local) {
                    const result = await Database.local.readJsonFile("data/mci/release.mci.json");
                    if (result !== null) {
                        Database.vanillaInfoData = result;
                    }
                }
                if (Database.vanillaInfoData && Database.vanillaInfoData.index && !Database.vanillaContentIndex) {
                    Database.vanillaContentIndex = new ContentIndex_1.default();
                    Database.vanillaContentIndex.loadFromData(Database.vanillaInfoData.index);
                }
            }
            catch {
                // Log.fail("Could not load vanilla metadata.");
            }
            this._isLoadingVanilla = false;
            const pendingLoad = this._pendingLoadVanillaRequests;
            this._pendingLoadVanillaRequests = [];
            for (const prom of pendingLoad) {
                prom(undefined);
            }
        }
    }
    static async loadPreviewVanillaInfoData() {
        if (Database.previewVanillaContentIndex) {
            return;
        }
        if (this._isLoadingPreviewVanilla) {
            const pendingLoad = this._pendingLoadPreviewVanillaRequests;
            const prom = (resolve, reject) => {
                pendingLoad.push(resolve);
            };
            await new Promise(prom);
        }
        else {
            this._isLoadingPreviewVanilla = true;
            try {
                // @ts-ignore
                if (typeof window !== "undefined") {
                    const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/mci/preview.mci.json.zip", {
                        responseType: "arraybuffer",
                        headers: {
                            Accept: "application/octet-stream, application/json, text/plain, */*",
                        },
                    });
                    if (response) {
                        Database.previewVanillaInfoData = await ZipStorage_1.default.fromZipBytesToJsonObject(response.data);
                    }
                }
                else if (Database.local) {
                    const result = await Database.local.readJsonFile("data/mci/preview.mci.json");
                    if (result !== null) {
                        Database.previewVanillaInfoData = result;
                    }
                }
                if (Database.previewVanillaInfoData &&
                    Database.previewVanillaInfoData.index &&
                    !Database.previewVanillaContentIndex) {
                    Database.previewVanillaContentIndex = new ContentIndex_1.default();
                    Database.previewVanillaContentIndex.loadFromData(Database.previewVanillaInfoData.index);
                }
            }
            catch {
                // Log.fail("Could not load preview vanilla metadata.");
            }
            this._isLoadingVanilla = false;
            const pendingLoad = this._pendingLoadPreviewVanillaRequests;
            this._pendingLoadPreviewVanillaRequests = [];
            for (const prom of pendingLoad) {
                prom(undefined);
            }
        }
    }
    static async loadSampleInfoData() {
        if (Database.samplesContentIndex) {
            return;
        }
        if (this._isLoadingSamples) {
            const pendingLoad = this._pendingLoadSamplesRequests;
            const prom = (resolve, reject) => {
                pendingLoad.push(resolve);
            };
            await new Promise(prom);
        }
        else {
            this._isLoadingSamples = true;
            try {
                // @ts-ignore
                if (typeof window !== "undefined") {
                    const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/mci/minecraft-samples-main.mci.json");
                    Database.samplesInfoData = response.data;
                }
                else if (Database.local) {
                    const result = await Database.local.readJsonFile("data/mci/minecraft-samples-main.mci.json");
                    if (result !== null) {
                        Database.samplesInfoData = result;
                    }
                }
                if (Database.samplesInfoData && Database.samplesInfoData.index && !Database.samplesContentIndex) {
                    Database.samplesContentIndex = new ContentIndex_1.default();
                    Database.samplesContentIndex.loadFromData(Database.samplesInfoData.index);
                }
            }
            catch {
                // Log.fail("Could not load preview vanilla metadata.");
            }
            this._isLoadingSamples = false;
            const pendingLoad = this._pendingLoadSamplesRequests;
            this._pendingLoadSamplesRequests = [];
            for (const prom of pendingLoad) {
                prom(undefined);
            }
        }
    }
    static async load() {
        if (Database.isLoaded) {
            return;
        }
        try {
            // @ts-ignore
            if (typeof window !== "undefined") {
                const response = await axios_1.default.get(CartoApp_1.default.contentRoot + "data/mccat.json");
                Database.catalog = response.data;
            }
            else if (Database.local) {
                const result = await Database.local.readJsonFile("data/mccat.json");
                if (result !== null) {
                    Database.catalog = result;
                    // Log.debugAlert("Loaded catalog: " + Database.catalog.blockBaseTypes.length);
                }
            }
            if (Database.catalog !== null) {
                for (let i = 0; i < Database.catalog.blockBaseTypes.length; i++) {
                    const blockBaseTypeData = Database.catalog.blockBaseTypes[i];
                    const baseTypeName = MinecraftUtilities_1.default.canonicalizeName(blockBaseTypeData.name);
                    const blockBaseType = new BlockBaseType_1.default(baseTypeName);
                    blockBaseType.data = blockBaseTypeData;
                    if (blockBaseTypeData.abstract === undefined || blockBaseTypeData.abstract === false) {
                        const newBlockType = {
                            name: blockBaseTypeData.name,
                            id: blockBaseTypeData.id,
                            icon: blockBaseTypeData.icon,
                            mapColor: blockBaseTypeData.mapColor,
                            shortId: blockBaseTypeData.shortId,
                            altShortId: blockBaseTypeData.altShortId,
                        };
                        const blockType = this.ensureBlockType(baseTypeName);
                        blockType.data = newBlockType;
                        blockType.baseType = blockBaseType;
                    }
                    if (blockBaseTypeData.variants !== undefined) {
                        for (let j = 0; j < blockBaseTypeData.variants.length; j++) {
                            const variantBlockTypeData = blockBaseTypeData.variants[j];
                            const blockType = this.ensureBlockType(variantBlockTypeData.name);
                            if (!variantBlockTypeData.id && blockBaseTypeData.id) {
                                variantBlockTypeData.id = blockBaseTypeData.id;
                            }
                            if (!variantBlockTypeData.icon && blockBaseTypeData.icon) {
                                variantBlockTypeData.icon = blockBaseTypeData.icon;
                            }
                            if (!variantBlockTypeData.shortId && blockBaseTypeData.shortId) {
                                variantBlockTypeData.shortId = blockBaseTypeData.shortId;
                            }
                            if (!variantBlockTypeData.altShortId && blockBaseTypeData.altShortId) {
                                variantBlockTypeData.altShortId = blockBaseTypeData.altShortId;
                            }
                            if (!variantBlockTypeData.mapColor && blockBaseTypeData.mapColor) {
                                variantBlockTypeData.mapColor = blockBaseTypeData.mapColor;
                            }
                            blockType.data = variantBlockTypeData;
                            blockType.baseType = blockBaseType;
                        }
                    }
                }
            }
        }
        catch {
            Log_1.default.fail("Could not load Minecraft types catalog.");
        }
        /*
        try {
          // @ts-ignore
          if (typeof window !== "undefined") {
            const response = await axios.get(CartoApp.contentRoot + "data/javacat.json");
    
            Database.javaBlockTypeData = response.data;
          } else if (Database.local) {
            const result = await Database.local.readJsonFile("data/javacat.json");
            if (result !== null) {
              Database.javaBlockTypeData = result as { [id: string]: IJavaBlockTypeData };
            }
          }
    
          if (Database.javaBlockTypeData) {
            for (const blockName in Database.javaBlockTypeData) {
              const jtnBlock = Database.javaBlockTypeData[blockName];
              jtnBlock.source = blockName;
    
              this.ensureBlockType(blockName).javaData = jtnBlock;
            }
          }
    
          Database.isLoaded = true;
        } catch {
          Log.fail("Could not load java catalog.");
        }*/
    }
}
exports.default = Database;

//# sourceMappingURL=../maps/minecraft/Database.js.map
