import { __decorate } from "tslib";
import * as Core from "@intelligentgraphics/ig.gfx.core";
import { generateId } from "@intelligentgraphics/ig.utilities";
import { IGXE } from "@intelligentgraphics/igd.core";
import Logger from "../Logger";
export var EIGXEProductDataManagerDebugInfo;
(function (EIGXEProductDataManagerDebugInfo) {
    EIGXEProductDataManagerDebugInfo[EIGXEProductDataManagerDebugInfo["All"] = 1] = "All";
    EIGXEProductDataManagerDebugInfo[EIGXEProductDataManagerDebugInfo["Condition"] = 2] = "Condition";
    EIGXEProductDataManagerDebugInfo[EIGXEProductDataManagerDebugInfo["Properties"] = 3] = "Properties";
    EIGXEProductDataManagerDebugInfo[EIGXEProductDataManagerDebugInfo["ArticlePolymorphy"] = 4] = "ArticlePolymorphy";
    EIGXEProductDataManagerDebugInfo[EIGXEProductDataManagerDebugInfo["Init"] = 5] = "Init";
})(EIGXEProductDataManagerDebugInfo || (EIGXEProductDataManagerDebugInfo = {}));
var ECheckConditionResult;
(function (ECheckConditionResult) {
    ECheckConditionResult[ECheckConditionResult["Invalid"] = 1] = "Invalid";
    ECheckConditionResult[ECheckConditionResult["Valid"] = 2] = "Valid";
    ECheckConditionResult[ECheckConditionResult["Unresolvable"] = 3] = "Unresolvable";
})(ECheckConditionResult || (ECheckConditionResult = {}));
const CheckComId = (target, propertyKey, descriptor) => {
    const tOgFunction = descriptor.value;
    descriptor.value = function (comId, ...args) {
        if (!this["basket"][comId]) {
            return Promise.resolve(undefined);
        }
        return tOgFunction.call(this, comId, ...args);
    };
};
/**
 * IGXEProductDataManager is an implementation of the IProductDataManager interface
 * that uses the IGD.IGXE product data format as data source.
 */
