"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorldDataInfoGeneratorTest = void 0;
const ProjectInfoItem_1 = require("./ProjectInfoItem");
const IProjectItemData_1 = require("../app/IProjectItemData");
const MCWorld_1 = require("../minecraft/MCWorld");
const Log_1 = require("../core/Log");
const IInfoItemData_1 = require("./IInfoItemData");
const CommandBlockActor_1 = require("../minecraft/blockActors/CommandBlockActor");
const Status_1 = require("../app/Status");
const CommandStructure_1 = require("../app/CommandStructure");
const CommandRegistry_1 = require("../app/CommandRegistry");
const Dialogue_1 = require("../minecraft/Dialogue");
const ContentIndex_1 = require("../core/ContentIndex");
const NbtBinaryTag_1 = require("../minecraft/NbtBinaryTag");
const AnimationControllerBehaviorDefinition_1 = require("../minecraft/AnimationControllerBehaviorDefinition");
const AnimationBehaviorDefinition_1 = require("../minecraft/AnimationBehaviorDefinition");
const ProjectInfoUtilities_1 = require("./ProjectInfoUtilities");
const WorldLevelDat_1 = require("../minecraft/WorldLevelDat");
var WorldDataInfoGeneratorTest;
(function (WorldDataInfoGeneratorTest) {
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["unexpectedCommandInMCFunction"] = 101] = "unexpectedCommandInMCFunction";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["unexpectedCommandInCommandBlock"] = 102] = "unexpectedCommandInCommandBlock";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["minX"] = 103] = "minX";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["minZ"] = 104] = "minZ";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["maxX"] = 105] = "maxX";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["maxZ"] = 106] = "maxZ";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["containsWorldImpactingCommand"] = 112] = "containsWorldImpactingCommand";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["blocks"] = 121] = "blocks";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["blockData"] = 122] = "blockData";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["command"] = 123] = "command";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["executeSubCommand"] = 124] = "executeSubCommand";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["levelDat"] = 125] = "levelDat";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["levelDatExperiments"] = 126] = "levelDatExperiments";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["subchunklessChunks"] = 127] = "subchunklessChunks";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["chunks"] = 128] = "chunks";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["commandIsFromOlderMinecraftVersion"] = 212] = "commandIsFromOlderMinecraftVersion";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["errorProcessingWorld"] = 400] = "errorProcessingWorld";
    WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["unexpectedError"] = 401] = "unexpectedError";
})(WorldDataInfoGeneratorTest = exports.WorldDataInfoGeneratorTest || (exports.WorldDataInfoGeneratorTest = {}));
class WorldDataInfoGenerator {
    id = "WORLDDATA";
    title = "World Data Validation";
    modernCommandVersion = 33; // corresponds to 1.20.0 versions of Minecraft.
    performAddOnValidations = false;
    performPlatformVersionValidations = false;
    getTopicData(topicId) {
        return {
            title: ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, topicId),
        };
    }
    summarize(info, infoSet) {
        info.chunkCount = infoSet.getSummedDataValue("WORLDDATA", WorldDataInfoGeneratorTest.chunks);
        info.subchunkLessChunkCount = infoSet.getSummedDataValue("WORLDDATA", WorldDataInfoGeneratorTest.subchunklessChunks);
        info.worldLoadErrors = infoSet.getCount("WORLDDATA", WorldDataInfoGeneratorTest.errorProcessingWorld);
        const levelItems = infoSet.getItems(this.id, WorldDataInfoGeneratorTest.levelDat);
        const capabilitiesSet = new Set(info.capabilities);
        for (const levelItem of levelItems) {
            if (levelItem && levelItem.featureSets) {
                const gameType = levelItem.featureSets.GameType;
                if (gameType !== undefined) {
                    if (gameType[WorldLevelDat_1.GameType.adventure] !== undefined && gameType[WorldLevelDat_1.GameType.adventure] > 0) {
                        if (!capabilitiesSet.has("adventure")) {
                            info.capabilities.push("adventure");
                            capabilitiesSet.add("adventure");
                        }
                    }
                    if (gameType[WorldLevelDat_1.GameType.survival] !== undefined && gameType[WorldLevelDat_1.GameType.survival] > 0) {
                        if (!capabilitiesSet.has("survival")) {
                            info.capabilities.push("survival");
                            capabilitiesSet.add("survival");
                        }
                    }
                    if (gameType[WorldLevelDat_1.GameType.creative] !== undefined && gameType[WorldLevelDat_1.GameType.creative] > 0) {
                        if (!capabilitiesSet.has("creative")) {
                            info.capabilities.push("creative");
                            capabilitiesSet.add("creative");
                        }
                    }
                }
            }
        }
        info.commands = [];
        const commandsSet = new Set();
        const commandItems = infoSet.getItems(this.id, WorldDataInfoGeneratorTest.command);
        const subCommandItems = infoSet.getItems(this.id, WorldDataInfoGeneratorTest.executeSubCommand);
        for (const commandItem of commandItems) {
            let commandNames = commandItem.featureSets;
            if (commandNames) {
                for (const commandName in commandNames) {
                    if (!commandsSet.has(commandName)) {
                        info.commands.push(commandName);
                        commandsSet.add(commandName);
                    }
                }
            }
        }
        for (const commandItem of subCommandItems) {
            let commandNames = commandItem.featureSets;
            if (commandNames) {
                for (const commandName in commandNames) {
                    if (!commandsSet.has(commandName)) {
                        info.commands.push(commandName);
                        commandsSet.add(commandName);
                    }
                }
            }
        }
        info.commands.sort();
    }
    processListOfCommands(commandList, items, projectItem, commandsPi, subCommandsPi, checkForSlash) {
        for (let i = 0; i < commandList.length; i++) {
            if (commandList[i].trim().length > 2 && (!checkForSlash || commandList[i].startsWith("/"))) {
                const command = CommandStructure_1.default.parse(commandList[i]);
                if (CommandRegistry_1.default.isMinecraftBuiltInCommand(command.name)) {
                    if (this.performAddOnValidations && CommandRegistry_1.default.isAddOnBlockedCommand(command.name)) {
                        items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.warning, this.id, WorldDataInfoGeneratorTest.containsWorldImpactingCommand, "Contains command '" +
                            command.name +
                            "' which is impacts the state of the entire world, and generally shouldn't be used in an add-on", projectItem, command.name, undefined, commandList[i]));
                    }
                    commandsPi.incrementFeature(command.name);
                    if (command.name === "execute") {
                        let foundRun = false;
                        for (const arg of command.commandArguments) {
                            if (arg === "run") {
                                foundRun = true;
                            }
                            else if (foundRun && CommandRegistry_1.default.isMinecraftBuiltInCommand(arg)) {
                                subCommandsPi.incrementFeature(arg);
                                break;
                            }
                        }
                    }
                }
                else if (!this.performPlatformVersionValidations && !this.performAddOnValidations) {
                    items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, WorldDataInfoGeneratorTest.unexpectedCommandInCommandBlock, "Unexpected command '" + command.name + "'", projectItem, command.name, undefined, commandList[i]));
                }
            }
        }
    }
    async generate(projectItem, contentIndex) {
        const items = [];
        if (projectItem.itemType !== IProjectItemData_1.ProjectItemType.MCWorld &&
            projectItem.itemType !== IProjectItemData_1.ProjectItemType.MCTemplate &&
            projectItem.itemType !== IProjectItemData_1.ProjectItemType.worldFolder &&
            projectItem.itemType !== IProjectItemData_1.ProjectItemType.dialogueBehaviorJson &&
            projectItem.itemType !== IProjectItemData_1.ProjectItemType.animationControllerBehaviorJson &&
            projectItem.itemType !== IProjectItemData_1.ProjectItemType.animationBehaviorJson &&
            projectItem.itemType !== IProjectItemData_1.ProjectItemType.MCFunction) {
            return items;
        }
        const blocksPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.blocks, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.blocks), projectItem);
        items.push(blocksPi);
        const blockActorsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.blockData, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.blockData), projectItem);
        items.push(blockActorsPi);
        const commandsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.command, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.command), projectItem);
        items.push(commandsPi);
        const subCommandsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.executeSubCommand, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.executeSubCommand), projectItem);
        items.push(subCommandsPi);
        const nbtPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.levelDat, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.levelDat), projectItem);
        items.push(nbtPi);
        const nbtExperimentsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.levelDatExperiments, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.levelDatExperiments), projectItem);
        items.push(nbtExperimentsPi);
        if (projectItem.itemType === IProjectItemData_1.ProjectItemType.dialogueBehaviorJson) {
            await projectItem.ensureFileStorage();
            if (projectItem.primaryFile) {
                const diaManifest = await Dialogue_1.default.ensureOnFile(projectItem.primaryFile);
                if (diaManifest && diaManifest.definition && diaManifest.definition["minecraft:npc_dialogue"]) {
                    let scenes = diaManifest.definition["minecraft:npc_dialogue"].scenes;
                    for (const scene of scenes) {
                        if (scene.on_open_commands) {
                            this.processListOfCommands(scene.on_open_commands, items, projectItem, commandsPi, subCommandsPi, true);
                        }
                        if (scene.on_close_commands) {
                            this.processListOfCommands(scene.on_close_commands, items, projectItem, commandsPi, subCommandsPi, true);
                        }
                    }
                    let buttons = diaManifest.getAllButtons();
                    for (const button of buttons) {
                        if (button.commands) {
                            this.processListOfCommands(button.commands, items, projectItem, commandsPi, subCommandsPi, true);
                        }
                    }
                }
            }
        }
        else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.animationControllerBehaviorJson) {
            await projectItem.ensureFileStorage();
            if (projectItem.primaryFile) {
                const acManifest = await AnimationControllerBehaviorDefinition_1.default.ensureOnFile(projectItem.primaryFile);
                if (acManifest && acManifest.data && acManifest.data.animation_controllers) {
                    let states = acManifest.getAllStates();
                    for (const state of states) {
                        if (state.state.on_entry) {
                            this.processListOfCommands(state.state.on_entry, items, projectItem, commandsPi, subCommandsPi, true);
                        }
                        if (state.state.on_exit) {
                            this.processListOfCommands(state.state.on_exit, items, projectItem, commandsPi, subCommandsPi, true);
                        }
                    }
                }
            }
        }
        else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.animationBehaviorJson) {
            await projectItem.ensureFileStorage();
            if (projectItem.primaryFile) {
                const animManifest = await AnimationBehaviorDefinition_1.default.ensureOnFile(projectItem.primaryFile);
                if (animManifest && animManifest.data && animManifest.data.animations) {
                    let timelines = animManifest.getAllTimeline();
                    for (const timeline of timelines) {
                        if (timeline.timeline) {
                            this.processListOfCommands(timeline.timeline, items, projectItem, commandsPi, subCommandsPi, true);
                        }
                    }
                }
            }
        }
        else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.MCFunction) {
            let content = await projectItem.getStringContent();
            if (content !== undefined) {
                let contentLines = content.split("\n");
                this.processListOfCommands(contentLines, items, projectItem, commandsPi, subCommandsPi, false);
            }
        }
        if (projectItem.itemType === IProjectItemData_1.ProjectItemType.MCWorld ||
            projectItem.itemType === IProjectItemData_1.ProjectItemType.MCTemplate ||
            projectItem.itemType === IProjectItemData_1.ProjectItemType.worldFolder) {
            let mcworld = await MCWorld_1.default.ensureOnItem(projectItem);
            if (!mcworld) {
                Log_1.default.debugAlert("Could not find respective world.");
                return items;
            }
            await mcworld.load(false);
            await mcworld.loadData(false);
            if (mcworld.isInErrorState &&
                mcworld.errorMessages &&
                !this.performAddOnValidations &&
                !this.performPlatformVersionValidations) {
                for (const err of mcworld.errorMessages) {
                    items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, WorldDataInfoGeneratorTest.errorProcessingWorld, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.errorProcessingWorld), projectItem, err.message + (err.context ? " - " + err.context : ""), mcworld.name));
                }
            }
            if (projectItem.projectPath &&
                contentIndex &&
                mcworld.levelData &&
                mcworld.levelData.nbt &&
                mcworld.levelData.nbt.singleRoot) {
                const children = mcworld.levelData.nbt.singleRoot.getTagChildren();
                for (const child of children) {
                    if (child.name === "experiments") {
                        for (const experimentChild of child.getTagChildren()) {
                            if (experimentChild.type === NbtBinaryTag_1.NbtTagType.int ||
                                experimentChild.type === NbtBinaryTag_1.NbtTagType.byte ||
                                experimentChild.type === NbtBinaryTag_1.NbtTagType.string) {
                                nbtExperimentsPi.incrementFeature(experimentChild.name, experimentChild.valueAsString);
                                contentIndex.insert(experimentChild.name + "==" + experimentChild.valueAsString, projectItem.projectPath, ContentIndex_1.AnnotationCategory.experiment);
                            }
                        }
                    }
                    else if (child.type === NbtBinaryTag_1.NbtTagType.int ||
                        child.type === NbtBinaryTag_1.NbtTagType.byte ||
                        child.type === NbtBinaryTag_1.NbtTagType.string) {
                        if (child.name !== "LevelName" &&
                            child.name !== "FlatWorldLayers" &&
                            child.name !== "lightningTime" &&
                            child.name !== "EducationOid" &&
                            child.name !== "EducationProductId" &&
                            child.name !== "rainTime" &&
                            child.name !== "worldTemplateUUID" &&
                            !child.name.startsWith("LimitedWorld") &&
                            !child.name.startsWith("SpawnX") &&
                            !child.name.startsWith("SpawnY") &&
                            !child.name.startsWith("SpawnZ")) {
                            if (child.name.indexOf("ersion") >= 0 && !child.valueAsString.startsWith("1.")) {
                                nbtPi.incrementFeature(child.name, "(unknown version)");
                            }
                            else {
                                nbtPi.incrementFeature(child.name, child.valueAsString);
                            }
                            contentIndex.insert(child.name + "==" + child.valueAsString, projectItem.projectPath, ContentIndex_1.AnnotationCategory.worldProperty);
                        }
                    }
                }
            }
            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.chunks, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.chunks), projectItem, mcworld.chunkCount, mcworld.name));
            let blockCount = 0;
            let chunkCount = 0;
            let subchunkLessChunkCount = 0;
            for (const dimIndex in mcworld.chunks) {
                let dim = mcworld.chunks[dimIndex];
                for (const chunkSliverIndex in dim) {
                    const chunkSliver = dim[chunkSliverIndex];
                    if (chunkSliver) {
                        for (const chunkId in chunkSliver) {
                            const chunk = chunkSliver[chunkId];
                            if (chunk) {
                                chunkCount++;
                                if (chunk.subChunks.length <= 0) {
                                    subchunkLessChunkCount++;
                                }
                                if (chunkCount % 1000 === 0) {
                                    await projectItem.project.carto.notifyStatusUpdate("World data validation: scanned " +
                                        chunkCount / 1000 +
                                        "K of " +
                                        Math.floor(mcworld.chunkCount / 1000) +
                                        "K chunks in " +
                                        mcworld.name, Status_1.StatusTopic.validation);
                                }
                                const blockActors = chunk.blockActors;
                                for (let i = 0; i < blockActors.length; i++) {
                                    const blockActor = blockActors[i];
                                    if (blockActor.id) {
                                        blockActorsPi.incrementFeature(blockActor.id);
                                    }
                                    if (blockActor instanceof CommandBlockActor_1.default) {
                                        let cba = blockActor;
                                        if (cba.version) {
                                            blockActorsPi.spectrumIntFeature("Command Version", cba.version);
                                        }
                                        if (cba.version && cba.version < this.modernCommandVersion) {
                                            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.recommendation, this.id, WorldDataInfoGeneratorTest.commandIsFromOlderMinecraftVersion, "Command '" + cba.command + "' is from an older Minecraft version (" + cba.version + ") ", projectItem, "(Command at location " + cba.x + ", " + cba.y + ", " + cba.z + ")", undefined, cba.command));
                                        }
                                        if (cba.command && cba.command.trim().length > 2) {
                                            let command = CommandStructure_1.default.parse(cba.command);
                                            if (CommandRegistry_1.default.isMinecraftBuiltInCommand(command.name)) {
                                                if (this.performAddOnValidations && CommandRegistry_1.default.isAddOnBlockedCommand(command.name)) {
                                                    items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.warning, this.id, WorldDataInfoGeneratorTest.containsWorldImpactingCommand, "Contains command '" +
                                                        command.name +
                                                        "' which is impacts the state of the entire world, and generally shouldn't be used in an add-on", projectItem, command.name, undefined, cba.command));
                                                }
                                                commandsPi.incrementFeature(command.name);
                                                if (command.name === "execute") {
                                                    let foundRun = false;
                                                    for (const arg of command.commandArguments) {
                                                        if (arg === "run") {
                                                            foundRun = true;
                                                        }
                                                        else if (foundRun && CommandRegistry_1.default.isMinecraftBuiltInCommand(arg)) {
                                                            subCommandsPi.incrementFeature(arg);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            else if (!this.performAddOnValidations && !this.performPlatformVersionValidations) {
                                                items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, WorldDataInfoGeneratorTest.unexpectedCommandInCommandBlock, "Unexpected command '" + command.name + "'", projectItem, command.name, undefined, cba.command));
                                            }
                                        }
                                    }
                                }
                                const blockList = chunk.getBlockList();
                                for (let i = 0; i < blockList.length; i++) {
                                    const block = blockList[i];
                                    if (block) {
                                        blockCount++;
                                        if (block.typeName) {
                                            let type = block.typeName;
                                            if (type.indexOf(":") >= 0 && type.indexOf("minecraft:") < 0) {
                                                type = "(custom)";
                                            }
                                            blocksPi.incrementFeature(type);
                                        }
                                    }
                                }
                                chunk.clearCachedData();
                            }
                        }
                    }
                }
            }
            blocksPi.data = blockCount;
            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.minX, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.minX), projectItem, mcworld.minX, mcworld.name));
            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.minZ, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.minZ), projectItem, mcworld.minZ, mcworld.name));
            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.maxX, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.maxX), projectItem, mcworld.maxX, mcworld.name));
            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.maxZ, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.maxZ), projectItem, mcworld.maxZ, mcworld.name));
            items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.subchunklessChunks, "Subchunkless Chunks", projectItem, subchunkLessChunkCount, mcworld.name));
        }
        return items;
    }
}
exports.default = WorldDataInfoGenerator;

//# sourceMappingURL=../maps/info/WorldDataInfoGenerator.js.map
