"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.CdmOperationCombineAttributes = void 0;
const internal_1 = require("../../internal");
/**
 * Class to handle CombineAttributes operations
 */
class CdmOperationCombineAttributes extends internal_1.CdmOperationBase {
    constructor(ctx) {
        super(ctx);
        this.TAG = CdmOperationCombineAttributes.name;
        this.objectType = internal_1.cdmObjectType.operationCombineAttributesDef;
        this.type = internal_1.cdmOperationType.combineAttributes;
        this.select = [];
    }
    /**
     * @inheritdoc
     */
    copy(resOpt, host) {
        if (!resOpt) {
            resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
        }
        const copy = !host ? new CdmOperationCombineAttributes(this.ctx) : host;
        copy.select = this.select ? this.select.slice() : undefined;
        copy.mergeInto = this.mergeInto ? this.mergeInto.copy(resOpt) : undefined;
        this.copyProj(resOpt, copy);
        return copy;
    }
    /**
     * @inheritdoc
     */
    getName() {
        return 'operationCombineAttributes';
    }
    /**
     * @inheritdoc
     */
    getObjectType() {
        return internal_1.cdmObjectType.operationCombineAttributesDef;
    }
    /**
     * @inheritdoc
     */
    validate() {
        const missingFields = [];
        if (!this.select) {
            missingFields.push('select');
        }
        if (!this.mergeInto) {
            missingFields.push('mergeInto');
        }
        if (missingFields.length > 0) {
            internal_1.Logger.error(this.ctx, this.TAG, this.validate.name, this.atCorpusPath, internal_1.cdmLogCode.ErrValdnIntegrityCheckFailure, this.atCorpusPath, missingFields.map((s) => `'${s}'`).join(', '));
            return false;
        }
        return true;
    }
    /**
     * @inheritdoc
     */
    visit(pathFrom, preChildren, postChildren) {
        const path = this.fetchDeclaredPath(pathFrom);
        if (preChildren && preChildren(this, path)) {
            return false;
        }
        if (this.mergeInto !== undefined && this.mergeInto.visit(`${path}/mergeInto/`, preChildren, postChildren)) {
            return true;
        }
        if (postChildren && postChildren(this, path)) {
            return true;
        }
        return false;
    }
    /**
     * @inheritdoc
     * @internal
     */
    appendProjectionAttributeState(projCtx, projAttrStateSet, attrCtx) {
        // Create a new attribute context for the operation
        const attrCtxOpCombineAttrsParam = {
            under: attrCtx,
            type: internal_1.cdmAttributeContextType.operationCombineAttributes,
            name: `operation/index${this.index}/operationCombineAttributes`
        };
        const attrCtxOpCombineAttrs = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxOpCombineAttrsParam);
        // Initialize a projection attribute context tree builder with the created attribute context for the operation
        const attrCtxTreeBuilder = new internal_1.ProjectionAttributeContextTreeBuilder(attrCtxOpCombineAttrs);
        // Get all the leaf level PAS nodes from the tree for each selected attribute and cache to a dictionary
        const leafLevelCombineAttributeNames = new Map();
        // Also, create a single list of leaf level PAS
        const leafLevelMergePASList = [];
        for (const select of this.select) {
            const leafLevelListForCurrentSelect = internal_1.ProjectionResolutionCommonUtil.getLeafList(projCtx, select);
            if (leafLevelListForCurrentSelect !== undefined &&
                leafLevelListForCurrentSelect.length > 0 &&
                !leafLevelCombineAttributeNames.has(select)) {
                leafLevelCombineAttributeNames.set(select, leafLevelListForCurrentSelect);
                leafLevelMergePASList.push.apply(leafLevelMergePASList, leafLevelListForCurrentSelect);
            }
        }
        // Create a List of top-level PAS objects that will be get merged based on the selected attributes
        const pasMergeList = [];
        // Run through the top-level PAS objects 
        for (const currentPAS of projCtx.currentAttributeStateSet.states) {
            if (leafLevelCombineAttributeNames.has(currentPAS.currentResolvedAttribute.resolvedName)) {
                // Attribute to Merge
                if (!pasMergeList.includes(currentPAS)) {
                    pasMergeList.push(currentPAS);
                }
            }
            else {
                // Attribute to Pass Through
                // Create a projection attribute state for the non-selected / pass-through attribute by creating a copy of the current state
                // Copy() sets the current state as the previous state for the new one
                const newPAS = currentPAS.copy();
                projAttrStateSet.add(newPAS);
            }
        }
        if (pasMergeList.length > 0) {
            const mergeIntoAttribute = this.mergeInto;
            // the merged attribute needs one new place to live, so here it is
            const mergedAttrCtxParam = {
                under: attrCtxOpCombineAttrs,
                type: internal_1.cdmAttributeContextType.attributeDefinition,
                name: mergeIntoAttribute.getName()
            };
            const mergedAttrCtx = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, mergedAttrCtxParam);
            // Create new resolved attribute, set the new attribute as target
            const raNewMergeInto = CdmOperationCombineAttributes.createNewResolvedAttribute(projCtx, mergedAttrCtx, mergeIntoAttribute);
            // Create new output projection attribute state set
            const newMergeIntoPAS = new internal_1.ProjectionAttributeState(projAttrStateSet.ctx);
            newMergeIntoPAS.currentResolvedAttribute = raNewMergeInto;
            newMergeIntoPAS.previousStateList = pasMergeList;
            const attributesAddedToContext = new Set();
            // Create the attribute context parameters and just store it in the builder for now
            // We will create the attribute contexts at the end
            for (const select of leafLevelCombineAttributeNames.keys()) {
                if (leafLevelCombineAttributeNames.has(select) &&
                    leafLevelCombineAttributeNames.get(select) !== undefined &&
                    leafLevelCombineAttributeNames.get(select).length > 0) {
                    for (const leafLevelForSelect of leafLevelCombineAttributeNames.get(select)) {
                        // When dealing with a polymorphic entity, it is possible that multiple entities have an attribute with the same name
                        // Only one attribute with each name should be added otherwise the attribute context will end up with duplicated nodes
                        if (!attributesAddedToContext.has(leafLevelForSelect.currentResolvedAttribute.resolvedName)) {
                            attributesAddedToContext.add(leafLevelForSelect.currentResolvedAttribute.resolvedName);
                            attrCtxTreeBuilder.createAndStoreAttributeContextParameters(select, leafLevelForSelect, newMergeIntoPAS.currentResolvedAttribute, internal_1.cdmAttributeContextType.attributeDefinition, leafLevelForSelect.currentResolvedAttribute.attCtx, // lineage is the source att
                            newMergeIntoPAS.currentResolvedAttribute.attCtx // merge into points back here
                            );
                        }
                    }
                }
            }
            projAttrStateSet.add(newMergeIntoPAS);
        }
        // Create all the attribute contexts and construct the tree
        attrCtxTreeBuilder.constructAttributeContextTree(projCtx);
        return projAttrStateSet;
    }
}
exports.CdmOperationCombineAttributes = CdmOperationCombineAttributes;
//# sourceMappingURL=CdmOperationCombineAttributes.js.map