"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.CdmOperationAlterTraits = void 0;
const internal_1 = require("../../internal");
/**
 * Class to handle AlterTraits operations
 */
class CdmOperationAlterTraits extends internal_1.CdmOperationBase {
    constructor(ctx) {
        super(ctx);
        this.TAG = CdmOperationAlterTraits.name;
        this.objectType = internal_1.cdmObjectType.operationAlterTraitsDef;
        this.type = internal_1.cdmOperationType.alterTraits;
    }
    /**
     * @inheritdoc
     */
    copy(resOpt, host) {
        if (!resOpt) {
            resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
        }
        const copy = !host ? new CdmOperationAlterTraits(this.ctx) : host;
        if (this.traitsToAdd && this.traitsToAdd.length > 0) {
            for (const trait of this.traitsToAdd) {
                copy.traitsToAdd.push(trait.copy());
            }
        }
        if (this.traitsToRemove && this.traitsToRemove.length > 0) {
            for (const trait of this.traitsToRemove) {
                copy.traitsToRemove.push(trait.copy());
            }
        }
        copy.applyTo = this.applyTo ? this.applyTo.slice() : undefined;
        copy.argumentsContainWildcards = this.argumentsContainWildcards;
        this.copyProj(resOpt, copy);
        return copy;
    }
    /**
     * @inheritdoc
     */
    getName() {
        return 'operationAlterTraits';
    }
    /**
     * @inheritdoc
     */
    getObjectType() {
        return internal_1.cdmObjectType.operationAlterTraitsDef;
    }
    /**
     * @inheritdoc
     */
    validate() {
        const missingFields = [];
        if (!this.traitsToAdd && !this.traitsToRemove) {
            missingFields.push('traitsToAdd');
            missingFields.push('traitsToRemove');
        }
        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.traitsToAdd !== undefined && internal_1.CdmObjectBase.visitArray(this.traitsToAdd, `${path}/traitsToAdd/`, preChildren, postChildren)) {
            return true;
        }
        if (this.traitsToRemove !== undefined && internal_1.CdmObjectBase.visitArray(this.traitsToRemove, `${path}/traitsToRemove/`, preChildren, postChildren)) {
            return true;
        }
        if (postChildren && postChildren(this, path)) {
            return true;
        }
        return false;
    }
    /**
     * @inheritdoc
     * @internal
     */
    appendProjectionAttributeState(projCtx, projOutputSet, attrCtx) {
        // Create a new attribute context for the operation
        const attrCtxOpAlterTraitsParam = {
            under: attrCtx,
            type: internal_1.cdmAttributeContextType.operationAlterTraits,
            name: `operation/index${this.index}/${this.getName()}`
        };
        const attrCtxOpAlterTraits = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxOpAlterTraitsParam);
        // Get the top-level attribute names of the selected attributes to apply
        // We use the top-level names because the applyTo list may contain a previous name our current resolved attributes had
        const topLevelSelectedAttributeNames = this.applyTo !== undefined ? internal_1.ProjectionResolutionCommonUtil.getTopList(projCtx, this.applyTo) : undefined;
        // Iterate through all the projection attribute states generated from the source's resolved attributes
        // Each projection attribute state contains a resolved attribute that it is corresponding to
        for (const currentPAS of projCtx.currentAttributeStateSet.states) {
            // Check if the current projection attribute state's resolved attribute is in the list of selected attributes
            // If this attribute is not in the list, then we are including it in the output without changes
            if (topLevelSelectedAttributeNames === undefined || topLevelSelectedAttributeNames.has(currentPAS.currentResolvedAttribute.resolvedName)) {
                // Create a new attribute context for the new artifact attribute we will create
                const attrCtxNewAttrParam = {
                    under: attrCtxOpAlterTraits,
                    type: internal_1.cdmAttributeContextType.attributeDefinition,
                    name: currentPAS.currentResolvedAttribute.resolvedName
                };
                const attrCtxNewAttr = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxNewAttrParam);
                let newResAttr = null;
                if (currentPAS.currentResolvedAttribute.target instanceof internal_1.ResolvedAttributeSet) {
                    // Attribute group
                    // Create a copy of resolved attribute set 
                    const resAttrNewCopy = currentPAS.currentResolvedAttribute.target.copy();
                    newResAttr = new internal_1.ResolvedAttribute(projCtx.projectionDirective.resOpt, resAttrNewCopy, currentPAS.currentResolvedAttribute.resolvedName, attrCtxNewAttr);
                    // the resolved attribute group obtained from previous projection operation may have a different set of traits comparing to the resolved attribute target. 
                    // We would want to take the set of traits from the resolved attribute.
                    newResAttr.resolvedTraits = currentPAS.currentResolvedAttribute.resolvedTraits.deepCopy();
                }
                else if (currentPAS.currentResolvedAttribute.target instanceof internal_1.CdmAttribute) {
                    // Entity Attribute or Type Attribute
                    newResAttr = internal_1.CdmOperationBase.createNewResolvedAttribute(projCtx, attrCtxNewAttr, currentPAS.currentResolvedAttribute, currentPAS.currentResolvedAttribute.resolvedName);
                }
                else {
                    internal_1.Logger.warning(this.ctx, this.TAG, this.appendProjectionAttributeState.name, this.atCorpusPath, internal_1.cdmLogCode.ErrProjUnsupportedSource, typeof currentPAS.currentResolvedAttribute.target, this.getName());
                    projOutputSet.add(currentPAS);
                    break;
                }
                newResAttr.resolvedTraits = newResAttr.resolvedTraits.mergeSet(this.resolvedNewTraits(projCtx, currentPAS));
                this.removeTraitsInNewAttribute(projCtx.projectionDirective.resOpt, newResAttr);
                // Create a projection attribute state for the new attribute with new applied traits 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();
                // Update the resolved attribute to be the new attribute we created
                newPAS.currentResolvedAttribute = newResAttr;
                projOutputSet.add(newPAS);
            }
            else {
                // Pass through
                projOutputSet.add(currentPAS);
            }
        }
        return projOutputSet;
    }
    resolvedNewTraits(projCtx, currentPAS) {
        var _a;
        let resolvedTraitSet = new internal_1.ResolvedTraitSet(projCtx.projectionDirective.resOpt);
        const projectionOwnerName = (_a = projCtx.projectionDirective.originalSourceAttributeName) !== null && _a !== void 0 ? _a : "";
        for (const traitRef of this.traitsToAdd) {
            const traitRefCopy = traitRef.fetchResolvedTraits(projCtx.projectionDirective.resOpt).deepCopy();
            this.replaceWildcardCharacters(projCtx.projectionDirective.resOpt, traitRefCopy, projectionOwnerName, currentPAS);
            resolvedTraitSet = resolvedTraitSet.mergeSet(traitRefCopy);
        }
        return resolvedTraitSet;
    }
    /**
     * Replace wild characters in the arguments if argumentsContainWildcards is true.
     */
    replaceWildcardCharacters(resOpt, resolvedTraitSet, projectionOwnerName, currentPAS) {
        if (this.argumentsContainWildcards !== undefined && this.argumentsContainWildcards == true) {
            resolvedTraitSet.set.forEach(resolvedTrait => {
                const parameterValueSet = resolvedTrait.parameterValues;
                for (let i = 0; i < parameterValueSet.length; ++i) {
                    var value = parameterValueSet.fetchValue(i);
                    if (typeof value === 'string') {
                        var newVal = internal_1.CdmOperationBase.replaceWildcardCharacters(value, projectionOwnerName, currentPAS);
                        if (newVal != value) {
                            parameterValueSet.setParameterValue(resOpt, parameterValueSet.fetchParameterAtIndex(i).getName(), newVal);
                        }
                    }
                }
            });
        }
    }
    /**
     * Remove traits from the new resolved attribute.
     */
    removeTraitsInNewAttribute(resOpt, newResAttr) {
        const traitNamesToRemove = new Set();
        if (this.traitsToRemove !== undefined) {
            for (const traitRef of this.traitsToRemove) {
                const resolvedTraitSet = traitRef.fetchResolvedTraits(resOpt).deepCopy();
                resolvedTraitSet.set.forEach(rt => traitNamesToRemove.add(rt.traitName));
            }
            traitNamesToRemove.forEach(traitName => newResAttr.resolvedTraits.remove(resOpt, traitName));
        }
    }
}
exports.CdmOperationAlterTraits = CdmOperationAlterTraits;
//# sourceMappingURL=CdmOperationAlterTraits.js.map