"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWidgets = exports.retryDataBinds = exports.findWidgets = exports.getDom = exports.checkVisible = exports.WidgetsContext = exports.widgetKeys = void 0;
var tslib_1 = require("tslib");
var React = tslib_1.__importStar(require("react"));
var mobx_1 = require("mobx");
var lodash_es_1 = require("lodash-es");
var eventProxy_1 = require("./eventProxy");
var adapter_1 = require("./adapter");
exports.widgetKeys = [
    'getConfig',
    'findWidgets',
    'getWidgetsByType',
    'extends',
    'addWidgetsListener',
    'removeWidgetsListener',
    'getOwnerWidget',
    'getDom',
    'domRef',
    '_getInstanceRef',
];
exports.WidgetsContext = React.createContext({ parent: null });
function checkVisible(_a) {
    var _visible = _a._visible;
    return _visible !== false && _visible !== '';
}
exports.checkVisible = checkVisible;
function getDom(element, options) {
    if (!element) {
        console.warn('getDom 接口未传入有效的 element');
        return Promise.resolve({});
    }
    var result = {};
    if (options.id)
        result.id = element.id;
    if (options.dataset)
        result.dataset = element.dataset;
    if (options.rect) {
        var rect = element.getBoundingClientRect();
        Object.assign(result, {
            left: rect.left,
            right: rect.right,
            top: rect.top,
            bottom: rect.bottom,
        });
    }
    if (options.size) {
        var rect = element.getBoundingClientRect();
        Object.assign(result, {
            width: rect.width,
            height: rect.height,
        });
    }
    if (options.scrollOffset) {
        Object.assign(result, {
            scrollLeft: element.scrollLeft,
            scrollTop: element.scrollTop,
        });
    }
    if (options.properties && Array.isArray(options.properties)) {
        options.properties.forEach(function (propName) {
            result[propName] = element.getAttribute(propName);
        });
    }
    if (options.computedStyle && Array.isArray(options.computedStyle)) {
        var computedStyle_1 = adapter_1.runtime.getComputedStyle(element);
        options.computedStyle.forEach(function (propName) {
            result[propName] = computedStyle_1[propName];
        });
    }
    return Promise.resolve(result);
}
exports.getDom = getDom;
function findWidgets(widget, parentType, filterFn, includeInvisibleDescendants) {
    if (!widget)
        return [];
    var _a = widget.children, children = _a === void 0 ? [] : _a;
    var matched = children.filter(filterFn);
    // 过滤掉不可见后代
    if (!includeInvisibleDescendants) {
        children = children.filter(function (item) { return checkVisible(item); });
    }
    children.forEach(function (child) {
        // 如果递归过程中发现了自己，则停止递归
        matched = matched.concat(findWidgets(child, parentType, filterFn, includeInvisibleDescendants));
    });
    // 过滤掉不可见的匹配项
    if (!includeInvisibleDescendants) {
        matched = matched.filter(function (item) { return checkVisible(item); });
    }
    return matched;
}
exports.findWidgets = findWidgets;
var retryQueue = [];
// 递归执行
function retryDataBinds(tryTime) {
    if (tryTime === void 0) { tryTime = 10; }
    var fn = retryQueue.shift();
    if (!fn || tryTime <= 0)
        return;
    try {
        fn({ silent: tryTime > 1 });
    }
    catch (e) {
        console.error('retryDataBinds', e);
    }
    retryDataBinds(tryTime - 1);
}
exports.retryDataBinds = retryDataBinds;
function createWidgets(widgetProps, dataBinds, context) {
    var nodeTree = createWidgetTree(widgetProps, dataBinds);
    var widgets = runFor(nodeTree, {}, null, null, context);
    return widgets;
    /**
     *
     * @param {*} curForNode
     * @param {*} forItems
     * @param {*} parentLevelWidgets
     * @param {*} parentWidget
     * @returns top level widgets or for dispose
     */
    function runFor(curForNode, forItems, parentLevelWidgets, parentWidget, context) {
        var nodeId = curForNode.id;
        if (!curForNode.value) {
            return createSubTree(curForNode, {}, context);
        }
        var _waForKey = curForNode.value._waForKey;
        var timer = {};
        var dispose = (0, mobx_1.autorun)(function () {
            var forList = [];
            try {
                clearTimeout(timer[nodeId]);
                var list = dataBinds[nodeId]._waFor(forItems, undefined, context);
                if (!Array.isArray(list)) {
                    timer[nodeId] = setTimeout(function () {
                        console.warn(nodeId, 'For 循环绑定的数据并不是数组，请检查');
                    }, 1000);
                }
                else {
                    forList = list;
                }
            }
            catch (e) {
                timer[nodeId] = setTimeout(function () {
                    console.error('waFor error', e);
                }, 1000);
            }
            // 让 forList 进行监听
            forList.forEach(function () { });
            (0, mobx_1.untracked)(function () {
                // dispose widgets before reused instead
                // disposeWidgets(parentLevelWidgets[curForNode.id]);
                var exsitMap = forList.reduce(function (map, item) {
                    if (item === null || item === void 0 ? void 0 : item[_waForKey]) {
                        map[item[_waForKey]] = true;
                    }
                    return map;
                }, {});
                var forWidgets = parentLevelWidgets === null || parentLevelWidgets === void 0 ? void 0 : parentLevelWidgets[nodeId];
                var existedWidgetIndex = [];
                var extraWidgetsIndex = [];
                var extraWidgets = [];
                forWidgets.forEach(function (widget, index) {
                    if (exsitMap[widget._key]) {
                        existedWidgetIndex.push(index);
                        // need to use uqique key
                        exsitMap[widget._key] = undefined;
                    }
                    else {
                        extraWidgetsIndex.push(index);
                    }
                });
                var extraWidgetsIndexMap = extraWidgetsIndex.reduce(function (map, item) {
                    map[item] = true;
                    extraWidgets.push(forWidgets[item]);
                    return map;
                }, {});
                // clean nodes of previouse for run
                dfsTree(curForNode, function (node) {
                    var arr = parentLevelWidgets[node.id];
                    (0, lodash_es_1.remove)(arr, function (_, index) {
                        return extraWidgetsIndexMap[index];
                    });
                }, undefined);
                // 清理根 for 的 autorun, 并递归清理子节点
                extraWidgets.map(function (w) {
                    disposeWidgets([w]);
                    parentWidget && (0, lodash_es_1.remove)(parentWidget.children, function (_a) {
                        var id = _a.id;
                        return id === nodeId;
                    });
                });
                forList.forEach(function (item, index) {
                    var _a, _b;
                    var subForItems = tslib_1.__assign(tslib_1.__assign({}, forItems), (_a = {}, _a[nodeId] = item, _a.index = tslib_1.__assign(tslib_1.__assign({}, forItems.index), (_b = {}, _b[nodeId] = index, _b)), _a));
                    createSubTree(curForNode, subForItems, context);
                });
                // 非初始化时遇到需要重新构建 dataBinds
                retryDataBinds();
            });
        });
        return dispose;
        function createSubTree(curForNode, subForItems, context) {
            var widgets = {};
            // traverse down the tree to set all widgets
            dfsTree(curForNode, function (node, parentNode, cache) {
                var _a, _b;
                var parentForWidgetArr = (parentLevelWidgets === null || parentLevelWidgets === void 0 ? void 0 : parentLevelWidgets[node.id]) || [];
                var _waForKey = node.value._waForKey;
                var key = (_a = subForItems[node.id]) === null || _a === void 0 ? void 0 : _a[_waForKey];
                var index = cache[parentNode === null || parentNode === void 0 ? void 0 : parentNode.id]
                    ? cache[parentNode.id].index
                    : parentForWidgetArr.findIndex(function (widget) { return key && widget._key === key; });
                var existedWidget = index !== -1 ? parentForWidgetArr[index] : null;
                if (existedWidget) {
                    cache[node.id] = {
                        index: index,
                    };
                }
                if (node.forCount === curForNode.forCount) {
                    // Leaf node
                    var w_1 = existedWidget;
                    if (!existedWidget) {
                        w_1 = (0, mobx_1.observable)(widgetProps[node.id]);
                        w_1.id = node.id;
                        w_1._key = key;
                        if (node === curForNode) {
                            w_1._disposers = [];
                        }
                        w_1._listeners = new eventProxy_1.EventProxy();
                        // 提供一个给 Node 挂载 API 的方式
                        (0, mobx_1.untracked)(function () {
                            if (!w_1.getConfig) {
                                // 兼容非复合组件调用 getConfig 能力
                                w_1.getConfig = function () { return ({}); };
                            }
                            w_1.findWidgets = function (type, includeInvisibleDescendants) {
                                return findWidgets(w_1, w_1.widgetType, type, includeInvisibleDescendants);
                            };
                            w_1.getWidgetsByType = function (type, includeInvisibleDescendants) {
                                return w_1.findWidgets(function (currentWidget) { return currentWidget.widgetType === type; }, includeInvisibleDescendants);
                            };
                            w_1.extends = function (name, fnOrData) {
                                return Object.defineProperty(w_1, name, {
                                    value: fnOrData,
                                    writable: true,
                                });
                            };
                            w_1.addWidgetsListener = function (name, listener) { return w_1._listeners.on(name, listener); };
                            w_1.removeWidgetsListener = function (name, listener) { return w_1._listeners.cancel(name, listener); };
                            w_1.getOwnerWidget = function () { return null; }; // 默认带上一个 widget
                            w_1._getInstanceRef = function () { return null; }; // 默认初始值
                            Object.defineProperty(w_1, '_methods', {
                                get: function () {
                                    var instance = w_1._getInstanceRef();
                                    return instance === null || instance === void 0 ? void 0 : instance.current.methods;
                                },
                            });
                        });
                        w_1.children = [];
                        var parent_1 = parentNode ? widgets[parentNode.id] : parentWidget;
                        if (parent_1) {
                            w_1.parent = parent_1;
                            // 只有可显示 visible 的才存入 children 里
                            if (checkVisible(w_1)) {
                                parent_1.children.push(w_1);
                            }
                        }
                        (_b = parentForWidgetArr.push) === null || _b === void 0 ? void 0 : _b.call(parentForWidgetArr, w_1);
                    }
                    else {
                        disposeWidgets([existedWidget], true);
                    }
                    widgets[node.id] = w_1;
                    // Setup data binds
                    Object.keys(dataBinds[node.id] || {})
                        .sort(function (a, b) {
                        return a.length - b.length > 0 ? 1 : -1;
                    })
                        .map(function (prop) {
                        if (prop === '_waFor') {
                            return;
                        }
                        var timer = null;
                        function getBindData(options) {
                            if (options === void 0) { options = { silent: false }; }
                            var disposeError = false;
                            var dispose = (0, mobx_1.autorun)(function () {
                                try {
                                    clearTimeout(timer);
                                    // Computed data bind in the next tick since data bind may read widgets data
                                    var value_1 = dataBinds[node.id][prop](subForItems, undefined, context);
                                    var paths_1 = prop.split('.').filter(function (key) { return !!key; });
                                    if (paths_1.length > 1) {
                                        // 一定要 untracked 不然爆栈了
                                        (0, mobx_1.untracked)(function () {
                                            var pre = (0, lodash_es_1.get)(w_1, prop);
                                            (0, lodash_es_1.set)(w_1, prop, value_1);
                                            /**
                                             * 深层的对象内部值变化不会触发 obverver，因此浅拷贝对象
                                             */
                                            if (Array.isArray(w_1[paths_1[0]])) {
                                                if (!(0, lodash_es_1.isEqual)(pre, value_1)) {
                                                    w_1[paths_1[0]] = tslib_1.__spreadArray([], tslib_1.__read(w_1[paths_1[0]]), false);
                                                }
                                            }
                                        });
                                    }
                                    else {
                                        // 普通 key 直接赋值
                                        w_1[prop] = value_1;
                                    }
                                    disposeError = false;
                                }
                                catch (e) {
                                    if (prop === '_visible') {
                                        w_1[prop] = false;
                                    }
                                    else {
                                        retryQueue.push(getBindData);
                                        disposeError = true;
                                    }
                                    if (!(options === null || options === void 0 ? void 0 : options.silent)) {
                                        timer = setTimeout(function () {
                                            console.warn("Error computing data bind ".concat(w_1.id, ".").concat(prop), e);
                                            timer = null;
                                        }, 1000);
                                    }
                                }
                            });
                            if (!disposeError && curForNode.id) {
                                widgets[curForNode.id]._disposers.push(dispose);
                            }
                        }
                        getBindData();
                    });
                }
                else if (!existedWidget) {
                    if (parentLevelWidgets) {
                        var len = parentLevelWidgets[node.id].push([]);
                        widgets[node.id] = parentLevelWidgets[node.id][len - 1];
                    }
                    else {
                        widgets[node.id] = (0, mobx_1.observable)([]);
                    }
                }
                else {
                    widgets[node.id] = existedWidget;
                }
            }, undefined);
            // run for of next level
            dfsTree(curForNode, function (node, parentNode) {
                if (node.forCount === curForNode.forCount + 1 && dataBinds[node.id] && dataBinds[node.id]._waFor) {
                    widgets[node.id]._disposers = { dataBinds: [] };
                    var dispose_1 = runFor(node, subForItems, widgets, node.parent && widgets[node.parent.id], context);
                    curForNode.id && widgets[curForNode.id]._disposers.push(dispose_1);
                }
            }, undefined);
            return widgets;
        }
    }
}
exports.createWidgets = createWidgets;
/**
 * Add parent, children to widget
 */
