"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.CdmOperationRenameAttributes = void 0;
const internal_1 = require("../../internal");
/**
 * Class to handle RenameAttributes operations
 */
class CdmOperationRenameAttributes extends internal_1.CdmOperationBase {
    constructor(ctx) {
        super(ctx);
        this.TAG = CdmOperationRenameAttributes.name;
        this.objectType = internal_1.cdmObjectType.operationRenameAttributesDef;
        this.type = internal_1.cdmOperationType.renameAttributes;
    }
    /**
     * @inheritdoc
     */
    copy(resOpt, host) {
        if (!resOpt) {
            resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
        }
        const copy = !host ? new CdmOperationRenameAttributes(this.ctx) : host;
        copy.applyTo = this.applyTo ? this.applyTo.slice() : undefined;
        copy.renameFormat = this.renameFormat;
        this.copyProj(resOpt, copy);
        return copy;
    }
    /**
     * @inheritdoc
     */
    getName() {
        return 'operationRenameAttributes';
    }
    /**
     * @inheritdoc
     */
    getObjectType() {
        return internal_1.cdmObjectType.operationRenameAttributesDef;
    }
    /**
     * @inheritdoc
     */
    validate() {
        const missingFields = [];
        if (!this.renameFormat) {
            missingFields.push('renameFormat');
        }
        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 (postChildren && postChildren(this, path)) {
            return true;
        }
        return false;
    }
    /**
     * @inheritdoc
     * @internal
     */
    appendProjectionAttributeState(projCtx, projOutputSet, attrCtx) {
        // Create a new attribute context for the operation
        const attrCtxOpRenameAttrsParam = {
            under: attrCtx,
            type: internal_1.cdmAttributeContextType.operationRenameAttributes,
            name: `operation/index${this.index}/operationRenameAttributes`
        };
        const attrCtxOpRenameAttrs = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxOpRenameAttrsParam);
        // Get the list of attributes that will be renamed
        let renameAttributes;
        if (this.applyTo !== undefined) {
            renameAttributes = this.applyTo;
        }
        else {
            renameAttributes = [];
            for (const currentPAS of projCtx.currentAttributeStateSet.states) {
                renameAttributes.push(currentPAS.currentResolvedAttribute.resolvedName);
            }
        }
        // Get the top-level attribute names of the attributes to rename
        // We use the top-level names because the rename list may contain a previous name our current resolved attributes had
        const topLevelRenameAttributeNames = internal_1.ProjectionResolutionCommonUtil.getTopList(projCtx, renameAttributes);
        // Initialize a projection attribute context tree builder with the created attribute context for the operation
        const attrCtxTreeBuilder = new internal_1.ProjectionAttributeContextTreeBuilder(attrCtxOpRenameAttrs);
        // 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 attributes to rename
            // If this attribute is not in the rename list, then we are including it in the output without changes
            if (topLevelRenameAttributeNames.has(currentPAS.currentResolvedAttribute.resolvedName)) {
                if (currentPAS.currentResolvedAttribute.target.getObjectType) {
                    // The current attribute should be renamed
                    const newAttributeName = this.getNewAttributeName(projCtx.projectionDirective.originalSourceAttributeName, currentPAS);
                    // Create new resolved attribute with the new name, set the new attribute as target
                    const resAttrNew = internal_1.CdmOperationBase.createNewResolvedAttribute(projCtx, undefined, currentPAS.currentResolvedAttribute, newAttributeName);
                    // Get the attribute name the way it appears in the applyTo list
                    const applyToName = topLevelRenameAttributeNames.get(currentPAS.currentResolvedAttribute.resolvedName);
                    // Create the attribute context parameters and just store it in the builder for now
                    // We will create the attribute contexts at the end
                    attrCtxTreeBuilder.createAndStoreAttributeContextParameters(applyToName, currentPAS, resAttrNew, internal_1.cdmAttributeContextType.attributeDefinition, currentPAS.currentResolvedAttribute.attCtx, // lineage is the original attribute
                    undefined // don't know who will point here yet
                    );
                    // Create a projection attribute state for the renamed attribute by creating a copy of the current state
                    // Copy() sets the current state as the previous state for the new one
                    // We only create projection attribute states for attributes that are in the rename list
                    const newPAS = currentPAS.copy();
                    // Update the resolved attribute to be the new renamed attribute we created
                    newPAS.currentResolvedAttribute = resAttrNew;
                    projOutputSet.add(newPAS);
                }
                else {
                    internal_1.Logger.warning(this.ctx, this.TAG, this.appendProjectionAttributeState.name, null, internal_1.cdmLogCode.WarnProjRenameAttrNotSupported);
                    // Add the attribute without changes
                    projOutputSet.add(currentPAS);
                }
            }
            else {
                // Pass through
                projOutputSet.add(currentPAS);
            }
        }
        // Create all the attribute contexts and construct the tree
        attrCtxTreeBuilder.constructAttributeContextTree(projCtx);
        return projOutputSet;
    }
    /**
     * Renames an attribute with the current renameFormat
     * @param projectionOwnerName The attribute name of projection owner (only available when the owner is an entity attribute or type attribute).
     * @param attributeState The attribute state
     */
    getNewAttributeName(projectionOwnerName, currentPAS) {
        if (!this.renameFormat) {
            internal_1.Logger.error(this.ctx, this.TAG, this.getNewAttributeName.name, null, internal_1.cdmLogCode.ErrProjRenameFormatIsNotSet);
        }
        return internal_1.CdmOperationBase.replaceWildcardCharacters(this.renameFormat, projectionOwnerName, currentPAS);
    }
}
exports.CdmOperationRenameAttributes = CdmOperationRenameAttributes;
//# sourceMappingURL=CdmOperationRenameAttributes.js.map