"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.ProjectionResolutionCommonUtil = void 0;
const internal_1 = require("../../internal");
/**
 * A utility class to handle name based functionality for projections and operations
 * @internal
 */
class ProjectionResolutionCommonUtil {
    /**
     * Function to initialize the input projection attribute state Set for a projection
     * @internal
     */
    static initializeProjectionAttributeStateSet(projDir, ctx, orgSrcRAS, isSourcePolymorphic = false, polymorphicSet = undefined) {
        const set = new internal_1.ProjectionAttributeStateSet(ctx);
        for (const resAttr of orgSrcRAS.set) {
            let prevSet;
            if (isSourcePolymorphic && polymorphicSet) {
                const polyList = polymorphicSet.get(resAttr.resolvedName);
                prevSet = polyList;
            }
            const projAttrState = new internal_1.ProjectionAttributeState(ctx);
            projAttrState.currentResolvedAttribute = resAttr;
            projAttrState.previousStateList = prevSet;
            set.add(projAttrState);
        }
        return set;
    }
    /**
     * If a source is tagged as polymorphic source, get the list of original source
     * @internal
     */
    static getPolymorphicSourceSet(projDir, ctx, source, rasSource) {
        const polySources = new Map();
        // TODO (sukanyas): when projection based polymorphic source is made available - the following line will have to be changed
        // for now assuming non-projections based polymorphic source
        const sourceDef = source.fetchObjectDefinition(projDir.resOpt);
        for (const attr of sourceDef.attributes) {
            if (attr.objectType === internal_1.cdmObjectType.entityAttributeDef) {
                // the attribute context for this entity typed attribute was already created by the `FetchResolvedAttributes` that happens before this function call.
                // we are only interested in linking the attributes to the entity that they came from and the attribute context nodes should not be taken into account.
                // create this dummy attribute context so the resolution code works properly and discard it after.
                const attrCtxParam = {
                    regarding: attr,
                    type: internal_1.cdmAttributeContextType.passThrough,
                    under: new internal_1.CdmAttributeContext(ctx, 'discard')
                };
                const raSet = attr.fetchResolvedAttributes(projDir.resOpt, attrCtxParam);
                for (const resAttr of raSet.set) {
                    // we got a null ctx because null was passed in to fetch, but the nodes are in the parent's tree
                    // so steal them based on name
                    var resAttSrc = rasSource.get(resAttr.resolvedName);
                    if (resAttSrc !== undefined) {
                        resAttr.attCtx = resAttSrc.attCtx;
                    }
                    const projAttrState = new internal_1.ProjectionAttributeState(ctx);
                    projAttrState.currentResolvedAttribute = resAttr;
                    projAttrState.previousStateList = undefined;
                    // the key doesn't exist, initialize with an empty list first
                    if (!polySources.has(resAttr.resolvedName)) {
                        polySources.set(resAttr.resolvedName, []);
                    }
                    polySources.get(resAttr.resolvedName).push(projAttrState);
                }
            }
        }
        return polySources;
    }
    /**
     * Get leaf nodes of the projection state tree for polymorphic scenarios
     * @internal
     */
    static getLeafList(projCtx, attrName) {
        let result;
        for (const top of projCtx.currentAttributeStateSet.states) {
            let st = new internal_1.SearchStructure();
            st = internal_1.SearchStructure.buildStructure(top, top, attrName, st, false, 0);
            if ((st === null || st === void 0 ? void 0 : st.result.foundFlag) === true && st.result.leaf.length > 0) {
                result = st.result;
            }
        }
        return result === null || result === void 0 ? void 0 : result.leaf;
    }
    /**
     * Gets the names of the top-level nodes in the projection state tree (for non-polymorphic scenarios) that match a set of attribute names
     * @internal
     */
    static getTopList(projCtx, attrNames) {
        // This dictionary contains a mapping from the top-level (most recent) name of an attribute
        // to the attribute name the top-level name was derived from (the name contained in the given list)
        const topLevelAttributeNames = new Map();
        // Iterate through each attribute name in the list and search for their top-level names
        for (const attrName of attrNames) {
            // Iterate through each projection attribute state in the current set and check if its
            // current resolved attribute's name is the top-level name of the current attrName
            for (const top of projCtx.currentAttributeStateSet.states) {
                let st = new internal_1.SearchStructure();
                st = internal_1.SearchStructure.buildStructure(top, top, attrName, st, false, 0);
                // Found the top-level name
                if ((st === null || st === void 0 ? void 0 : st.result.foundFlag) === true) {
                    // Create a mapping from the top-level name of the attribute to the name it has in the given list
                    topLevelAttributeNames.set(top.currentResolvedAttribute.resolvedName, attrName);
                }
            }
        }
        return topLevelAttributeNames;
    }
    /**
     * Convert a single value to a list
     * @internal
     */
    static convertToList(top) {
        let topList;
        if (top) {
            topList = [];
            topList.push(top);
        }
        return topList;
    }
    /**
     * Create a constant entity that contains the source mapping to a foreign key.
     * e.g.
     * an fk created to entity "Customer" based on the "customerName", would add a parameter to the "is.linkedEntity.identifier" trait as follows:
     *   [
     *     "/Customer.cdm.json/Customer",
     *     "customerName"
     *   ]
     * In the case of polymorphic source, there will be a collection of such entries.
     * @internal
     */
    static createForeignKeyLinkedEntityIdentifierTraitParameter(projDir, corpus, refFoundList) {
        let traitParamEntRef;
        const entRefAndAttrNameList = [];
        for (const refFound of refFoundList) {
            const resAttr = refFound.currentResolvedAttribute;
            if (!resAttr.owner) {
                const atCorpusPath = resAttr.target instanceof internal_1.CdmObjectBase ? resAttr.target.atCorpusPath : resAttr.resolvedName;
                internal_1.Logger.warning(corpus.ctx, this.TAG, this.createForeignKeyLinkedEntityIdentifierTraitParameter.name, atCorpusPath, internal_1.cdmLogCode.WarnProjCreateForeignKeyTraits, resAttr.resolvedName);
            }
            else if (resAttr.target.objectType === internal_1.cdmObjectType.typeAttributeDef || resAttr.target.objectType === internal_1.cdmObjectType.entityAttributeDef) {
                // find the linked entity
                const owner = resAttr.owner;
                // find where the projection is defined
                const projectionDoc = projDir.owner !== undefined ? projDir.owner.inDocument : undefined;
                if (owner && owner.objectType === internal_1.cdmObjectType.entityDef && projectionDoc) {
                    const entDef = owner.fetchObjectDefinition(projDir.resOpt);
                    if (entDef) {
                        // should contain relative path without the namespace
                        const relativeEntPath = entDef.ctx.corpus.storage.createRelativeCorpusPath(entDef.atCorpusPath, projectionDoc);
                        entRefAndAttrNameList.push([relativeEntPath, resAttr.resolvedName]);
                    }
                }
            }
        }
        if (entRefAndAttrNameList.length > 0) {
            const constantEntity = corpus.MakeObject(internal_1.cdmObjectType.constantEntityDef);
            constantEntity.entityShape = corpus.MakeRef(internal_1.cdmObjectType.entityRef, 'entitySet', true);
            const constantValues = [];
            for (const entRefAndAttrName of entRefAndAttrNameList) {
                const originalSourceEntityAttributeName = projDir.originalSourceAttributeName || '';
                constantValues.push([entRefAndAttrName[0], entRefAndAttrName[1], `${originalSourceEntityAttributeName}_${entRefAndAttrName[0].substring(entRefAndAttrName[0].lastIndexOf('/') + 1)}`]);
            }
            constantEntity.constantValues = constantValues;
            traitParamEntRef = corpus.MakeRef(internal_1.cdmObjectType.entityRef, constantEntity, false);
        }
        return traitParamEntRef;
    }
}
exports.ProjectionResolutionCommonUtil = ProjectionResolutionCommonUtil;
ProjectionResolutionCommonUtil.TAG = ProjectionResolutionCommonUtil.name;
//# sourceMappingURL=ProjectionResolutionCommonUtil.js.map