import { __assign, __read, __spread, __values } from "tslib";
import { IGXE } from "@intelligentgraphics/igd.productdata";
import { Obj, isValue } from "@intelligentgraphics/ig.utilities";
/**
 * Creates a single group for each of the provided articles
 *
 * @param {*} sources
 * @returns
 */
export var byComId = function (sources) {
    var e_1, _a, e_2, _b;
    var propertyGroups = [];
    try {
        for (var sources_1 = __values(sources), sources_1_1 = sources_1.next(); !sources_1_1.done; sources_1_1 = sources_1.next()) {
            var articles = sources_1_1.value;
            try {
                for (var articles_1 = (e_2 = void 0, __values(articles)), articles_1_1 = articles_1.next(); !articles_1_1.done; articles_1_1 = articles_1.next()) {
                    var _c = articles_1_1.value, properties = _c.properties, comId = _c.comId;
                    propertyGroups.push({
                        groupId: comId,
                        properties: properties,
                        references: comId,
                        groupLabel: defaultCreateLabel(articles[0], properties),
                        isMerged: false,
                    });
                }
            }
            catch (e_2_1) { e_2 = { error: e_2_1 }; }
            finally {
                try {
                    if (articles_1_1 && !articles_1_1.done && (_b = articles_1.return)) _b.call(articles_1);
                }
                finally { if (e_2) throw e_2.error; }
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (sources_1_1 && !sources_1_1.done && (_a = sources_1.return)) _a.call(sources_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
    return propertyGroups;
};
/**
 * Creates a common item for each source id
 *
 * @param {Article[][]} sources
 * @returns
 */
export var bySource = function (sources) {
    return buildPropertyGroups({ sources: sources });
};
// /**
//  * Creates a common item for each unique articleId
//  *
//  * @param {any} articles
//  * @returns
//  */
// export const byArticleId: PropertyGrouper = sources => {
// 	return [];
// };
export var GroupPropertyMergeBehaviour;
(function (GroupPropertyMergeBehaviour) {
    /**
     * Adds all non conflicting properties to a merge group
     */
    GroupPropertyMergeBehaviour["union"] = "union";
    /**
     * Only adds propeties which exist on all group articles to the merged group
     */
    GroupPropertyMergeBehaviour["intersection"] = "intersection";
})(GroupPropertyMergeBehaviour || (GroupPropertyMergeBehaviour = {}));
export var ChoiceListMergeBehaviour;
(function (ChoiceListMergeBehaviour) {
    /**
     * Allows merged property value lists to
     * contain an intersection of the value lists of multiple properties
     */
    ChoiceListMergeBehaviour["allowPartialValueList"] = "allowPartialValueList";
    /**
     * Only allows merged properties when a value list matches in length and content
     */
    ChoiceListMergeBehaviour["onlyCompleteValueList"] = "onlyCompleteValueList";
})(ChoiceListMergeBehaviour || (ChoiceListMergeBehaviour = {}));
export var GroupContent;
(function (GroupContent) {
    /**
     * Create merged groups and a group for each article with all of its properties
     */
    GroupContent["all"] = "all";
    /**
     * Only create groups for merged properties (will create a group for an article if the article count equals one)
     */
    GroupContent["onlyMerged"] = "onlyMerged";
    /**
     * Create merged groups and a group for each article containing the non merged properties
     */
    GroupContent["mergedAndRest"] = "mergedAndRest";
})(GroupContent || (GroupContent = {}));
export var PropertySortMode;
(function (PropertySortMode) {
    /**
     * Sorts properties alphabetically by their text property
     */
    PropertySortMode["alphabetical"] = "alphabetical";
    /**
     * Sorts properties by their position property
     */
    PropertySortMode["position"] = "position";
    /**
     * Sorts properties by their position property and if in ambigous cases alphabetical
     */
    PropertySortMode["positionThenAlphabetical"] = "positionThenAlphabetical";
})(PropertySortMode || (PropertySortMode = {}));
export var buildPropertyGroups = function (_a) {
    var e_3, _b, e_4, _c;
    var sources = _a.sources, _d = _a.mergeBehaviour, mergeBehaviour = _d === void 0 ? GroupPropertyMergeBehaviour.union : _d, _e = _a.displayMode, displayMode = _e === void 0 ? GroupContent.all : _e, _f = _a.createLabel, createLabel = _f === void 0 ? defaultCreateLabel : _f, _g = _a.mergeProperties, mergeProperties = _g === void 0 ? defaultMergeProperties : _g, _h = _a.choiceListMergeBehaviour, choiceListMergeBehaviour = _h === void 0 ? ChoiceListMergeBehaviour.allowPartialValueList : _h, _j = _a.minGroupPropertyReferenceCount, minGroupPropertyReferenceCount = _j === void 0 ? 1 : _j, _k = _a.groupPropertySorting, groupPropertySorting = _k === void 0 ? false : _k, _l = _a.sortGroupProperties, sortGroupProperties = _l === void 0 ? defaultSortProperties : _l;
    var groups = [];
    var mergePropertiesSettings = {
        choiceListMergeBehaviour: choiceListMergeBehaviour,
    };
    var _loop_1 = function (articles) {
        var e_5, _a, e_6, _b, e_7, _c, e_8, _d;
        if (!articles.length) {
            return "continue";
        }
        if (articles.length === 1) {
            groups.push({
                groupId: articles[0].comId,
                references: articles[0].comId,
                groupLabel: createLabel(articles[0], articles[0].properties),
                properties: articles[0].properties,
                isMerged: false,
            });
            return "continue";
        }
        var sourceGroups = [];
        var _e = __read(articles, 1), templateArticle = _e[0];
        var commonSourceItem = {
            groupId: templateArticle.source,
            groupLabel: undefined,
            references: [],
            properties: [],
            isMerged: true,
        };
        // list of all properties for this group. null means some merging failed and the property should not be used
        var groupProperties = new Map();
        var referenceCounter = new Map();
        try {
            for (var articles_2 = (e_5 = void 0, __values(articles)), articles_2_1 = articles_2.next(); !articles_2_1.done; articles_2_1 = articles_2.next()) {
                var article = articles_2_1.value;
                try {
                    for (var _f = (e_6 = void 0, __values(article.properties)), _g = _f.next(); !_g.done; _g = _f.next()) {
                        var prop = _g.value;
                        var groupProperty = groupProperties.get(prop.Key);
                        // property key was already declared as non mergeable, so this property should be skipped
                        if (groupProperty === null) {
                            continue;
                        }
                        // property key did not yet exist, add it
                        if (groupProperty === undefined) {
                            // create a copy of the original property, so we can manipulate its values and subproperties, without manipulating the original property
                            groupProperties.set(prop.Key, __assign(__assign({}, prop), { SubProperties: Obj.deepClone(prop.SubProperties) }));
                            referenceCounter.set(prop.Key, 1);
                            continue;
                        }
                        var mergeResult = mergeProperties(groupProperty, prop, mergePropertiesSettings);
                        // property could not be merged. remove it from the referenceCounter and set the groupproperty to null,
                        // so it can be ignored later
                        if (mergeResult === false) {
                            referenceCounter.delete(prop.Key);
                            groupProperties.set(prop.Key, null);
                            continue;
                        }
                        referenceCounter.set(prop.Key, referenceCounter.get(prop.Key) + 1);
                        groupProperties.set(prop.Key, mergeResult);
                    }
                }
                catch (e_6_1) { e_6 = { error: e_6_1 }; }
                finally {
                    try {
                        if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
                    }
                    finally { if (e_6) throw e_6.error; }
                }
                commonSourceItem.references.push(article.comId);
                if (displayMode === GroupContent.all) {
                    // add article itself as a group
                    sourceGroups.push({
                        groupId: article.comId,
                        groupLabel: createLabel(article, article.properties),
                        properties: article.properties,
                        references: article.comId,
                        isMerged: false,
                    });
                }
            }
        }
        catch (e_5_1) { e_5 = { error: e_5_1 }; }
        finally {
            try {
                if (articles_2_1 && !articles_2_1.done && (_a = articles_2.return)) _a.call(articles_2);
            }
            finally { if (e_5) throw e_5.error; }
        }
        commonSourceItem.properties = Array.from(groupProperties.values()).filter(isValue);
        if (mergeBehaviour === GroupPropertyMergeBehaviour.intersection) {
            minGroupPropertyReferenceCount = articles.length;
        }
        if (minGroupPropertyReferenceCount > 1) {
            commonSourceItem.properties = commonSourceItem.properties.filter(function (prop) {
                if (referenceCounter.get(prop.Key) >=
                    minGroupPropertyReferenceCount) {
                    return true;
                }
                groupProperties.set(prop.Key, null);
                return false;
            });
        }
        if (displayMode === GroupContent.mergedAndRest) {
            try {
                // create new groups for each article with an unmerged group
                for (var articles_3 = (e_7 = void 0, __values(articles)), articles_3_1 = articles_3.next(); !articles_3_1.done; articles_3_1 = articles_3.next()) {
                    var article = articles_3_1.value;
                    var nonMergedProperties = article.properties.filter(function (property) { return groupProperties.get(property.Key) === null; });
                    if (nonMergedProperties.length > 0) {
                        sourceGroups.push({
                            groupId: article.comId,
                            properties: nonMergedProperties,
                            references: article.comId,
                            groupLabel: createLabel(article, nonMergedProperties),
                            isMerged: false,
                        });
                    }
                }
            }
            catch (e_7_1) { e_7 = { error: e_7_1 }; }
            finally {
                try {
                    if (articles_3_1 && !articles_3_1.done && (_c = articles_3.return)) _c.call(articles_3);
                }
                finally { if (e_7) throw e_7.error; }
            }
        }
        commonSourceItem.groupLabel = createLabel(commonSourceItem.references.map(function (comId) { return articles.find(function (item) { return item.comId === comId; }); }), commonSourceItem.properties);
        sourceGroups.unshift(commonSourceItem);
        try {
            for (var sourceGroups_1 = (e_8 = void 0, __values(sourceGroups)), sourceGroups_1_1 = sourceGroups_1.next(); !sourceGroups_1_1.done; sourceGroups_1_1 = sourceGroups_1.next()) {
                var group = sourceGroups_1_1.value;
                if (group.properties.length) {
                    groups.push(group);
                }
            }
        }
        catch (e_8_1) { e_8 = { error: e_8_1 }; }
        finally {
            try {
                if (sourceGroups_1_1 && !sourceGroups_1_1.done && (_d = sourceGroups_1.return)) _d.call(sourceGroups_1);
            }
            finally { if (e_8) throw e_8.error; }
        }
    };
    try {
        for (var sources_2 = __values(sources), sources_2_1 = sources_2.next(); !sources_2_1.done; sources_2_1 = sources_2.next()) {
            var articles = sources_2_1.value;
            _loop_1(articles);
        }
    }
    catch (e_3_1) { e_3 = { error: e_3_1 }; }
    finally {
        try {
            if (sources_2_1 && !sources_2_1.done && (_b = sources_2.return)) _b.call(sources_2);
        }
        finally { if (e_3) throw e_3.error; }
    }
    if (groupPropertySorting) {
        try {
            for (var groups_1 = __values(groups), groups_1_1 = groups_1.next(); !groups_1_1.done; groups_1_1 = groups_1.next()) {
                var group = groups_1_1.value;
                group.properties = sortGroupProperties(group.properties, groupPropertySorting, group);
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (groups_1_1 && !groups_1_1.done && (_c = groups_1.return)) _c.call(groups_1);
            }
            finally { if (e_4) throw e_4.error; }
        }
    }
    return groups;
};
export var defaultCreateLabel = function (input) {
    if (Array.isArray(input)) {
        if (input.length === 0) {
            return "";
        }
        var _a = __read(input, 1), firstItem = _a[0];
        return [
            firstItem.info.manufacturerName,
            firstItem.info.programName,
        ].join(" ");
    }
    var info = input.info, articleId = input.articleId;
    if (info.name) {
        return info.name;
    }
    if (info.shortText.length) {
        return info.shortText.join(" ");
    }
    return articleId;
};
export var defaultMergeProperties = function (a, b, _a) {
    var choiceListchoiceListMergeBehaviour = _a.choiceListMergeBehaviour;
    if (a.ValueType !== b.ValueType || a.Type !== b.Type) {
        return false;
    }
    if (a.Type === "toggle") {
        a.Value = a.Value === b.Value ? a.Value : undefined;
        return a;
    }
    if (a.Type === "choiceList") {
        return mergeChoiceListProperties(a, b, choiceListchoiceListMergeBehaviour);
    }
    return mergeNormalProperties(a, b);
};
export var mergeNormalProperties = function (a, b) {
    if (a.Value !== b.Value) {
        a.Value = undefined;
        a.Values = [];
    }
    if (a.ValueType === IGXE.EPropertyType.Int ||
        a.ValueType === IGXE.EPropertyType.Float) {
        if (a.ValueRangeFrom !== undefined || b.ValueRangeFrom !== undefined) {
            a.ValueRangeFrom = Math.max(a.ValueRangeFrom || 0, b.ValueRangeFrom || 0);
            a.ValueRangeTo = Math.min(a.ValueRangeTo || Infinity, b.ValueRangeTo || Infinity);
            if (a.ValueRangeTo < a.ValueRangeFrom) {
                return false;
            }
        }
    }
    return a;
};
export var mergeChoiceListProperties = function (a, b, mergeBehaviour) {
    var e_9, _a, e_10, _b, e_11, _c, e_12, _d;
    if (a.Value !== b.Value) {
        a.Value = undefined;
    }
    if (a.ValueType === IGXE.EPropertyType.Bool) {
        return a;
    }
    var mergedValues = a.Values.concat(b.Values);
    var referenceCounter = new Map();
    try {
        for (var mergedValues_1 = __values(mergedValues), mergedValues_1_1 = mergedValues_1.next(); !mergedValues_1_1.done; mergedValues_1_1 = mergedValues_1.next()) {
            var value = mergedValues_1_1.value;
            var stringifiedValue = value.Value.toString();
            referenceCounter.set(stringifiedValue, (referenceCounter.get(stringifiedValue) || 0) + 1);
        }
    }
    catch (e_9_1) { e_9 = { error: e_9_1 }; }
    finally {
        try {
            if (mergedValues_1_1 && !mergedValues_1_1.done && (_a = mergedValues_1.return)) _a.call(mergedValues_1);
        }
        finally { if (e_9) throw e_9.error; }
    }
    var newValues = [];
    var _loop_2 = function (value, references) {
        if (references < 2) {
            if (mergeBehaviour ===
                ChoiceListMergeBehaviour.onlyCompleteValueList) {
                return { value: false };
            }
            return "continue";
        }
        newValues.push(mergedValues.find(function (val) { return val.Value.toString() === value; }));
    };
    try {
        for (var referenceCounter_1 = __values(referenceCounter), referenceCounter_1_1 = referenceCounter_1.next(); !referenceCounter_1_1.done; referenceCounter_1_1 = referenceCounter_1.next()) {
            var _e = __read(referenceCounter_1_1.value, 2), value = _e[0], references = _e[1];
            var state_1 = _loop_2(value, references);
            if (typeof state_1 === "object")
                return state_1.value;
        }
    }
    catch (e_10_1) { e_10 = { error: e_10_1 }; }
    finally {
        try {
            if (referenceCounter_1_1 && !referenceCounter_1_1.done && (_b = referenceCounter_1.return)) _b.call(referenceCounter_1);
        }
        finally { if (e_10) throw e_10.error; }
    }
    a.Values = newValues;
    var subPropertyCollection = new Map();
    var allProperties = __spread(a.SubProperties, b.SubProperties);
    try {
        for (var allProperties_1 = __values(allProperties), allProperties_1_1 = allProperties_1.next(); !allProperties_1_1.done; allProperties_1_1 = allProperties_1.next()) {
            var property = allProperties_1_1.value;
            var collection = subPropertyCollection.get(property.Key);
            if (collection === undefined) {
                collection = [];
                subPropertyCollection.set(property.Key, collection);
            }
            collection.push(property);
        }
    }
    catch (e_11_1) { e_11 = { error: e_11_1 }; }
    finally {
        try {
            if (allProperties_1_1 && !allProperties_1_1.done && (_c = allProperties_1.return)) _c.call(allProperties_1);
        }
        finally { if (e_11) throw e_11.error; }
    }
    var newSubProperties = [];
    try {
        for (var subPropertyCollection_1 = __values(subPropertyCollection), subPropertyCollection_1_1 = subPropertyCollection_1.next(); !subPropertyCollection_1_1.done; subPropertyCollection_1_1 = subPropertyCollection_1.next()) {
            var _f = __read(subPropertyCollection_1_1.value, 2), properties = _f[1];
            if (properties.length !== 2) {
                if (mergeBehaviour ===
                    ChoiceListMergeBehaviour.onlyCompleteValueList) {
                    return false;
                }
                continue;
            }
            var mergedProperty = mergeChoiceListProperties(properties[0], properties[1], mergeBehaviour);
            if (!mergedProperty &&
                mergeBehaviour === ChoiceListMergeBehaviour.onlyCompleteValueList) {
                return false;
            }
            if (mergedProperty) {
                newSubProperties.push(mergedProperty);
            }
        }
    }
    catch (e_12_1) { e_12 = { error: e_12_1 }; }
    finally {
        try {
            if (subPropertyCollection_1_1 && !subPropertyCollection_1_1.done && (_d = subPropertyCollection_1.return)) _d.call(subPropertyCollection_1);
        }
        finally { if (e_12) throw e_12.error; }
    }
    a.SubProperties = newSubProperties;
    if (!a.Values.length && !a.SubProperties.length) {
        return false;
    }
    return a;
};
export var defaultSortProperties = function (properties, sortMode) {
    if (properties.length === 0) {
        return properties;
    }
    switch (sortMode) {
        case PropertySortMode.alphabetical:
            return properties.sort(comparePropertiesAlphabetically);
        case PropertySortMode.position:
            return properties.sort(comparePropertyPosition);
        case PropertySortMode.positionThenAlphabetical:
            return properties.sort(function (a, b) {
                var byPosition = comparePropertyPosition(a, b);
                if (byPosition !== 0) {
                    return byPosition;
                }
                return comparePropertiesAlphabetically(a, b);
            });
    }
    return properties;
};
var comparePropertiesAlphabetically = function (a, b) {
    var aText = a.Text.join(" ");
    var bText = b.Text.join(" ");
    return aText.localeCompare(bText);
};
var comparePropertyPosition = function (a, b) {
    if (a.Position === undefined) {
        if (b.Position === undefined) {
            return 0;
        }
        return 1;
    }
    if (b.Position === undefined) {
        return -1;
    }
    return a.Position - b.Position;
};
