"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManifestPersistence = void 0;
const guid_typescript_1 = require("guid-typescript");
const __1 = require("..");
const internal_1 = require("../../internal");
const cdmObjectTypeGuards_1 = require("../../Utilities/cdmObjectTypeGuards");
const Logger_1 = require("../../Utilities/Logging/Logger");
const timeUtils = require("../../Utilities/timeUtils");
const extensionHelper = require("./ExtensionHelper");
const types_1 = require("./types");
class ManifestPersistence {
    static fromObject(ctx, obj, folder) {
        return __awaiter(this, void 0, void 0, function* () {
            const extensionTraitDefList = [];
            const manifest = ctx.corpus.MakeObject(internal_1.cdmObjectType.manifestDef, obj.name);
            manifest.virtualLocation = folder.folderPath + internal_1.CdmConstants.modelJsonExtension;
            // We need to set up folder path and namespace of a manifest to be able to retrieve that object.
            folder.documents.push(manifest);
            if (obj['cdm:imports']) {
                obj['cdm:imports'].forEach((impElement) => {
                    const importObj = __1.CdmFolder.ImportPersistence.fromData(ctx, impElement);
                    manifest.imports.push(importObj);
                });
            }
            if (!manifest.imports.allItems.some((importPresent) => importPresent.corpusPath === internal_1.constants.FOUNDATIONS_CORPUS_PATH)) {
                manifest.imports.push(internal_1.constants.FOUNDATIONS_CORPUS_PATH);
            }
            manifest.explanation = obj.description;
            if (obj.modifiedTime !== undefined) {
                manifest.lastFileModifiedTime = new Date(obj.modifiedTime);
            }
            if (obj['cdm:lastChildFileModifiedTime'] !== undefined) {
                manifest.lastChildFileModifiedTime = new Date(obj['cdm:lastChildFileModifiedTime']);
            }
            if (obj['cdm:lastFileStatusCheckTime'] !== undefined) {
                manifest.lastFileStatusCheckTime = new Date(obj['cdm:lastFileStatusCheckTime']);
            }
            if (!internal_1.StringUtils.isBlankByCdmStandard(obj['cdm:documentVersion'])) {
                manifest.documentVersion = obj['cdm:documentVersion'];
            }
            if (obj.application) {
                const applicationTrait = ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, 'is.managedBy', false);
                applicationTrait.isFromProperty = true;
                const arg = ctx.corpus.MakeObject(internal_1.cdmObjectType.argumentDef, 'application');
                arg.value = obj.application;
                applicationTrait.arguments.push(arg);
                // Returns true if the object (or the referenced object) is an extension from the specified symbol name in some way.
                manifest.exhibitsTraits.push(applicationTrait);
            }
            if (obj.version) {
                const versionTrait = ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, 'is.modelConversion.modelVersion', false);
                const arg = ctx.corpus.MakeObject(internal_1.cdmObjectType.argumentDef, 'version');
                arg.value = obj.version;
                versionTrait.arguments.push(arg);
                manifest.exhibitsTraits.push(versionTrait);
            }
            if (obj.culture) {
                const cultureTrait = ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, 'is.partition.culture', false);
                cultureTrait.isFromProperty = true;
                const arg = ctx.corpus.MakeObject(internal_1.cdmObjectType.argumentDef, 'culture');
                arg.value = obj.culture;
                cultureTrait.arguments.push(arg);
                manifest.exhibitsTraits.push(cultureTrait);
            }
            if (obj.isHidden === true) {
                const isHiddenTrait = ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, 'is.hidden', true);
                isHiddenTrait.isFromProperty = true;
                manifest.exhibitsTraits.push(isHiddenTrait);
            }
            const referenceModels = new Map();
            if (obj.referenceModels) {
                // Create a trait and put reference models inside an argument that goes inside the trait.
                const referenceModelsTrait = ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, 'is.modelConversion.referenceModelMap', true);
                referenceModelsTrait.simpleNamedReference = false;
                const referenceModelArg = ctx.corpus.MakeObject(internal_1.cdmObjectType.argumentDef, 'referenceModelMap', false);
                referenceModelArg.value = obj.referenceModels;
                referenceModelsTrait.arguments.push(referenceModelArg);
                manifest.exhibitsTraits.push(referenceModelsTrait);
                obj.referenceModels.forEach((element) => {
                    referenceModels.set(element.id, element.location);
                });
            }
            const entitySchemaByName = new Map();
            if (obj.entities && obj.entities.length > 0) {
                for (const element of obj.entities) {
                    let entity;
                    if (element.$type === 'LocalEntity') {
                        entity = yield __1.ModelJson.LocalEntityDeclarationPersistence.fromData(ctx, folder, element, extensionTraitDefList, manifest);
                    }
                    else if (element.$type === 'ReferenceEntity') {
                        const referenceEntity = element;
                        const entityLocation = referenceModels.get(referenceEntity.modelId);
                        if (!entityLocation) {
                            Logger_1.Logger.error(ctx, this.TAG, this.fromObject.name, undefined, internal_1.cdmLogCode.ErrPersistModelJsonModelIdNotFound);
                            return;
                        }
                        entity = yield __1.ModelJson.ReferencedEntityDeclarationPersistence.fromData(ctx, referenceEntity, entityLocation);
                    }
                    else {
                        Logger_1.Logger.error(ctx, this.TAG, this.fromObject.name, undefined, internal_1.cdmLogCode.ErrPersistModelJsonEntityParsingError);
                        return;
                    }
                    if (entity) {
                        manifest.entities.push(entity);
                        entitySchemaByName.set(entity.entityName, entity.entityPath);
                    }
                    else {
                        Logger_1.Logger.error(ctx, this.TAG, this.fromObject.name, undefined, internal_1.cdmLogCode.ErrPersistModelJsonEntityParsingError);
                    }
                }
            }
            if (obj.relationships !== undefined && obj.relationships.length > 0) {
                for (const relationship of obj.relationships) {
                    const cdmRelationship = yield __1.ModelJson.RelationshipPersistence.fromData(ctx, relationship, entitySchemaByName);
                    if (cdmRelationship !== undefined) {
                        manifest.relationships.push(cdmRelationship);
                    }
                    else {
                        Logger_1.Logger.warning(ctx, this.TAG, this.fromObject.name, undefined, internal_1.cdmLogCode.WarnPersistModelJsonRelReadFailed);
                        return undefined;
                    }
                }
            }
            yield __1.ModelJson.utils.processAnnotationsFromData(ctx, obj, manifest.exhibitsTraits);
            const localExtensionTraitDefList = [];
            extensionHelper.processExtensionFromJson(ctx, obj, types_1.modelBaseProperties, manifest.exhibitsTraits, extensionTraitDefList, localExtensionTraitDefList);
            const importDocs = yield extensionHelper.standardImportDetection(ctx, extensionTraitDefList, localExtensionTraitDefList);
            extensionHelper.addImportDocsToManifest(ctx, importDocs, manifest);
            ManifestPersistence.createExtensionDocAndAddToFolderAndImports(ctx, extensionTraitDefList, folder);
            return manifest;
        });
    }
    static fromData(ctx, docName, jsonData, folder) {
        return __awaiter(this, void 0, void 0, function* () {
            const obj = JSON.parse(jsonData);
            return ManifestPersistence.fromObject(ctx, obj, folder);
        });
    }
    static toData(instance, resOpt, options) {
        return __awaiter(this, void 0, void 0, function* () {
            const result = {};
            result.name = instance.manifestName;
            result.description = instance.explanation;
            result.modifiedTime = timeUtils.getFormattedDateString(instance.lastFileModifiedTime);
            result['cdm:lastChildFileModifiedTime'] = timeUtils.getFormattedDateString(instance.lastChildFileModifiedTime);
            result['cdm:lastFileStatusCheckTime'] = timeUtils.getFormattedDateString(instance.lastFileStatusCheckTime);
            result['cdm:documentVersion'] = instance.documentVersion;
            const t2pm = new internal_1.traitToPropertyMap(instance);
            const isHiddenTrait = t2pm.fetchTraitReference('is.hidden');
            if (isHiddenTrait !== undefined) {
                result.isHidden = true;
            }
            const applicationTrait = t2pm.fetchTraitReference('is.managedBy');
            if (applicationTrait !== undefined) {
                result.application = applicationTrait.arguments.allItems[0].value;
            }
            const versionTrait = t2pm.fetchTraitReference('is.modelConversion.modelVersion');
            if (versionTrait !== undefined) {
                result.version = versionTrait.arguments.allItems[0].value;
            }
            else {
                // version property is required. If it doesn't exist set default.
                result.version = '1.0';
            }
            const cultureTrait = t2pm.fetchTraitReference('is.partition.culture');
            if (cultureTrait !== undefined) {
                result.culture = cultureTrait.arguments.allItems[0].value;
            }
            const referenceEntityLocations = new Map();
            const referenceModels = new Map();
            const referenceModelsTrait = t2pm.fetchTraitReference('is.modelConversion.referenceModelMap');
            if (referenceModelsTrait !== undefined) {
                const refModels = referenceModelsTrait.arguments.allItems[0].value;
                refModels.forEach((element) => {
                    const referenceModel = element;
                    referenceModels.set(referenceModel.id, referenceModel.location);
                    referenceEntityLocations.set(referenceModel.location, referenceModel.id);
                });
            }
            // processAnnotationsToData also processes extensions.
            yield __1.ModelJson.utils.processTraitsAndAnnotationsToData(instance.ctx, result, instance.exhibitsTraits);
            if (instance.entities && instance.entities.length > 0) {
                function createPromise(instance) {
                    return __awaiter(this, void 0, void 0, function* () {
                        result.entities = [];
                        for (const entity of instance.entities.allItems) {
                            let element;
                            if ((0, cdmObjectTypeGuards_1.isLocalEntityDeclarationDefinition)(entity)) {
                                element = yield __1.ModelJson.LocalEntityDeclarationPersistence.toData(entity, instance, resOpt, options);
                            }
                            else if ((0, cdmObjectTypeGuards_1.isReferencedEntityDeclarationDefinition)(entity)) {
                                element = yield __1.ModelJson.ReferencedEntityDeclarationPersistence.toData(entity, resOpt, options);
                                let entityLocation = instance.ctx.corpus.storage.corpusPathToAdapterPath(entity.entityPath);
                                if (internal_1.StringUtils.isBlankByCdmStandard(entityLocation)) {
                                    Logger_1.Logger.error(instance.ctx, ManifestPersistence.TAG, ManifestPersistence.toData.name, instance.atCorpusPath, internal_1.cdmLogCode.ErrPersistModelJsonInvalidEntityPath, entity.entityName);
                                    element = undefined;
                                }
                                const referenceEntity = element;
                                if (referenceEntity !== undefined) {
                                    // path separator can differ depending on the adapter, cover the case where path uses '/' or '\'
                                    const lastSlashLocation = entityLocation.lastIndexOf('/') > entityLocation.lastIndexOf('\\') ?
                                        entityLocation.lastIndexOf('/') : entityLocation.lastIndexOf('\\');
                                    if (lastSlashLocation > 0) {
                                        entityLocation = entityLocation.slice(0, lastSlashLocation);
                                    }
                                    if (referenceEntity.modelId !== undefined) {
                                        const savedLocation = referenceModels.get(referenceEntity.modelId);
                                        if (savedLocation !== undefined && savedLocation !== entityLocation) {
                                            Logger_1.Logger.error(instance.ctx, ManifestPersistence.TAG, ManifestPersistence.toData.name, instance.atCorpusPath, internal_1.cdmLogCode.ErrPersistModelJsonModelIdDuplication);
                                            element = undefined;
                                        }
                                        else if (savedLocation === undefined) {
                                            referenceModels.set(referenceEntity.modelId, entityLocation);
                                            referenceEntityLocations.set(entityLocation, referenceEntity.modelId);
                                        }
                                    }
                                    else if (referenceEntity.modelId === undefined
                                        && referenceEntityLocations.get(entityLocation) !== undefined) {
                                        referenceEntity.modelId = referenceEntityLocations.get(entityLocation);
                                    }
                                    else {
                                        referenceEntity.modelId = guid_typescript_1.Guid.create()
                                            .toString();
                                        referenceModels.set(referenceEntity.modelId, entityLocation);
                                        referenceEntityLocations.set(entityLocation, referenceEntity.modelId);
                                    }
                                }
                            }
                            if (element) {
                                result.entities.push(element);
                            }
                            else {
                                Logger_1.Logger.error(instance.ctx, ManifestPersistence.TAG, ManifestPersistence.toData.name, instance.atCorpusPath, internal_1.cdmLogCode.ErrPersistModelJsonEntityDeclarationConversionError, entity.entityName);
                            }
                        }
                        return result.entities;
                    });
                }
                yield createPromise(instance);
            }
            if (referenceModels.size > 0) {
                result.referenceModels = [];
                referenceModels.forEach((value, key) => {
                    const model = {
                        id: key,
                        location: value
                    };
                    result.referenceModels.push(model);
                });
            }
            if (instance.relationships && instance.relationships.length > 0) {
                result.relationships = [];
                for (const cdmRelationship of instance.relationships) {
                    const relationship = yield __1.ModelJson.RelationshipPersistence.toData(cdmRelationship, resOpt, options, instance.ctx);
                    if (relationship !== undefined) {
                        result.relationships.push(relationship);
                    }
                }
            }
            result['cdm:imports'] = [];
            if (instance.imports && instance.imports.allItems.length > 0) {
                instance.imports.allItems.forEach((element) => {
                    const importObj = __1.CdmFolder.ImportPersistence.toData(element, resOpt, options);
                    if (importObj !== undefined) {
                        result['cdm:imports'].push(importObj);
                    }
                });
            }
            //  Importing foundations.cdm.json to resolve trait properly on manifest
            if (instance.imports === undefined || instance.imports.item(internal_1.constants.FOUNDATIONS_CORPUS_PATH, undefined, false) === undefined) {
                const foundationsImport = {
                    corpusPath: internal_1.constants.FOUNDATIONS_CORPUS_PATH
                };
                result['cdm:imports'].push(foundationsImport);
            }
            return result;
        });
    }
    static createExtensionDocAndAddToFolderAndImports(ctx, extensionTraitDefList, folder) {
        if (extensionTraitDefList.length > 0) {
            const extensionDoc = ctx.corpus.MakeObject(internal_1.cdmObjectType.documentDef, extensionHelper.extensionDocName);
            // pull out the extension trait definitions into a new custom extension document
            for (const def of extensionTraitDefList) {
                extensionDoc.definitions.push(def);
            }
            // import the cdm extensions into this new document that has the custom extensions
            extensionDoc.imports.push('cdm:/extensions/base.extension.cdm.json');
            // add the extension doc to the folder, will wire everything together as needed
            folder.documents.push(extensionDoc);
        }
    }
}
exports.ManifestPersistence = ManifestPersistence;
ManifestPersistence.TAG = ManifestPersistence.name;
// Whether this persistence class has async methods.
ManifestPersistence.isPersistenceAsync = true;
// The file format/extension types this persistence class supports.
ManifestPersistence.formats = [internal_1.CdmConstants.modelJsonExtension];
//# sourceMappingURL=ManifestPersistence.js.map