export class IGXEProductDataManager {
    constructor(config) {
        this.pd = config.nativeData;
        this.ipPropsTop =
            config.pushInterProductPropertysToTop === undefined
                ? false
                : config.pushInterProductPropertysToTop;
        this.basket = {};
        this.dbgInfo = config.debugInfos || [];
        this.debug(EIGXEProductDataManagerDebugInfo.Init, "initialized IGXEProductDataManager", config);
        config.additionalData = config.additionalData || [];
        config.additionalData.forEach(ad => {
            this.mergeIGXE(this.pd, ad);
        });
        this.debug(EIGXEProductDataManagerDebugInfo.Init, "merged all pdata", this.pd);
    }
    is() {
        return false;
    }
    async addArticle(articleNo, initialConfiguration, source) {
        const tArticleId = articleNo.baseArticleNumber;
        if (!this.pd.Articles[tArticleId]) {
            throw new Error("Unknown article " + tArticleId);
        }
        try {
            const uid = generateId();
            if (typeof initialConfiguration === "string") {
                const tAC = {};
                for (const tK in this.basket[initialConfiguration]
                    .articleConfiguration) {
                    tAC[tK] = this.basket[initialConfiguration].articleConfiguration[tK].Value;
                }
                initialConfiguration = tAC;
            }
            const tNewAc = await this._intGetArticleConfiguration(tArticleId, {}, initialConfiguration === undefined
                ? {}
                : IGXE.createArticleConfigurationValuesFromChanges(initialConfiguration));
            this.basket[uid] = {
                articleID: tArticleId,
                articleConfiguration: tNewAc,
                source: source,
            };
            return uid;
        }
        catch (err) {
            throw new Error("Initial configuration error " + err);
        }
    }
    removeArticle(comID) {
        delete this.basket[comID];
        return Promise.resolve(true);
    }
    duplicateArticle(comID) {
        const tUID = generateId();
        this.basket[tUID] = {
            articleID: this.basket[comID].articleID,
            articleConfiguration: this.basket[comID].articleConfiguration,
            source: this.basket[comID],
        };
        return Promise.resolve(tUID);
    }
    getArticleType(comId) {
        return Promise.resolve("graphical");
    }
    getArticleID(comID) {
        return Promise.resolve(this.basket[comID].articleID);
    }
    getArticleName(comId) {
        return Promise.resolve(undefined);
    }
    getArticleShortText(comID) {
        return Promise.resolve(this.pd.Articles[this.basket[comID].articleID].ShortText);
    }
    getArticleLongText(comID) {
        return Promise.resolve(this.pd.Articles[this.basket[comID].articleID].LongText);
    }
    async getArticleVariantText(comID) {
        const tR = new Array();
        const articleConfiguration = this.basket[comID].articleConfiguration;
        const tList = await this.getArticlePropertyKeys(comID);
        if (tList.length) {
            tList.forEach(pkey => {
                const tProp = this.getProperty(pkey);
                if (tProp && tProp.InVarCode) {
                    const value = articleConfiguration[pkey];
                    if (value === undefined || value.Text === undefined) {
                        return;
                    }
                    tR.push({
                        key: tProp.Text.join(" "),
                        value: value.Text.join(" "),
                    });
                }
            });
        }
        return tR;
    }
    getArticleVariantCode(comID) {
        let tR = "";
        return this.getArticlePropertyKeys(comID).then(list => {
            if (list.length) {
                list.forEach(pkey => {
                    const tProp = this.getProperty(pkey);
                    if (tProp && tProp.InVarCode) {
                        if (tR.length) {
                            tR += ";";
                        }
                        tR +=
                            pkey +
                                "=" +
                                this.basket[comID].articleConfiguration[pkey].Value;
                    }
                });
            }
            return tR;
        });
    }
    getArticleManufacturerID(comID) {
        return Promise.resolve(this.pd.Articles[this.basket[comID].articleID].Manufacturer);
    }
    getArticleProgramID(comID) {
        return Promise.resolve(this.pd.Articles[this.basket[comID].articleID].Series);
    }
    getArticleManufacturerName(comID) {
        return Promise.resolve("//TODO");
    }
    getArticleProgramName(comID) {
        return Promise.resolve("//TODO");
    }
    getArticleCategories(comId) {
        return Promise.resolve({});
    }
    getArticleConfiguration(comID) {
        const tAC = {};
        for (const tK in this.basket[comID].articleConfiguration) {
            tAC[tK] = this.basket[comID].articleConfiguration[tK].Value;
        }
        return Promise.resolve(tAC);
    }
    getArticleCatalogImage(comID) {
        return Promise.resolve(undefined);
    }
    _intGetArticleConfiguration(articleId, oldConfig, userSetOfValues, _intRecurseDepth) {
        return new Promise((resolve, reject) => {
            const tAC = this.mergeRecursive({}, userSetOfValues);
            const tCheckPrecondition = (precondKey) => {
                let tR = ECheckConditionResult.Valid;
                if (precondKey && precondKey.length) {
                    if (this.pd.hasOwnProperty("Conditions") &&
                        this.pd.Conditions[precondKey]) {
                        tR = this.checkCondition(this.pd.Conditions[precondKey], tAC);
                    }
                    else {
                        console.warn('Condition "' + precondKey + '" not found!');
                    }
                }
                return tR;
            };
            function tGetDefaultValue(PK) {
                let tR;
                const tPKDef = PK.Default;
                if (PK.Values) {
                    PK.Values.some(pv => {
                        if (tCheckPrecondition(pv.Precondition) ===
                            ECheckConditionResult.Valid) {
                            if (tPKDef === undefined || pv.Value === tPKDef) {
                                tR = pv;
                            }
                        }
                        return tR !== undefined;
                    });
                }
                // number properties doesn't need its default value specified in Values set
                if (tR === undefined &&
                    tPKDef &&
                    (PK.ValueType === IGXE.EPropertyType.Float ||
                        PK.ValueType === IGXE.EPropertyType.Int)) {
                    tR = {
                        Value: tPKDef,
                    };
                }
                return tR;
            }
            let tUnresolvedIssuesPresent = false;
            let tIPConfDefaults = {};
            this.getInterProductID(articleId).then(ipid => {
                if (ipid) {
                    tIPConfDefaults = this.pd.InterProduct.ArticlePolymorphy[ipid][articleId];
                }
                const tArt = this.pd.Articles[articleId];
                if (tArt) {
                    if (tArt.PropertyClasses) {
                        tArt.PropertyClasses.forEach(pcid => {
                            const tPC = this.pd.PropertyClasses[pcid];
                            const tCCR_PC = tCheckPrecondition(tPC.Precondition);
                            if (tCCR_PC === ECheckConditionResult.Unresolvable) {
                                // marking propclass as unresolved
                                tUnresolvedIssuesPresent = true;
                            }
                            else if (tCCR_PC === ECheckConditionResult.Valid) {
                                // iterate though properties
                                for (const tPropKey in tPC.Properties) {
                                    if (tPC.Properties.hasOwnProperty(tPropKey)) {
                                        const tPK = tPC.Properties[tPropKey];
                                        const tKey = pcid + "." + tPropKey;
                                        const tCCR_PK = tCheckPrecondition(tPK.Precondition);
                                        if (tCCR_PK ===
                                            ECheckConditionResult.Unresolvable) {
                                            // marking property as unresolved
                                            tUnresolvedIssuesPresent = true;
                                        }
                                        else if (tCCR_PK ===
                                            ECheckConditionResult.Valid) {
                                            // get PropValue
                                            let tValue;
                                            if (userSetOfValues[tKey] !=
                                                undefined) {
                                                tValue = this._intGetIPropValue(userSetOfValues[tKey], tPK);
                                                const tPV = tValue;
                                                if (tPV == undefined ||
                                                    tCheckPrecondition(tPV.Precondition) !==
                                                        ECheckConditionResult.Valid) {
                                                    tValue = undefined;
                                                }
                                            }
                                            if (tValue == undefined &&
                                                tIPConfDefaults[tKey] !=
                                                    undefined) {
                                                tPK.Values.some(pv => {
                                                    if (pv.Value ===
                                                        tIPConfDefaults[tKey]) {
                                                        tValue = pv;
                                                        return true;
                                                    }
                                                    return false;
                                                });
                                            }
                                            if (tValue == undefined &&
                                                oldConfig[tKey] != undefined) {
                                                tValue = this._intGetIPropValue(oldConfig[tKey], tPK);
                                                const tPV = tValue;
                                                if (tPV == undefined ||
                                                    tCheckPrecondition(tPV.Precondition) !==
                                                        ECheckConditionResult.Valid) {
                                                    tValue = undefined;
                                                }
                                            }
                                            if (tValue == undefined) {
                                                tValue = tGetDefaultValue(tPK);
                                            }
                                            if (tValue == undefined) {
                                                // marking propvalue as unresolved
                                                tUnresolvedIssuesPresent = true;
                                            }
                                            else {
                                                tAC[tKey] = this.mergeRecursive({}, tValue);
                                            }
                                        }
                                    }
                                }
                            }
                        });
                    }
                    if (!tUnresolvedIssuesPresent) {
                        if (tArt.Condition) {
                            switch (tCheckPrecondition(tArt.Condition)) {
                                case ECheckConditionResult.Unresolvable:
                                    tUnresolvedIssuesPresent = true;
                                case ECheckConditionResult.Invalid:
                                    reject("Article condition not met");
                            }
                        }
                    }
                }
                if (tUnresolvedIssuesPresent) {
                    const tRD = (_intRecurseDepth || 0) + 1;
                    if (tRD < Object.keys(tAC).length) {
                        this.debug(EIGXEProductDataManagerDebugInfo.Condition, "Retry solving conditions\n", JSON.stringify(tAC));
                        resolve(this._intGetArticleConfiguration(articleId, oldConfig, tAC, tRD));
                    }
                    else {
                        console.warn("Could not solve conditions in a reasonable number of tries!\n", JSON.stringify(tAC));
                    }
                }
                resolve(tAC);
            });
        });
    }
    /**
     * @param propKey Property key in the format <PropClassId>.<PropertyID>
     * @returns IProperty object
     */
    getProperty(propKey) {
        const tPCP = propKey.split(".");
        let tProp;
        if (this.pd &&
            this.pd.PropertyClasses &&
            this.pd.PropertyClasses[tPCP[0]] &&
            this.pd.PropertyClasses[tPCP[0]].Properties[tPCP[1]]) {
            tProp = this.pd.PropertyClasses[tPCP[0]].Properties[tPCP[1]];
        }
        this.debug(EIGXEProductDataManagerDebugInfo.Properties, "getProperty", propKey, tProp);
        return tProp;
    }
    /**
     * @param articleID The article whose properties should be aquired
     * @param articleConfiguration The article configuration, this is used
     * to check preconditions of property classes and properties
     * @returns An Array of property keys in the format <PropClassId>.<PropertyID>,
     * sorted by PropClass and the .Position value of IProperty
     */
    async getArticlePropertyKeys(comID) {
        const tR = new Array();
        if (!this.pd.Articles[this.basket[comID].articleID].PropertyClasses) {
            return tR;
        }
        this.pd.Articles[this.basket[comID].articleID].PropertyClasses.forEach(pcid => {
            const tPropClass = this.pd.PropertyClasses[pcid];
            if (this.checkCondition(tPropClass.Precondition, this.basket[comID].articleConfiguration) !== ECheckConditionResult.Valid) {
                return;
            }
            // propclass is valid
            const tProps = new Array();
            for (const tPropKey in tPropClass.Properties) {
                if (!tPropClass.Properties.hasOwnProperty(tPropKey)) {
                    continue;
                }
                const tProp = tPropClass.Properties[tPropKey];
                if (this.checkCondition(tProp.Precondition, this.basket[comID].articleConfiguration) === ECheckConditionResult.Valid) {
                    tProps.push({
                        key: pcid + "." + tPropKey,
                        prop: tPropClass.Properties[tPropKey],
                    });
                }
            }
            // sort by position (still inside propclass)
            tProps.sort((a, b) => {
                if (a.prop.Position !== undefined &&
                    b.prop.Position !== undefined) {
                    if (a.prop.Position > b.prop.Position) {
                        return 1;
                    }
                    if (a.prop.Position < b.prop.Position) {
                        return -1;
                    }
                }
                return 0;
            });
            tProps.forEach(prop => tR.push(prop.key));
        });
        if (this.ipPropsTop) {
            try {
                const tIpId = await this.getInterProductID(this.basket[comID].articleID);
                if (tIpId === undefined) {
                    return tR;
                }
                const tPrioProps = await this.getInterProductPropertyKeys(tIpId);
                // push interproduct props to top
                tR.sort((a, b) => {
                    if (tPrioProps.indexOf(a) < 0 &&
                        tPrioProps.indexOf(b) < 0) {
                        return 0;
                    }
                    if (tPrioProps.indexOf(a) > -1 &&
                        tPrioProps.indexOf(b) > -1) {
                        return 0;
                    }
                    if (tPrioProps.indexOf(a) > -1) {
                        return -1;
                    }
                    return 1;
                });
            }
            catch (err) {
                // ignore
            }
        }
        return tR;
    }
    getArticlePropertyList(comID) {
        if (!this.basket[comID] || !this.basket[comID].articleConfiguration) {
            return Promise.reject(`Can not retrieve properties of unknown article "${comID}"`);
        }
        const articleConfiguration = this.basket[comID].articleConfiguration;
        return this.getArticlePropertyKeys(comID).then(keys => keys
            .filter(key => articleConfiguration[key])
            .map(propkey => {
            const tProp = this.getProperty(propkey);
            if (!tProp) {
                return undefined;
            }
            const values = this.getPropertyValues(tProp, articleConfiguration).map(val => ({
                Image: val.Image,
                Text: val.Text,
                Value: val.Value,
            }));
            const editable = values.length > 0;
            return {
                Key: propkey,
                Text: tProp.Text,
                AdditionalValues: tProp.AdditionalValues,
                ValueRangeFrom: tProp.ValueRangeFrom,
                ValueRangeTo: tProp.ValueRangeTo,
                ValueType: tProp.ValueType,
                ValueUnit: tProp.ValueUnit,
                Value: articleConfiguration[propkey].Value,
                Values: values,
                Visible: editable &&
                    articleConfiguration[propkey].Value !==
                        undefined,
                Editable: editable,
                Type: "choiceList",
                SubProperties: [],
            };
        })
            .filter(Boolean));
    }
    getArticleSubProperties(comID, parentPropertyKey) {
        return Promise.resolve([]);
    }
    getPropertyValues(property, articleConfiguration) {
        const resolvedProperty = typeof property === "string"
            ? this.getProperty(property)
            : property;
        if (resolvedProperty === undefined) {
            return [];
        }
        return resolvedProperty.Values.filter(value => this.checkCondition(value.Precondition, articleConfiguration) === ECheckConditionResult.Valid);
    }
    getArticlePrices(comID) {
        const tR = new Array();
        if (this.pd.Price_Type === IGXE.EPriceType.None) {
            // no prices
            return Promise.resolve(tR);
        }
        if (!this.pd.Articles[this.basket[comID].articleID]) {
            console.error("Article not found!", this.basket[comID].articleID);
            return Promise.reject(new Error("Article not found! " + this.basket[comID].articleID));
        }
        this.pd.Articles[this.basket[comID].articleID].Prices.forEach(ap => {
            if (this.checkCondition(ap.Precondition, this.basket[comID].articleConfiguration) === ECheckConditionResult.Valid) {
                tR.push({
                    Currency: ap.Currency,
                    Level: ap.Level,
                    Text: ap.Text,
                    Value: ap.Value,
                });
            }
        });
        // sort base prices to the top, leave other unchanged
        tR.sort((a, b) => {
            if (a.Level === IGXE.EPriceLevel.Base) {
                return -1;
            }
            if (b.Level === IGXE.EPriceLevel.Base) {
                return 1;
            }
            return 0;
        });
        return Promise.resolve(tR);
    }
    /**
     * Returns true if the article should be added to basket/BOM upon creation
     * @param comID: Commercial ID of the article instance.
     */
    articleIsCommercial(comID) {
        return Promise.resolve(this.pd.Articles[this.basket[comID].articleID].IsCommercial);
    }
    /**
     * Returns true if the article has a graphic representation that
     * getGfxRepresentation can process
     * @param comID: Commercial ID of the article instance.
     */
    articleHasGraphics(comID) {
        return Promise.resolve(this.pd.Articles[this.basket[comID].articleID].HasGraphics);
    }
    isGeometricProperty(comID, propKey) {
        const tProps = typeof propKey === "string" ? [propKey] : propKey;
        return Promise.resolve(tProps.some(pk => {
            const tProp = this.getProperty(pk);
            if (tProp !== undefined && tProp.Geometric) {
                return true;
            }
            return false;
        }));
    }
    async getMaterialAssignment(comID, propKey) {
        const propertyKeys = typeof propKey === "string" ? [propKey] : propKey;
        const assignments = {};
        propertyKeys.forEach(pk => {
            const tProp = this.getProperty(pk);
            if (tProp !== undefined &&
                tProp.MaterialCategory &&
                tProp.ValueType === IGXE.EPropertyType.Material) {
                const tAC = this.basket[comID].articleConfiguration;
                if (tAC[pk]) {
                    assignments[tProp.MaterialCategory] = tAC[pk]
                        .Value;
                }
                return true;
            }
        });
        return assignments;
    }
    // InterProduct methods
    getInterProductPropertyKeys(interProductID) {
        let tR = new Array();
        if (interProductID &&
            this.pd.InterProduct &&
            this.pd.InterProduct.ArticlePolymorphy) {
            const tIP = this.pd.InterProduct.ArticlePolymorphy[interProductID];
            if (tIP) {
                const tAllArtIDs = Object.keys(tIP);
                if (tAllArtIDs.length) {
                    const tFirst = tIP[tAllArtIDs[0]];
                    tR = tR.concat(Object.keys(tFirst));
                }
            }
        }
        this.debug(EIGXEProductDataManagerDebugInfo.ArticlePolymorphy, "getInterProductPropertyKeys", interProductID, "=>", tR);
        return Promise.resolve(tR);
    }
    getFirstArticleIdOfInterProduct(interProductID) {
        let tRes;
        if (this.pd &&
            this.pd.InterProduct &&
            this.pd.InterProduct.ArticlePolymorphy) {
            const tIP = this.pd.InterProduct.ArticlePolymorphy[interProductID];
            if (tIP) {
                const tAllArtIDs = Object.keys(tIP);
                if (tAllArtIDs.length) {
                    tRes = tAllArtIDs[0];
                }
            }
        }
        this.debug(EIGXEProductDataManagerDebugInfo.ArticlePolymorphy, "getFirstArticleIdOfInterProduct", interProductID, "=>", tRes);
        return Promise.resolve(tRes);
    }
    getInterProductID(articleID) {
        let tR;
        if (this.pd &&
            this.pd.InterProduct &&
            this.pd.InterProduct.ArticlePolymorphy) {
            Object.keys(this.pd.InterProduct.ArticlePolymorphy).some(cgid => {
                const tCG = this.pd.InterProduct.ArticlePolymorphy[cgid];
                if (Object.keys(tCG).indexOf(articleID) > -1) {
                    tR = cgid;
                    return true;
                }
                return false;
            });
        }
        this.debug(EIGXEProductDataManagerDebugInfo.ArticlePolymorphy, "getInterProductID", articleID, "=>", tR);
        return Promise.resolve(tR);
    }
    /**
     * Searches all sub-articles for the given main article based on its configuration
     */
    getSubArticleIDs(comID) {
        return Promise.resolve([]);
        // const tR: string[] = [];
        // if (
        // 	!this.pd ||
        // 	!this.pd.InterProduct ||
        // 	/*!this.pd.InterProduct.ArticlePolymorphy || */ !this.pd
        // 		.InterProduct.SubArticles
        // ) {
        // 	return tR;
        // }
        // const tIpId = await this.getInterProductID(
        // 	this.basket[comID].articleID
        // );
        // Object.keys(this.pd.InterProduct.SubArticles).forEach(subartID => {
        // 	const tSA = this.pd.InterProduct!.SubArticles![subartID];
        // 	if (tSA.MainArticleIDs.indexOf(this.basket[comID].articleID) > -1) {
        // 		if (
        // 			this.checkMatchingConfiguration(
        // 				this.basket[comID].articleConfiguration,
        // 				tSA.Configuration
        // 			)
        // 		) {
        // 			tR.push(subartID);
        // 		}
        // 	}
        // 	if (tIpId && tSA.MainArticleIDs.indexOf(tIpId) > -1) {
        // 		if (
        // 			this.checkMatchingConfiguration(
        // 				this.basket[comID].articleConfiguration,
        // 				tSA.Configuration
        // 			)
        // 		) {
        // 			tR.push(subartID);
        // 		}
        // 	}
        // });
        // return tR;
    }
    async setPropValue(comID, changedPropKey, newPropValue) {
        try {
            const tAID = this.basket[comID].articleID;
            const tAC = this.basket[comID].articleConfiguration;
            try {
                const tList = await this.getArticlePropertyKeys(comID);
                if (tList.indexOf(changedPropKey) < 0) {
                    throw new Error("No properties changed");
                }
            }
            catch (err) {
                return;
            }
            // find IPropertyValue to given TPropertyValue
            const tProp = this.getProperty(changedPropKey);
            const tPV = this._intGetIPropValue(newPropValue, tProp);
            const tModdedAC = {};
            tModdedAC[changedPropKey] = tPV;
            // get new article configuration, all dependencies resolved
            const tNewArticleConfiguration = await this._intGetArticleConfiguration(tAID, tAC, tModdedAC).catch(err => {
                this.debug(EIGXEProductDataManagerDebugInfo.Condition, err);
                throw err;
            });
            this.basket[comID].articleConfiguration = tNewArticleConfiguration;
            // check if this configuration leads to a different article ID due to polymorphy defined in InterProduct rules:
            const tIpId = await this.getInterProductID(tAID);
            if (tIpId) {
                const tNewArtID = this._intGetInterProductArticleID(tIpId, this.basket[comID].articleConfiguration);
                this.basket[comID].articleID = tNewArtID;
            }
        }
        catch (err) {
            throw err;
        }
    }
    async setPropValues(comId, values) {
        const changes = IGXE.normalizePropertyValueChanges(values);
        for (const { key, value } of changes) {
            await this.setPropValue(comId, key, value);
        }
    }
    /**
     * This IGXEProductDataManager-specific method returns the native IGXE property
     * definition of the given property.
     * @param propKey Property key in the format <PropClassId>.<PropertyID>
     * @returns IGXE.IProperty object
     */
    getIGXEProperty(propKey) {
        return this.getProperty(propKey);
    }
    async assignMaterialPropertyValues(article) {
        if (!article.comId || !article.gfxId) {
            return Logger.error("IGXEProductDataManager.assignMaterialPropertyValues: article under-specified.", article);
        }
        const propertyKeys = await this.getArticlePropertyKeys(article.comId);
        const materialAssignment = await this.getMaterialAssignment(article.comId, propertyKeys);
        return Core.State.API.SetCategoryMaterials(article.gfxId, materialAssignment);
    }
    checkCondition(condition, currentConfig) {
        if (condition === undefined) {
            return ECheckConditionResult.Valid;
        }
        let tCond;
        if (typeof condition === "string") {
            if (condition.length === 0) {
                return ECheckConditionResult.Valid;
            }
            if (this.pd.hasOwnProperty("Conditions") &&
                this.pd.Conditions[condition]) {
                tCond = this.pd.Conditions[condition];
            }
            else {
                console.warn("Condition", condition, "not found in product data!");
                return ECheckConditionResult.Invalid;
            }
        }
        else {
            tCond = condition;
        }
        this.debug(EIGXEProductDataManagerDebugInfo.Condition, "Parsing", JSON.stringify(tCond));
        let tEvalStr = "";
        function tGetValidVarName(propid) {
            if (typeof propid === "string") {
                return propid.replace(/\./g, "___");
            }
            return undefined;
        }
        function tGetPropNameWithoutClass(propid) {
            const tP = propid.lastIndexOf(".");
            return propid.substr(tP + 1);
        }
        for (const propid in currentConfig) {
            if (currentConfig.hasOwnProperty(propid) &&
                propid.indexOf("$") < 0) {
                // creating two local vars per property: with and without propclass
                const tVal = typeof currentConfig[propid] === "object"
                    ? currentConfig[propid]
                        .Value
                    : currentConfig[propid];
                tEvalStr +=
                    "var " +
                        tGetValidVarName(propid) +
                        "=" +
                        tGetPropNameWithoutClass(propid) +
                        "=" +
                        JSON.stringify(tVal) +
                        ";";
            }
        }
        function tGetConditionEvalString(c, isSubEval) {
            const tCType = typeof c;
            if (tCType === "string" ||
                tCType === "number" ||
                tCType === "boolean") {
                if (tCType === "string") {
                    return tGetValidVarName(c);
                }
                return c.toString();
            }
            else {
                if (Array.isArray(c)) {
                    return JSON.stringify(c);
                }
            }
            const tC = c;
            let tS = "(";
            if (tC.Not === true) {
                tS += "!";
            }
            tS += "(";
            switch (tC.Comp) {
                case IGXE.EConditionJoin.AND:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " && ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionJoin.OR:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " || ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionJoin.PLUS:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " + ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionJoin.MINUS:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " - ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionJoin.MUL:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " * ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionJoin.DIV:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " / ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionCompare.Equal:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " === ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionCompare.Greater:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " > ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionCompare.GreaterOrEqual:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " >= ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionCompare.Smaller:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " < ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionCompare.SmallerOrEqual:
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += " <= ";
                    tS += tGetConditionEvalString(tC.Op2, true);
                    break;
                case IGXE.EConditionCompare.In:
                    tS += tGetConditionEvalString(tC.Op2, true);
                    tS += ".indexOf(";
                    tS += tGetConditionEvalString(tC.Op1, true);
                    tS += ") > -1";
                    break;
            }
            tS += "))";
            if (isSubEval !== true) {
                tS += ";";
            }
            return tS;
        }
        tEvalStr += tGetConditionEvalString(tCond);
        this.debug(EIGXEProductDataManagerDebugInfo.Condition, "Evaluate", tEvalStr);
        let tRes = ECheckConditionResult.Invalid;
        try {
            const tR = eval(tEvalStr);
            if (tR === true) {
                tRes = ECheckConditionResult.Valid;
            }
        }
        catch (e) {
            if (e instanceof ReferenceError) {
                tRes = ECheckConditionResult.Unresolvable;
            }
            else {
                tRes = ECheckConditionResult.Invalid;
                console.warn(e);
            }
        }
        this.debug(EIGXEProductDataManagerDebugInfo.Condition, "Result =", ECheckConditionResult[tRes]);
        return tRes;
    }
    mergeIGXE(obj1, obj2) {
        for (const p in obj2) {
            if (obj2[p] === undefined) {
                // never overwrite with undefined
                continue;
            }
            switch (p) {
                case "Articles":
                    // merge articles
                    if (obj1[p] == undefined) {
                        obj1[p] = {};
                    }
                    for (const artNum in obj2[p]) {
                        if (obj1[p][artNum] == undefined) {
                            obj1[p][artNum] = obj2[p][artNum];
                        }
                        else {
                            for (const artKey in obj2[p][artNum]) {
                                switch (artKey) {
                                    case "ShortText":
                                    case "LongText":
                                        // overwrite article texts
                                        // TODO: only overwrite languages
                                        if (obj2[p][artNum][artKey] &&
                                            obj2[p][artNum][artKey].length) {
                                            obj1[p][artNum][artKey] =
                                                obj2[p][artNum][artKey];
                                        }
                                        break;
                                    case "PropertyClasses":
                                        // concat PropClasses
                                        obj1[p][artNum][artKey] = (obj1[p][artNum][artKey] || []).concat(obj2[p][artNum][artKey] || []);
                                        break;
                                    case "Prices":
                                        if (obj1[p][artNum][artKey] == undefined) {
                                            obj1[p][artNum][artKey] =
                                                obj2[p][artNum][artKey];
                                        }
                                        else {
                                            if (obj1[p][artNum][artKey]
                                                .length ===
                                                obj2[p][artNum][artKey].length) {
                                                // if both price array have the same length, merge their contents
                                                for (let idx = 0; idx <
                                                    obj1[p][artNum][artKey]
                                                        .length; idx++) {
                                                    obj1[p][artNum][artKey][idx] = this.mergeRecursive(obj1[p][artNum][artKey][idx], obj2[p][artNum][artKey][idx]);
                                                }
                                            }
                                        }
                                        break;
                                    default:
                                        // overwrite all other
                                        if (obj2[p][artNum][artKey]) {
                                            obj1[p][artNum][artKey] =
                                                obj2[p][artNum][artKey];
                                        }
                                }
                            }
                        }
                    }
                    break;
                case "PropertyClasses":
                    // merge PCs
                    this.mergeRecursive(obj1[p], obj2[p]);
                    break;
                case "Conditions":
                    // merge Conds
                    this.mergeRecursive(obj1[p], obj2[p]);
                    break;
                default:
                    // all other keys
                    obj1[p] = this.mergeRecursive(obj1[p], obj2[p]);
            }
        }
        return obj1;
    }
    mergeRecursive(obj1, obj2) {
        if (typeof obj2 === "object") {
            for (const p in obj2) {
                if (obj2[p] === undefined) {
                    // never overwrite with undefined
                    continue;
                }
                try {
                    // Property in destination object set; update its value.
                    if (Array.isArray(obj2[p])) {
                        if (Array.isArray(obj1[p])) {
                            obj1[p] = obj1[p].concat(obj2[p]);
                        }
                        else {
                            obj1[p] = obj2[p];
                        }
                    }
                    else {
                        if (typeof obj2[p] === "object") {
                            obj1[p] = this.mergeRecursive(obj1[p], obj2[p]);
                        }
                        else {
                            obj1[p] = obj2[p];
                        }
                    }
                }
                catch (e) {
                    // Property in destination object not set; create it and set its value.
                    obj1[p] = obj2[p];
                }
            }
        }
        else {
            if (obj2 !== undefined) {
                obj1 = obj2;
            }
        }
        return obj1;
    }
    debug(lvl, ...message) {
        if (this.dbgInfo.indexOf(EIGXEProductDataManagerDebugInfo.All) > -1 ||
            this.dbgInfo.indexOf(lvl) > -1) {
            // tslint:disable-next-line:no-console
            console.debug(EIGXEProductDataManagerDebugInfo[lvl] + ":", ...message);
        }
    }
    _intGetTPropValue(pv) {
        let tR;
        if (typeof pv === "object") {
            tR = pv.Value;
        }
        else {
            tR = pv;
        }
        return tR;
    }
    _intGetIPropValue(pv, propdef) {
        const tTVal = this._intGetTPropValue(pv);
        const tR = propdef.Values.find(val => val.Value === tTVal);
        if (tR === undefined && propdef.AdditionalValues) {
            // new value is not in Values, but additional values are allowed
            return {
                Value: tTVal,
            };
        }
        return tR;
    }
    _intGetInterProductArticleID(interProductID, articleConfiguration) {
        let tR;
        if (interProductID &&
            this.pd.InterProduct &&
            this.pd.InterProduct.ArticlePolymorphy) {
            const tCG = this.pd.InterProduct.ArticlePolymorphy[interProductID];
            if (tCG) {
                // iterate through articles, check if all config items are fullfilled
                Object.keys(tCG).some(artid => {
                    const tK = Object.keys(tCG[artid]);
                    let tMatches = 0;
                    tK.forEach(ac => {
                        // find ac in articleConfiguration
                        if (Object.keys(articleConfiguration).some(ackey => {
                            return (ac === ackey &&
                                tCG[artid][ac] ===
                                    articleConfiguration[ackey].Value);
                        })) {
                            tMatches++;
                        }
                    });
                    if (tMatches >= tK.length) {
                        tR = artid;
                        return true;
                    }
                    return false;
                });
            }
        }
        this.debug(EIGXEProductDataManagerDebugInfo.ArticlePolymorphy, "getInterProductArticleID", interProductID, articleConfiguration, "=>", tR);
        return tR;
    }
    getIGXCRepresentation(comId) {
        return Promise.resolve(undefined);
    }
    async getGfxRepresentation(comID) {
        const hasGfx = await this.articleHasGraphics(comID);
        if (!hasGfx) {
            Logger.warn("Article has no graphical representation");
            return undefined;
        }
        const scene = Core.IGXC.createScene();
        const materialAssignment = await this.getMaterialAssignment(comID, await this.getArticlePropertyKeys(comID));
        scene.CommercialId = comID;
        scene.Categories = materialAssignment;
        return {
            scene,
        };
    }
    /**
     * Get the connected articles based on comIDs.
     * @param comIDs Commercial IDs of the base articles (i.e. the one the user clicks on)
     * @returns Array of the comIDs of all articles in the group (incl. comID)
     */
    getConnectedArticles(comIDs) {
        return Promise.resolve(comIDs);
    }
    getArticlePlacement(comId) {
        return Promise.resolve(undefined);
    }
}
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "removeArticle", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "duplicateArticle", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleType", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleID", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleName", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleShortText", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleLongText", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleVariantText", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleVariantCode", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleManufacturerID", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleProgramID", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleManufacturerName", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleProgramName", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleCategories", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticleConfiguration", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticlePropertyKeys", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getArticlePrices", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "articleIsCommercial", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "articleHasGraphics", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "isGeometricProperty", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "getMaterialAssignment", null);
__decorate([
    CheckComId
], IGXEProductDataManager.prototype, "setPropValue", null);
