"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var lodash_1 = tslib_1.__importDefault(require("lodash"));
var Core = tslib_1.__importStar(require("@intelligentgraphics/ig.gfx.core"));
/** A package of Multi Articles. */
var Package = /** @class */ (function () {
    /** The constructor */
    function Package(data) {
        this.descendants = new Map();
        this.Registry = data.Registry;
        this.Definitions = data.Definitions;
        this.Instances = data.Instances;
        this.Parents = data.Parents;
        this.Categories = data.Categories;
        this.AttachPoints = data.AttachPoints;
        this.Actions = data.Actions || {};
        this.FilterBehaviour = data.FilterBehaviour || {};
        this.allMultiArticles = lodash_1.default.uniq(lodash_1.default.values(this.Parents));
        for (var key in this.Parents) {
            var parent_1 = this.Parents[key];
            var parentChildren = this.descendants.get(parent_1);
            if (parentChildren === undefined) {
                parentChildren = [key];
                this.descendants.set(parent_1, parentChildren);
            }
            else if (parentChildren.indexOf(key) === -1) {
                parentChildren.push(key);
            }
        }
    }
    /** Get the immediate property keys of the given type.
     * @param type A type.
     * @returns The list of properties.
     */
    Package.prototype.getProperties = function (type) {
        var definition = this.Definitions[type];
        if (Array.isArray(definition)) {
            return definition;
        }
        if (definition === undefined) {
            return [];
        }
        return Object.keys(definition);
    };
    /**
     * Get the information that belongs to the property of the type or undefined if none exist.
     * @param type
     * @param property
     * @returns {IDefinitionInformation}
     */
    Package.prototype.getPropertyInformation = function (type, property) {
        // no type information? fallback
        if (this.Definitions[type] instanceof Array) {
            return undefined;
        }
        // convert strings to enums
        var tDef = this
            .Definitions[type]
            ? this.Definitions[type][property]
            : undefined;
        if (tDef && typeof tDef.Type === "string") {
            tDef.Type = Core.IGXM.EDefinitionType[tDef.Type];
        }
        // invalid type
        if (tDef && tDef.Type === undefined) {
            tDef = undefined;
        }
        // unit may be a custom string or an enum
        if (tDef && tDef.Unit && typeof tDef.Unit === "string") {
            var tUnit = Core.IGXM.EDefinitionUnit[tDef.Unit];
            if (tUnit !== undefined) {
                tDef.Unit = tUnit;
            }
        }
        // parse decimals if it's not a number
        if (tDef && tDef.Decimals && typeof tDef.Decimals !== "number") {
            try {
                tDef.Decimals = parseInt(tDef.Decimals);
            }
            catch (e) {
                tDef.Decimals = undefined;
            }
        }
        return tDef;
    };
    /**
     * Gets all properties of the type that are available
     * @param type
     * @returns {Array}
     */
    Package.prototype.accumulatedProperties = function (type) {
        var e_1, _a;
        var result = new Array();
        var pathParts = this.getPath(type);
        try {
            for (var pathParts_1 = tslib_1.__values(pathParts), pathParts_1_1 = pathParts_1.next(); !pathParts_1_1.done; pathParts_1_1 = pathParts_1.next()) {
                var pathPart = pathParts_1_1.value;
                var tProperties = this.getProperties(pathPart);
                result.push.apply(result, tslib_1.__spread(tProperties));
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (pathParts_1_1 && !pathParts_1_1.done && (_a = pathParts_1.return)) _a.call(pathParts_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return result;
    };
    /** Get the property values for the specified property.
     * @param type A type.
     * @param property A property key.
     * @returns List of collected property values, or null.
     */
    Package.prototype.getPropertyValues = function (type, property) {
        var tInstance = this.Instances[type];
        if (!tInstance) {
            var configuration = this.getConfiguration(type);
            if (configuration[property]) {
                return configuration[property].split("|");
            }
            return [];
        }
        var relevantStates = lodash_1.default.values(tInstance).filter(function (state) { return state && state[property]; });
        var rawValues = relevantStates
            .map(function (state) { return state[property]; })
            .filter(function (x) { return x; });
        return lodash_1.default.flatMap(rawValues, function (raw) { return raw.split("|"); });
    };
    /** Resolve the configuration to go to the next level.
     * @param type A type.
     * @param configuration A configuration state.
     * @param incomplete If set, ignore properties not set in configuration.
     * @returns List of matching types, or null.
     */
    Package.prototype.resolveConfiguration = function (type, configuration, incomplete) {
        var tInstance = this.Instances[type];
        if (!tInstance) {
            return [];
        }
        var tResult = [];
        for (var tType in tInstance) {
            var tState = tInstance[tType];
            var tOk = true;
            for (var tProperty in tState) {
                if (!tOk) {
                    continue;
                }
                if (incomplete && configuration[tProperty] === undefined) {
                    continue;
                }
                tOk = tState[tProperty] === configuration[tProperty];
            }
            if (tOk) {
                tResult.push(tType);
            }
        }
        return tResult;
    };
    /** Get the accumulated configuration state for the given type.
     * @param type Pseudo or real type.
     * @returns The accumulated configuration.
     */
    Package.prototype.getConfiguration = function (type) {
        var e_2, _a;
        var tConfiguration = {};
        var pathParts = this.getPath(type);
        try {
            for (var pathParts_2 = tslib_1.__values(pathParts), pathParts_2_1 = pathParts_2.next(); !pathParts_2_1.done; pathParts_2_1 = pathParts_2.next()) {
                var pathPart = pathParts_2_1.value;
                var tParent = this.Parents[pathPart];
                if (!tParent) {
                    continue;
                }
                var tInstance = this.Instances[tParent];
                if (tInstance) {
                    var tState = tInstance[pathPart];
                    if (tState) {
                        for (var tProperty in tState) {
                            if (tState.hasOwnProperty(tProperty)) {
                                tConfiguration[tProperty] = tState[tProperty];
                            }
                        }
                    }
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (pathParts_2_1 && !pathParts_2_1.done && (_a = pathParts_2.return)) _a.call(pathParts_2);
            }
            finally { if (e_2) throw e_2.error; }
        }
        return tConfiguration;
    };
    /** Get the accumulated categories for the given type.
     * @param type Pseudo or real type.
     * @returns List of categories, each with a validity rule.
     */
    Package.prototype.getCategories = function (type) {
        var e_3, _a;
        var tCategories = {};
        var pathParts = this.getPath(type);
        try {
            for (var pathParts_3 = tslib_1.__values(pathParts), pathParts_3_1 = pathParts_3.next(); !pathParts_3_1.done; pathParts_3_1 = pathParts_3.next()) {
                var pathPart = pathParts_3_1.value;
                var tCats = this.Categories[pathPart];
                if (tCats) {
                    for (var tCategory in tCats) {
                        tCategories[tCategory] = tCats[tCategory];
                    }
                }
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (pathParts_3_1 && !pathParts_3_1.done && (_a = pathParts_3.return)) _a.call(pathParts_3);
            }
            finally { if (e_3) throw e_3.error; }
        }
        return tCategories;
    };
    /**
     * Check if the MA matches the configuration.
     * That is, if the MA implements all set values of the configuration the same way.
     * @param type
     * @param configuration
     * @returns {boolean}
     */
    Package.prototype.matchesConfiguration = function (type, configuration) {
        var articleConfiguration = this.getConfiguration(type);
        var configurationKeys = Object.getOwnPropertyNames(configuration);
        for (var index = 0; index < configurationKeys.length; index++) {
            var key = configurationKeys[index];
            var value = configuration[key];
            if (value === "" || value === undefined) {
                continue;
            }
            var articleValue = articleConfiguration[key];
            if (articleValue !== value &&
                (articleValue === undefined ||
                    articleValue.split("|").indexOf(value) === -1)) {
                return false;
            }
        }
        return true;
    };
    /**
     * Check if the MA matches the configuration, but remove own properties from the configuration first.
     * @param type
     * @param configuration
     * @returns {boolean}
     */
    Package.prototype.matchesConfigExceptForOwnProperties = function (type, configuration) {
        var articleConfiguration = this.getConfiguration(type);
        var articleConfigurationKeys = Object.getOwnPropertyNames(articleConfiguration);
        var partialConfiguration = {};
        var nonArticleProperties = tslib_1.__assign({}, articleConfiguration);
        for (var index = 0; index < articleConfigurationKeys.length; index++) {
            var key = articleConfigurationKeys[index];
            partialConfiguration[key] = configuration[key];
            delete nonArticleProperties[key];
        }
        var shouldBeOwnProperties = Object.getOwnPropertyNames(nonArticleProperties);
        var props = this.accumulatedProperties(type);
        var areAllOwnProperties = shouldBeOwnProperties.every(function (prop) {
            return lodash_1.default.includes(props, prop);
        });
        return (areAllOwnProperties &&
            this.matchesConfiguration(type, partialConfiguration));
    };
    /** Get the accumulated attachment points for the given type.
     * @param type Pseudo or real type.
     * @returns List of attachment points.
     */
    Package.prototype.getAttachPoints = function (type) {
        var e_4, _a;
        var tAttachPoints = [];
        var pathParts = this.getPath(type);
        try {
            for (var pathParts_4 = tslib_1.__values(pathParts), pathParts_4_1 = pathParts_4.next(); !pathParts_4_1.done; pathParts_4_1 = pathParts_4.next()) {
                var pathPart = pathParts_4_1.value;
                var tPoints = this.AttachPoints[pathPart];
                if (!lodash_1.default.isEmpty(tPoints)) {
                    tPoints.forEach(function (attachPoint) {
                        tAttachPoints.push(attachPoint);
                    });
                }
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (pathParts_4_1 && !pathParts_4_1.done && (_a = pathParts_4.return)) _a.call(pathParts_4);
            }
            finally { if (e_4) throw e_4.error; }
        }
        return tAttachPoints;
    };
    Package.prototype.getRootAncestor = function (type) {
        return this.getPath(type)[0];
    };
    /** Get the path of the passed type.
     * @param type Pseudo or real type.
     * @returns Path from top to down.
     */
    Package.prototype.getPath = function (type) {
        var tPath = [type];
        var tType = type;
        var tParent = this.Parents[tType];
        while (tParent) {
            tPath.unshift(tParent);
            tParent = this.Parents[tParent];
        }
        return tPath;
    };
    Package.prototype.isDescendant = function (descendant, type) {
        return this.getPath(descendant).indexOf(type) !== -1;
    };
    /**
     * Gets all children of a Multi Article, that is all MA that have this as their parent.
     * @param type
     * @returns {string[]}
     */
    Package.prototype.getChildren = function (type) {
        return this.descendants.get(type) || [];
    };
    /**
     * Get all the descendants, i.e.
     * @param type
     */
    Package.prototype.getDescendants = function (type) {
        var _this = this;
        var descendants = [];
        var collectChildren = function (type) {
            var children = _this.getChildren(type);
            for (var index = 0; index < children.length; index++) {
                var child = children[index];
                descendants.push(child);
                collectChildren(child);
            }
        };
        collectChildren(type);
        return descendants;
    };
    /**
     * Get all multi articles, without concrete articles
     * @returns {T[]}
     */
    Package.prototype.getAllMultiArticles = function () {
        return this.allMultiArticles;
    };
    /**
     * Get all concrete articles
     * @returns {string[]}
     */
    Package.prototype.getAllConcreteArticles = function () {
        var multiArticles = this.getAllMultiArticles();
        return Object.getOwnPropertyNames(this.Parents).filter(function (article) { return !lodash_1.default.includes(multiArticles, article); });
    };
    Package.prototype.isConcreteArticle = function (type) {
        var nonConcreteArticles = this.getAllMultiArticles();
        return type in this.Parents && nonConcreteArticles.indexOf(type) === -1;
    };
    /**
     * Get all root artilces.
     * @returns {string[]}
     */
    Package.prototype.getRootMultiArticles = function () {
        var _this = this;
        return this.getAllMultiArticles().filter(function (article) { return _this.getPath(article).length === 1; });
    };
    Package.prototype.getActions = function (type) {
        var e_5, _a;
        var tActions = [];
        var pathParts = this.getPath(type);
        try {
            for (var pathParts_5 = tslib_1.__values(pathParts), pathParts_5_1 = pathParts_5.next(); !pathParts_5_1.done; pathParts_5_1 = pathParts_5.next()) {
                var pathPart = pathParts_5_1.value;
                var actions = this.Actions[pathPart] || [];
                tActions.push.apply(tActions, tslib_1.__spread(actions));
            }
        }
        catch (e_5_1) { e_5 = { error: e_5_1 }; }
        finally {
            try {
                if (pathParts_5_1 && !pathParts_5_1.done && (_a = pathParts_5.return)) _a.call(pathParts_5);
            }
            finally { if (e_5) throw e_5.error; }
        }
        return tActions;
    };
    return Package;
}());
exports.Package = Package;
