"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.CdmOperationArrayExpansion = void 0;
const internal_1 = require("../../internal");
/**
 * Class to handle ArrayExpansion operations
 */
class CdmOperationArrayExpansion extends internal_1.CdmOperationBase {
    constructor(ctx) {
        super(ctx);
        this.TAG = CdmOperationArrayExpansion.name;
        this.objectType = internal_1.cdmObjectType.operationArrayExpansionDef;
        this.type = internal_1.cdmOperationType.arrayExpansion;
    }
    /**
     * @inheritdoc
     */
    copy(resOpt, host) {
        if (!resOpt) {
            resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
        }
        const copy = !host ? new CdmOperationArrayExpansion(this.ctx) : host;
        copy.startOrdinal = this.startOrdinal;
        copy.endOrdinal = this.endOrdinal;
        this.copyProj(resOpt, copy);
        return copy;
    }
    /**
     * @inheritdoc
     */
    getName() {
        return 'operationArrayExpansion';
    }
    /**
     * @inheritdoc
     */
    getObjectType() {
        return internal_1.cdmObjectType.operationArrayExpansionDef;
    }
    /**
     * @inheritdoc
     */
    validate() {
        const missingFields = [];
        if (this.startOrdinal === undefined) {
            missingFields.push('startOrdinal');
        }
        if (this.endOrdinal === undefined) {
            missingFields.push('endOrdinal');
        }
        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 attrCtxOpArrayExpansionParam = {
            under: attrCtx,
            type: internal_1.cdmAttributeContextType.operationArrayExpansion,
            name: `operation/index${this.index}/operationArrayExpansion`
        };
        const attrCtxOpArrayExpansion = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxOpArrayExpansionParam);
        // Expansion steps start at round 0
        let round = 0;
        let projAttrStatesFromRounds = [];
        // Ordinal validation
        if (this.startOrdinal > this.endOrdinal) {
            internal_1.Logger.warning(this.ctx, this.TAG, this.appendProjectionAttributeState.name, this.atCorpusPath, internal_1.cdmLogCode.WarnValdnOrdinalStartEndOrder, this.startOrdinal.toString(), this.endOrdinal.toString());
        }
        else {
            // Ordinals should start at startOrdinal or 0, whichever is larger.
            const startingOrdinal = Math.max(0, this.startOrdinal);
            // Ordinals should end at endOrdinal or the maximum ordinal allowed (set in resolve options), whichever is smaller.
            if (this.endOrdinal > projCtx.projectionDirective.resOpt.maxOrdinalForArrayExpansion) {
                internal_1.Logger.warning(this.ctx, this.TAG, this.appendProjectionAttributeState.name, this.atCorpusPath, internal_1.cdmLogCode.WarnValdnMaxOrdinalTooHigh, this.endOrdinal, projCtx.projectionDirective.resOpt.maxOrdinalForArrayExpansion.toString());
            }
            const endingOrdinal = Math.min(projCtx.projectionDirective.resOpt.maxOrdinalForArrayExpansion, this.endOrdinal);
            // For each ordinal, create a copy of the input resolved attribute
            for (let i = startingOrdinal; i <= endingOrdinal; i++) {
                // Create a new attribute context for the round
                const attrCtxRoundParam = {
                    under: attrCtxOpArrayExpansion,
                    type: internal_1.cdmAttributeContextType.generatedRound,
                    name: `_generatedAttributeRound${round}`
                };
                const attrCtxRound = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxRoundParam);
                // 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) {
                    // Create a new attribute context for the expanded attribute with the current ordinal
                    const attrCtxExpandedAttrParam = {
                        under: attrCtxRound,
                        type: internal_1.cdmAttributeContextType.attributeDefinition,
                        name: `${currentPAS.currentResolvedAttribute.resolvedName}@${i}`
                    };
                    const attrCtxExpandedAttr = internal_1.CdmAttributeContext.createChildUnder(projCtx.projectionDirective.resOpt, attrCtxExpandedAttrParam);
                    if (currentPAS.currentResolvedAttribute.target instanceof internal_1.ResolvedAttributeSet) {
                        internal_1.Logger.error(this.ctx, this.TAG, this.appendProjectionAttributeState.name, this.atCorpusPath, internal_1.cdmLogCode.ErrProjUnsupportedAttrGroups);
                        projAttrStatesFromRounds = [];
                        break;
                    }
                    // Create a new resolved attribute for the expanded attribute
                    const newResAttr = internal_1.CdmOperationBase.createNewResolvedAttribute(projCtx, attrCtxExpandedAttr, currentPAS.currentResolvedAttribute, currentPAS.currentResolvedAttribute.resolvedName);
                    // Create a projection attribute state for the expanded attribute
                    const newPAS = new internal_1.ProjectionAttributeState(projOutputSet.ctx);
                    newPAS.currentResolvedAttribute = newResAttr;
                    newPAS.previousStateList = [currentPAS];
                    newPAS.ordinal = i;
                    projAttrStatesFromRounds.push(newPAS);
                }
                if (i === endingOrdinal) {
                    break;
                }
                // Increment the round
                round++;
            }
        }
        if (projAttrStatesFromRounds.length === 0) {
            // No rounds were produced from the array expansion - input passes through
            for (const pas of projCtx.currentAttributeStateSet.states) {
                projOutputSet.add(pas);
            }
        }
        else {
            // Add all the projection attribute states containing the expanded attributes to the output
            for (const pas of projAttrStatesFromRounds) {
                projOutputSet.add(pas);
            }
        }
        return projOutputSet;
    }
}
exports.CdmOperationArrayExpansion = CdmOperationArrayExpansion;
//# sourceMappingURL=CdmOperationArrayExpansion.js.map