function createWidgetTree(widgets, dataBinds) {
    var virtualRoot = { children: [], forCount: 0 };
    var nodes = Object.keys(widgets).reduce(function (result, id) {
        result[id] = {
            id: id,
            value: widgets[id],
            children: [],
            parent: null,
            forCount: 0,
        };
        return result;
    }, {});
    // Create widgets tree API
    Object.keys(nodes).map(function (id) {
        var curNode = nodes[id];
        var parent = nodes[widgets[id]._parentId];
        if (!parent) {
            virtualRoot.children.push(curNode);
            return;
        }
        curNode.parent = parent;
        parent.children.push(curNode);
    });
    virtualRoot.children.map(addForCount);
    // dfs, add forCount
    function addForCount(node) {
        var _a;
        if (node.parent) {
            node.forCount = node.parent.forCount;
        }
        if ((_a = dataBinds[node.id]) === null || _a === void 0 ? void 0 : _a._waFor) {
            node.forCount++;
        }
        node.children.map(addForCount);
    }
    return virtualRoot;
}
function dfsTree(node, fn, parent, cache) {
    if (cache === void 0) { cache = {}; }
    node.value && fn(node, parent, cache);
    node.children.map(function (e) { return dfsTree(e, fn, node.value ? node : null, cache); });
}
// dispose autorun
function disposeWidgets(widgets, noRecursive) {
    if (widgets === void 0) { widgets = []; }
    if (noRecursive === void 0) { noRecursive = false; }
    widgets.forEach(function (widget) {
        var disposers = widget._disposers;
        if (disposers) {
            disposers.map(function (dispose) { return dispose(); });
            disposers.splice(0, disposers.length);
        }
        if (!noRecursive) {
            disposeWidgets(widget.children);
        }
    });
}
//# sourceMappingURL=widgets.js.map