"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.CdmObjectBase = void 0;
const internal_1 = require("../internal");
const Persistence_1 = require("../Persistence");
class CdmObjectBase {
    constructor(ctx) {
        this.resolvingTraits = false;
        this.ID = internal_1.CdmCorpusDefinition.nextID();
        this.ctx = ctx;
    }
    get atCorpusPath() {
        if (!this.inDocument) {
            return `NULL:/NULL/${this.declaredPath ? this.declaredPath : ''}`;
        }
        else {
            return `${this.inDocument.atCorpusPath}/${this.declaredPath ? this.declaredPath : ''}`;
        }
    }
    static get objectType() {
        return;
    }
    /**
     * @deprecated
     */
    static instanceFromData(...args) {
        const objectType = this.objectType;
        const persistenceType = 'CdmFolder';
        return Persistence_1.PersistenceLayer.fromData(...args, objectType, persistenceType);
    }
    /**
     * @internal
     */
    static visitArray(items, path, preChildren, postChildren) {
        // let bodyCode = () =>
        {
            let result = false;
            if (items) {
                const lItem = items.length;
                for (let iItem = 0; iItem < lItem; iItem++) {
                    const element = items.allItems[iItem];
                    if (element) {
                        if (element.visit(path, preChildren, postChildren)) {
                            result = true;
                            break;
                        }
                    }
                }
            }
            return result;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    static resolvedTraitToTraitRef(resOpt, rt) {
        let traitRef;
        if (rt.parameterValues && rt.parameterValues.length) {
            traitRef = rt.trait.ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, rt.traitName, false);
            const l = rt.parameterValues.length;
            if (l === 1) {
                // just one argument, use the shortcut syntax
                let val = CdmObjectBase.protectParameterValues(resOpt, rt.parameterValues.values[0]);
                if (val !== undefined) {
                    traitRef.arguments.push(undefined, val);
                }
            }
            else {
                for (let i = 0; i < l; i++) {
                    const param = rt.parameterValues.fetchParameterAtIndex(i);
                    let val = CdmObjectBase.protectParameterValues(resOpt, rt.parameterValues.values[i]);
                    if (val !== undefined) {
                        traitRef.arguments.push(param.name, val);
                    }
                }
            }
        }
        else {
            traitRef = rt.trait.ctx.corpus.MakeObject(internal_1.cdmObjectType.traitRef, rt.traitName, true);
        }
        if (resOpt.saveResolutionsOnCopy) {
            // used to localize references between documents
            traitRef.explicitReference = rt.trait;
            traitRef.inDocument = rt.trait.inDocument;
        }
        // always make it a property when you can, however the dataFormat traits should be left alone
        // also the wellKnown is the first constrained list that uses the datatype to hold the table instead of the default value property.
        // so until we figure out how to move the enums away from default value, show that trait too
        if (rt.trait.associatedProperties && !rt.trait.isDerivedFrom('is.dataFormat', resOpt) && rt.trait.traitName !== 'is.constrainedList.wellKnown') {
            traitRef.isFromProperty = true;
        }
        return traitRef;
    }
    /**
     * @deprecated
     */
    copyData(resOpt, options) {
        const persistenceType = 'CdmFolder';
        if (resOpt == null) {
            resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
        }
        if (options == null) {
            options = new internal_1.copyOptions();
        }
        return Persistence_1.PersistenceLayer.toData(this, resOpt, options, persistenceType);
    }
    /**
     * @internal
     */
    constructResolvedTraits(rtsb, resOpt) {
        // let bodyCode = () =>
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    constructResolvedAttributes(resOpt, under) {
        // let bodyCode = () =>
        {
            return undefined;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    fetchResolvedTraits(resOpt) {
        // let bodyCode = () =>
        {
            const wasPreviouslyResolving = this.ctx.corpus.isCurrentlyResolving;
            this.ctx.corpus.isCurrentlyResolving = true;
            if (!resOpt) {
                resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
            }
            const kind = 'rtsb';
            const ctx = this.ctx;
            let cacheTagA = ctx.corpus.createDefinitionCacheTag(resOpt, this, kind);
            let rtsbAll;
            if (!this.traitCache) {
                this.traitCache = new Map();
            }
            else {
                rtsbAll = cacheTagA ? this.traitCache.get(cacheTagA) : undefined;
            }
            // store the previous symbol set, we will need to add it with
            // children found from the constructResolvedTraits call
            const currSymbolRefSet = resOpt.symbolRefSet || new internal_1.SymbolSet();
            resOpt.symbolRefSet = new internal_1.SymbolSet();
            if (!rtsbAll) {
                rtsbAll = new internal_1.ResolvedTraitSetBuilder();
                if (!this.resolvingTraits) {
                    this.resolvingTraits = true;
                    this.constructResolvedTraits(rtsbAll, resOpt);
                    this.resolvingTraits = false;
                }
                const objDef = this.fetchObjectDefinition(resOpt);
                if (objDef !== undefined) {
                    // register set of possible docs
                    ctx.corpus.registerDefinitionReferenceSymbols(objDef, kind, resOpt.symbolRefSet);
                    if (rtsbAll.rts === undefined) {
                        // nothing came back, but others will assume there is a set in this builder
                        rtsbAll.rts = new internal_1.ResolvedTraitSet(resOpt);
                    }
                    // get the new cache tag now that we have the list of docs
                    cacheTagA = ctx.corpus.createDefinitionCacheTag(resOpt, this, kind);
                    if (cacheTagA) {
                        this.traitCache.set(cacheTagA, rtsbAll);
                    }
                }
            }
            else {
                // cache was found
                // get the SymbolSet of refereces for this cached object
                const key = internal_1.CdmCorpusDefinition.createCacheKeyFromObject(this, kind);
                resOpt.symbolRefSet = ctx.corpus.definitionReferenceSymbols.get(key);
            }
            // merge child symbols set with current
            currSymbolRefSet.merge(resOpt.symbolRefSet);
            resOpt.symbolRefSet = currSymbolRefSet;
            this.ctx.corpus.isCurrentlyResolving = wasPreviouslyResolving;
            return rtsbAll.rts;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    fetchObjectFromCache(resOpt, acpInContext) {
        const kind = 'rasb';
        const ctx = this.ctx; // what it actually is
        const cacheTag = ctx.corpus.createDefinitionCacheTag(resOpt, this, kind, acpInContext ? 'ctx' : '');
        return cacheTag ? ctx.attributeCache.get(cacheTag) : undefined;
    }
    /**
     * @internal
     */
    fetchResolvedAttributes(resOpt, acpInContext) {
        // let bodyCode = () =>
        {
            const wasPreviouslyResolving = this.ctx.corpus.isCurrentlyResolving;
            this.ctx.corpus.isCurrentlyResolving = true;
            if (!resOpt) {
                resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
            }
            let inCircularReference = false;
            const wasInCircularReference = resOpt.inCircularReference;
            if (internal_1.isEntityDefinition(this)) {
                inCircularReference = resOpt.currentlyResolvingEntities.has(this);
                resOpt.currentlyResolvingEntities.add(this);
                resOpt.inCircularReference = inCircularReference;
                // uncomment this line as a test to turn off allowing cycles
                //if (inCircularReference) {
                //    return new ResolvedAttributeSet();
                //}
            }
            const currentDepth = resOpt.depthInfo.currentDepth;
            const kind = 'rasb';
            const ctx = this.ctx; // what it actually is
            let rasbResult;
            let rasbCache = this.fetchObjectFromCache(resOpt, acpInContext);
            let underCtx;
            // store the previous symbol set, we will need to add it with
            // children found from the constructResolvedTraits call
            const currSymRefSet = resOpt.symbolRefSet || new internal_1.SymbolSet();
            resOpt.symbolRefSet = new internal_1.SymbolSet();
            // if using the cache passes the maxDepth, we cannot use it
            if (rasbCache && resOpt.depthInfo.currentDepth + rasbCache.ras.depthTraveled > resOpt.depthInfo.maxDepth) {
                rasbCache = undefined;
            }
            if (!rasbCache) {
                // a new context node is needed for these attributes, 
                // this tree will go into the cache, so we hang it off a placeholder parent
                // when it is used from the cache (or now), then this placeholder parent is ignored and the things under it are
                // put into the 'receiving' tree
                underCtx = internal_1.CdmAttributeContext.getUnderContextForCacheContext(resOpt, this.ctx, acpInContext);
                rasbCache = this.constructResolvedAttributes(resOpt, underCtx);
                if (rasbCache !== undefined) {
                    // register set of possible docs
                    const odef = this.fetchObjectDefinition(resOpt);
                    if (odef !== undefined) {
                        ctx.corpus.registerDefinitionReferenceSymbols(odef, kind, resOpt.symbolRefSet);
                        if (this.objectType === internal_1.cdmObjectType.entityDef) {
                            // if we just got attributes for an entity, take the time now to clean up this cached tree and prune out
                            // things that don't help explain where the final set of attributes came from
                            if (underCtx) {
                                const scopesForAttributes = new Set();
                                underCtx.collectContextFromAtts(rasbCache.ras, scopesForAttributes); // the context node for every final attribute
                                if (!underCtx.pruneToScope(scopesForAttributes)) {
                                    return undefined;
                                }
                            }
                        }
                        // get the new cache tag now that we have the list of docs
                        const cacheTag = ctx.corpus.createDefinitionCacheTag(resOpt, this, kind, acpInContext ? 'ctx' : undefined);
                        // save this as the cached version
                        if (cacheTag) {
                            ctx.attributeCache.set(cacheTag, rasbCache);
                        }
                    }
                    // get the 'underCtx' of the attribute set from the acp that is wired into
                    // the target tree
                    underCtx = rasbCache.ras.attributeContext ?
                        rasbCache.ras.attributeContext.getUnderContextFromCacheContext(resOpt, acpInContext) : undefined;
                }
            }
            else {
                // get the 'underCtx' of the attribute set from the cache. The one stored there was build with a different
                // acp and is wired into the fake placeholder. so now build a new underCtx wired into the output tree but with
                // copies of all cached children
                underCtx = rasbCache.ras.attributeContext ?
                    rasbCache.ras.attributeContext.getUnderContextFromCacheContext(resOpt, acpInContext) : undefined;
                //underCtx.validateLineage(resOpt); // debugging
            }
            if (rasbCache) {
                // either just built something or got from cache
                // either way, same deal: copy resolved attributes and copy the context tree associated with it
                // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree
                // 2. deep copy the tree. 
                // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree
                rasbResult = new internal_1.ResolvedAttributeSetBuilder();
                rasbResult.ras = rasbCache.ras.copy();
                // 2. deep copy the tree and map the context references. 
                if (underCtx) // null context? means there is no tree, probably 0 attributes came out
                 {
                    if (!underCtx.associateTreeCopyWithAttributes(resOpt, rasbResult.ras)) {
                        return undefined;
                    }
                }
            }
            if (internal_1.isEntityAttributeDefinition(this)) {
                // if we hit the maxDepth, we are now going back up
                resOpt.depthInfo.currentDepth = currentDepth;
                // now at the top of the chain where max depth does not influence the cache
                if (resOpt.depthInfo.currentDepth === 0) {
                    resOpt.depthInfo.maxDepthExceeded = false;
                }
            }
            if (!inCircularReference && internal_1.isEntityDefinition(this)) {
                // should be removed from the root level only
                // if it is in a circular reference keep it there
                resOpt.currentlyResolvingEntities.delete(this);
            }
            resOpt.inCircularReference = wasInCircularReference;
            // merge child reference symbols set with current
            currSymRefSet.merge(resOpt.symbolRefSet);
            resOpt.symbolRefSet = currSymRefSet;
            this.ctx.corpus.isCurrentlyResolving = wasPreviouslyResolving;
            return rasbResult ? rasbResult.ras : undefined;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    clearTraitCache() {
        // let bodyCode = () =>
        {
            this.traitCache = undefined;
        }
        // return p.measure(bodyCode);
    }
    static protectParameterValues(resOpt, val) {
        if (val) {
            // the value might be a contant entity object, need to protect the original 
            let cEnt = val ? val.explicitReference : undefined;
            if (cEnt) {
                // copy the constant entity AND the reference that holds it
                cEnt = cEnt.copy(resOpt);
                val = val.copy(resOpt);
                val.explicitReference = cEnt;
            }
        }
        return val;
    }
}
exports.CdmObjectBase = CdmObjectBase;
//# sourceMappingURL=CdmObjectBase.js.map