import { __assign, __awaiter, __extends, __generator } from "tslib";
import * as React from "react";
import { IGXE } from "@intelligentgraphics/igd.core";
import { PropertyUtil, RuleUtil, } from "@intelligentgraphics/igd.productdata";
import { isNullOrUndefined, memoizeOne, } from "@intelligentgraphics/ig.utilities";
import { withSessionContext } from "../SessionContext";
import * as Units from "../Units";
import { collectArticleData, groupArticlesBySourceId } from "./Articles";
import { byComId } from "./Groupers";
import { createComponentState, parsePropertyIdentifier, } from "./Util";
import { PropertyEditorContext } from "./Context";
export var PropertyEditorSubPropertyBehaviour;
(function (PropertyEditorSubPropertyBehaviour) {
    /**
     * Keep sub properties on their parent properties
     */
    PropertyEditorSubPropertyBehaviour["Depth"] = "depth";
    /**
     * Extract available properties and add them the to normal list of properties
     */
    PropertyEditorSubPropertyBehaviour["ShowInline"] = "showInline";
})(PropertyEditorSubPropertyBehaviour || (PropertyEditorSubPropertyBehaviour = {}));
export var PropertyEditorMode;
(function (PropertyEditorMode) {
    /**
     * Display articles of the session
     */
    PropertyEditorMode["AllArticles"] = "AllArticles";
    /**
     * Display articles of the session application state selection
     */
    PropertyEditorMode["Selection"] = "Selection";
    /**
     * Display articles provided via prop
     */
    PropertyEditorMode["ProvidedArticles"] = "ProvidedArticles";
    /**
     * Display provided articles or the session application state selection, if no articles are provided
     */
    PropertyEditorMode["Auto"] = "Auto";
})(PropertyEditorMode || (PropertyEditorMode = {}));
var PropertyEditor = /** @class */ (function (_super) {
    __extends(PropertyEditor, _super);
    function PropertyEditor() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.state = {
            groups: [],
            groupReferences: {},
            properties: {},
        };
        _this.createContextValue = memoizeOne(function (context, properties) { return (__assign(__assign({}, context), { properties: properties })); });
        _this.createViewProps = memoizeOne(function (state) { return ({
            groups: state.groups,
            properties: Object.getOwnPropertyNames(state.properties),
            getPropertyByIdentifier: function (identifier) { return state.properties[identifier]; },
        }); });
        _this.articleData = {};
        _this.transferTargets = [];
        _this.childContext = {
            properties: {},
            setPropertyValue: _this.setPropertyValue.bind(_this),
            applyToAll: _this.applyToTransferTargets.bind(_this),
            getComIdsByProperty: _this.getComIdsByProperty.bind(_this),
        };
        _this.onItemCreated = function (article) {
            if (_this.props.mode === PropertyEditorMode.AllArticles) {
                return _this.queueUpdate(_this.getComIds());
            }
        };
        _this.onItemRemoved = function (article) {
            if (_this.props.mode === PropertyEditorMode.AllArticles) {
                return _this.queueUpdate(_this.getComIds());
            }
        };
        _this.onItemConfigured = function (article) {
            if (!_this.articleData[article.comId]) {
                return;
            }
            _this.queueUpdate(_this.getComIds());
        };
        _this.onApplicationStateValueChanged = function (payload) {
            if (payload.key !== "selection") {
                return;
            }
            _this.queueUpdate(_this.getComIds());
        };
        _this.queueUpdate = function (comIds) {
            if (comIds === void 0) { comIds = []; }
            if (_this.updateBatchTimer !== undefined) {
                window.clearTimeout(_this.updateBatchTimer);
                _this.updateBatchTimer = undefined;
            }
            _this.updateBatchTimer = window.setTimeout(function () {
                return _this.setupForArticles(comIds);
            });
        };
        _this.setupId = 0;
        return _this;
    }
    PropertyEditor.prototype.render = function () {
        var context = this.createContextValue(this.childContext, this.state.properties);
        var viewProps = this.createViewProps(this.state);
        return (React.createElement(PropertyEditorContext.Provider, { value: context }, this.props.children(viewProps)));
    };
    PropertyEditor.prototype.componentDidMount = function () {
        this.setupForArticles(this.getComIds());
        this.subscription = this.props.session.subscribe({
            ItemConfigured: this.onItemConfigured,
            ItemCreated: this.onItemCreated,
            ItemRemoved: this.onItemRemoved,
            ApplicationStateValueChanged: this.onApplicationStateValueChanged,
        });
    };
    PropertyEditor.prototype.componentWillUnmount = function () {
        if (this.subscription) {
            this.subscription.destroy();
        }
    };
    PropertyEditor.prototype.componentDidUpdate = function (prevProps) {
        if (this.props.articles !== prevProps.articles ||
            this.props.transferTargets !== prevProps.transferTargets ||
            this.props.units !== prevProps.units ||
            this.props.subPropertyBehaviour !==
                prevProps.subPropertyBehaviour ||
            this.props.mode !== prevProps.mode) {
            this.queueUpdate(this.getComIds());
        }
    };
    PropertyEditor.prototype.setupForArticles = function (comIds) {
        return __awaiter(this, void 0, void 0, function () {
            var id, transferTargets, articleData, articleGroups, sourceGroups, propertyList, result;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.setupId++;
                        id = this.setupId;
                        transferTargets = this.props.transferTargets;
                        // replace article com ids with set com ids if the articles are not configurable
                        comIds = RuleUtil.execute(this.props.session, comIds, "configurable");
                        if (transferTargets !== undefined) {
                            // do the same for transfer targets, when available
                            transferTargets = RuleUtil.execute(this.props.session, transferTargets, "configurable");
                        }
                        articleData = collectArticleData(this.props.session, comIds.concat(transferTargets || []));
                        articleGroups = groupArticlesBySourceId(comIds.map(function (comId) { return articleData[comId]; }));
                        sourceGroups = Object.values(articleGroups);
                        return [4 /*yield*/, this.props.createPropertyGroups(sourceGroups)];
                    case 1:
                        propertyList = _a.sent();
                        if (id !== this.setupId) {
                            // if the setupId has changed while propertyGroups were created, skip remaining setup
                            return [2 /*return*/];
                        }
                        this.transferTargets = transferTargets;
                        this.articleData = articleData;
                        result = createComponentState({
                            propertyList: propertyList,
                            units: this.props.units,
                            transferTargets: transferTargets.map(function (comId) { return _this.articleData[comId]; }),
                            subPropertyBehaviour: this.props.subPropertyBehaviour,
                        });
                        this.setState(result);
                        return [2 /*return*/];
                }
            });
        });
    };
    PropertyEditor.prototype.setPropertyValue = function (name, newValue) {
        var _a = parsePropertyIdentifier(name), groupId = _a.groupId, propertyKey = _a.propertyKey;
        var references = this.state.groupReferences[groupId];
        if (references === undefined) {
            throw new Error("Could not find a group for id \"" + groupId + "\"");
        }
        var normalizedReferences = Array.isArray(references)
            ? references
            : [references];
        var targetProperty = this.state.properties[name];
        if (name === undefined) {
            throw new Error("Could not find a property for name \"" + name + "\"");
        }
        if (!isNullOrUndefined(targetProperty.ValueUnit) &&
            (targetProperty.ValueType === IGXE.EPropertyType.Int ||
                targetProperty.ValueType === IGXE.EPropertyType.Float)) {
            newValue = Units.convert(Number(newValue), targetProperty.ValueUnit, IGXE.EPropertyValueUnit.LENGTH_M);
        }
        return this.props.setPropertyValue(this.props.session, normalizedReferences, propertyKey, newValue);
    };
    PropertyEditor.prototype.applyToTransferTargets = function (name) {
        return __awaiter(this, void 0, void 0, function () {
            var propertyKey, property, newValueDefinition, newValue, applyTargets;
            var _this = this;
            return __generator(this, function (_a) {
                propertyKey = parsePropertyIdentifier(name).propertyKey;
                property = this.state.properties[name];
                if (property === undefined) {
                    throw new Error("Can not find a property for \"" + name + "\"");
                }
                newValueDefinition = IGXE.getFinalPropertyValue(property);
                if (newValueDefinition === undefined) {
                    throw new Error("Property \"" + name + "\" can not be applied to all, because no value has been configured");
                }
                newValue = newValueDefinition.Value;
                if (!isNullOrUndefined(property.ValueUnit) &&
                    (property.ValueType === IGXE.EPropertyType.Int ||
                        property.ValueType === IGXE.EPropertyType.Float)) {
                    newValue = Units.convert(Number(newValue), property.ValueUnit, IGXE.EPropertyValueUnit.LENGTH_M);
                }
                applyTargets = PropertyUtil.findApplyTargets(this.transferTargets.map(function (target) { return _this.articleData[target]; }), propertyKey, newValue);
                return [2 /*return*/, this.props.setPropertyValue(this.props.session, applyTargets.map(function (article) { return article.comId; }), propertyKey, newValue)];
            });
        });
    };
    PropertyEditor.prototype.getComIdsByProperty = function (name) {
        var groupId = parsePropertyIdentifier(name).groupId;
        var references = this.state.groupReferences[groupId];
        return Array.isArray(references) ? references : [references];
    };
    PropertyEditor.prototype.getComIds = function () {
        switch (this.props.mode) {
            case PropertyEditorMode.AllArticles:
                return this.props.session.getAllComIds();
            case PropertyEditorMode.Selection:
                return this.props.session.getApplicationStateValue("selection");
            case PropertyEditorMode.ProvidedArticles:
                if (this.props.articles === undefined) {
                    throw new Error("The property editor's mode has been set to ProvidedArticles, but no articles have been provided!");
                }
                return this.props.articles;
        }
        if (this.props.articles !== undefined) {
            return this.props.articles;
        }
        return this.props.session.getApplicationStateValue("selection");
    };
    PropertyEditor.defaultProps = {
        createPropertyGroups: byComId,
        transferTargets: [],
        setPropertyValue: function (session, articles, propKey, propValue) {
            return session.changeProperty(articles, propKey, propValue);
        },
        subPropertyBehaviour: PropertyEditorSubPropertyBehaviour.ShowInline,
        mode: PropertyEditorMode.Auto,
    };
    return PropertyEditor;
}(React.Component));
export default withSessionContext(PropertyEditor);
