"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.CdmTraitDefinition = void 0;
const internal_1 = require("../internal");
class CdmTraitDefinition extends internal_1.CdmObjectDefinitionBase {
    constructor(ctx, name, extendsTrait) {
        super(ctx);
        this.TAG = CdmTraitDefinition.name;
        // let bodyCode = () =>
        {
            this.hasSetFlags = false;
            this.objectType = internal_1.cdmObjectType.traitDef;
            this.traitName = name;
            this.extendsTrait = extendsTrait;
        }
        // return p.measure(bodyCode);
    }
    static get objectType() {
        return internal_1.cdmObjectType.traitDef;
    }
    get parameters() {
        if (!this._parameters) {
            this._parameters = new internal_1.CdmCollection(this.ctx, this, internal_1.cdmObjectType.parameterDef);
        }
        return this._parameters;
    }
    getObjectType() {
        // let bodyCode = () =>
        {
            return internal_1.cdmObjectType.traitDef;
        }
        // return p.measure(bodyCode);
    }
    copy(resOpt, host) {
        // let bodyCode = () =>
        {
            if (!resOpt) {
                resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
            }
            let copy;
            if (!host) {
                copy = new CdmTraitDefinition(this.ctx, this.traitName, undefined);
            }
            else {
                copy = host;
                copy.traitName = this.traitName;
            }
            copy.extendsTrait = this.extendsTrait ? this.extendsTrait.copy(resOpt) : undefined;
            copy.allParameters = undefined;
            copy.elevated = this.elevated;
            copy.ugly = this.ugly;
            copy.associatedProperties = this.associatedProperties ? this.associatedProperties.slice() : undefined;
            this.copyDef(resOpt, copy);
            return copy;
        }
        // return p.measure(bodyCode);
    }
    validate() {
        // let bodyCode = () =>
        {
            if (!this.traitName) {
                let missingFields = ['traitName'];
                internal_1.Logger.error(this.ctx, this.TAG, this.validate.name, this.atCorpusPath, internal_1.cdmLogCode.ErrValdnIntegrityCheckFailure, missingFields.map((s) => `'${s}'`).join(', '), this.atCorpusPath);
                return false;
            }
            return true;
        }
        // return p.measure(bodyCode);
    }
    getName() {
        // let bodyCode = () =>
        {
            return this.traitName;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    fetchExtendsTrait() {
        // let bodyCode = () =>
        {
            return this.extendsTrait;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    hasParameterDefs() {
        // let bodyCode = () =>
        {
            return this.parameters;
        }
        // return p.measure(bodyCode);
    }
    isDerivedFrom(base, resOpt) {
        // let bodyCode = () =>
        {
            if (!resOpt) {
                resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
            }
            if (base === this.traitName) {
                return true;
            }
            return this.isDerivedFromDef(resOpt, this.extendsTrait, this.traitName, base);
        }
        // return p.measure(bodyCode);
    }
    visit(pathFrom, preChildren, postChildren) {
        // let bodyCode = () =>
        {
            const path = this.fetchDeclaredPath(pathFrom);
            if (preChildren && preChildren(this, path)) {
                return false;
            }
            if (this.extendsTrait) {
                this.extendsTrait.owner = this;
                if (this.extendsTrait.visit(`${path}/extendsTrait/`, preChildren, postChildren)) {
                    return true;
                }
            }
            if (this.parameters) {
                if (this.parameters.visitArray(`${path}/hasParameters/`, preChildren, postChildren)) {
                    return true;
                }
            }
            if (postChildren && postChildren(this, path)) {
                return true;
            }
            return false;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    fetchResolvedTraits(resOpt) {
        // let bodyCode = () =>
        {
            if (!resOpt) {
                resOpt = new internal_1.resolveOptions(this, this.ctx.corpus.defaultResolutionDirectives);
            }
            const kind = 'rtsb';
            const ctx = this.ctx;
            // this may happen 0, 1 or 2 times. so make it fast
            let baseTrait;
            let baseRts;
            let baseValues;
            const getBaseInfo = () => {
                if (this.extendsTrait) {
                    baseTrait = this.extendsTrait.fetchObjectDefinition(resOpt);
                    if (baseTrait) {
                        baseRts = this.extendsTrait.fetchResolvedTraits(resOpt);
                        if (baseRts && baseRts.size === 1) {
                            const basePv = baseRts.get(baseTrait) ? baseRts.get(baseTrait).parameterValues : undefined;
                            if (basePv) {
                                baseValues = basePv.values;
                            }
                        }
                    }
                }
            };
            // see if one is already cached
            // if this trait has parameters, then the base trait found through the reference might be a different reference
            // because trait references are unique per argument value set. so use the base as a part of the cache tag
            // since it is expensive to figure out the extra tag, cache that too!
            if (this.baseIsKnownToHaveParameters === undefined) {
                getBaseInfo();
                // is a cache tag needed? then make one
                this.baseIsKnownToHaveParameters = false;
                if (baseValues && baseValues.length > 0) {
                    this.baseIsKnownToHaveParameters = true;
                }
            }
            let cacheTagExtra = '';
            if (this.baseIsKnownToHaveParameters) {
                cacheTagExtra = this.extendsTrait.ID.toString();
            }
            let cacheTag = ctx.corpus.createDefinitionCacheTag(resOpt, this, kind, cacheTagExtra);
            let rtsResult = cacheTag ? ctx.traitCache.get(cacheTag) : undefined;
            // store the previous reference 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 not, then make one and save it
            if (!rtsResult) {
                getBaseInfo();
                if (baseTrait) {
                    // get the resolution of the base class and use the values as a starting point for this trait's values
                    if (this.hasSetFlags === false) {
                        // inherit these flags
                        if (this.elevated === undefined) {
                            this.elevated = baseTrait.elevated;
                        }
                        if (this.ugly === undefined) {
                            this.ugly = baseTrait.ugly;
                        }
                        if (this.associatedProperties === undefined) {
                            this.associatedProperties = baseTrait.associatedProperties;
                        }
                    }
                }
                this.hasSetFlags = true;
                const parameterCollection = this.fetchAllParameters(resOpt);
                const argumentValues = [];
                const wasSet = [];
                this.thisIsKnownToHaveParameters = (parameterCollection.sequence.length > 0);
                for (let i = 0; i < parameterCollection.sequence.length; i++) {
                    // either use the default value or (higher precidence) the value taken from the base reference
                    let value = parameterCollection.sequence[i].defaultValue;
                    let baseValue;
                    if (baseValues && i < baseValues.length) {
                        baseValue = baseValues[i];
                        if (baseValue) {
                            value = baseValue;
                        }
                    }
                    argumentValues.push(value);
                    wasSet.push(false);
                }
                // save it
                const resTrait = new internal_1.ResolvedTrait(this, parameterCollection, argumentValues, wasSet);
                rtsResult = new internal_1.ResolvedTraitSet(resOpt);
                rtsResult.merge(resTrait, false);
                // register set of possible symbols
                ctx.corpus.registerDefinitionReferenceSymbols(this.fetchObjectDefinition(resOpt), kind, resOpt.symbolRefSet);
                // get the new cache tag now that we have the list of docs
                cacheTag = ctx.corpus.createDefinitionCacheTag(resOpt, this, kind, cacheTagExtra);
                if (cacheTag) {
                    ctx.traitCache.set(cacheTag, rtsResult);
                }
            }
            else {
                // cache found
                // get the SymbolSet for this cached object
                const key = internal_1.CdmCorpusDefinition.createCacheKeyFromObject(this, kind);
                resOpt.symbolRefSet = ctx.corpus.definitionReferenceSymbols.get(key);
            }
            // merge child document set with current
            currSymRefSet.merge(resOpt.symbolRefSet);
            resOpt.symbolRefSet = currSymRefSet;
            return rtsResult;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    constructResolvedTraits(rtsb, resOpt) {
        // traits don't have traits.
    }
    /**
     * @internal
     */
    fetchAllParameters(resOpt) {
        // let bodyCode = () =>
        {
            if (this.allParameters) {
                return this.allParameters;
            }
            // get parameters from base if there is one
            let prior;
            if (this.extendsTrait) {
                prior = this.fetchExtendsTrait()
                    .fetchObjectDefinition(resOpt)
                    .fetchAllParameters(resOpt);
            }
            this.allParameters = new internal_1.ParameterCollection(prior);
            if (this.parameters) {
                this.parameters.allItems.forEach((element) => {
                    this.allParameters.add(element);
                });
            }
            return this.allParameters;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    constructResolvedAttributes(resOpt, under) {
        // let bodyCode = () =>
        {
            return undefined;
        }
        // return p.measure(bodyCode);
    }
}
exports.CdmTraitDefinition = CdmTraitDefinition;
//# sourceMappingURL=CdmTraitDefinition.js.map