"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkObserverInstanceRef = exports.generateWidgetAPIContext = exports.UserWidget = exports.createWidgets = exports.retryDataBinds = exports.findWidgets = exports.getDom = exports.checkDisplay = exports.checkVisible = exports.WidgetsContext = exports.WIDGET_API_MAP = exports.widgetKeys = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const mobx_1 = require("mobx");
const lodash_es_1 = require("lodash-es");
const eventProxy_1 = require("./eventProxy");
const adapter_1 = require("./adapter");
const common_1 = require("./common");
const constants_1 = require("./constants");
const global_api_1 = require("../app/global-api");
const debugExpr_1 = require("./debugExpr");
const WIDGET_API_LIST = [
    'getConfig',
    'findWidgets',
    'getWidgetsByType',
    'extends',
    'addWidgetsListener',
    'removeWidgetsListener',
    'getOwnerWidget',
    'getDom',
    'domRef',
    '_listeners',
    '_instanceRef',
    '_getInstanceRef',
    '_scope',
    '_descendants',
    '_userWidget',
];
exports.widgetKeys = [
    ...WIDGET_API_LIST,
    '_key',
    'parent',
    '_visible',
    '_display',
    '_parentId',
    '_waForKey',
    '_disposers',
];
exports.WIDGET_API_MAP = WIDGET_API_LIST.reduce((map, key) => {
    map[key] = true;
    return map;
}, {});
exports.WidgetsContext = React.createContext({ parent: null });
function checkVisible({ id, _visible = undefined }) {
    return _visible !== false && _visible !== '';
}
exports.checkVisible = checkVisible;
function checkDisplay({ id, _display = true }) {
    return _display !== false && _display !== '';
}
exports.checkDisplay = checkDisplay;
function getDom(element, options) {
    if (!element) {
        console.warn('getDom 接口未传入有效的 element');
        return Promise.resolve({});
    }
    let result = {};
    if (options.id)
        result.id = element.id;
    if (options.dataset)
        result.dataset = element.dataset;
    if (options.rect) {
        const rect = element.getBoundingClientRect();
        Object.assign(result, {
            left: rect.left,
            right: rect.right,
            top: rect.top,
            bottom: rect.bottom,
        });
    }
    if (options.size) {
        const 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((propName) => {
            result[propName] = element.getAttribute(propName);
        });
    }
    if (options.computedStyle && Array.isArray(options.computedStyle)) {
        const computedStyle = adapter_1.runtime.getComputedStyle(element);
        options.computedStyle.forEach((propName) => {
            result[propName] = computedStyle[propName];
        });
    }
    return Promise.resolve(result);
}
exports.getDom = getDom;
function findWidgets(widget, parentType, filterFn, includeInvisibleDescendants) {
    if (!widget)
        return [];
    let { children = [] } = widget;
    let matched = children.filter(filterFn);
    // 过滤掉不可见后代
    if (!includeInvisibleDescendants) {
        children = children.filter((item) => checkVisible(item));
    }
    children.forEach((child) => {
        // 如果递归过程中发现了自己，则停止递归
        matched = matched.concat(findWidgets(child, parentType, filterFn, includeInvisibleDescendants));
    });
    // 过滤掉不可见的匹配项
    if (!includeInvisibleDescendants) {
        matched = matched.filter((item) => checkVisible(item));
    }
    return matched;
}
exports.findWidgets = findWidgets;
let retryQueue = [];
// 递归执行
function retryDataBinds(tryTime = 10) {
    const fn = retryQueue.shift();
    if (!fn || tryTime <= 0) {
        retryQueue = [];
        return;
    }
    try {
        fn({ silent: tryTime > 1 });
    }
    catch (e) {
        console.error('retryDataBinds', e);
    }
    retryDataBinds(tryTime - 1);
}
exports.retryDataBinds = retryDataBinds;
function createWidgets(ctx, widgetProps, dataBinds, controledDataContext, widgets) {
    const globalCtx = ctx.globalCtx || null;
    const { nodeTree, nodeMap } = createWidgetTree(ctx.nodeMap, ctx.widgetProps, widgetProps, dataBinds);
    /**
     * 函数存在副作用
     * 因为构建过程中立即就对当前值进行了访问
     */
    let widgetsResult = createSubTree({ apiContext: ctx.apiContext, preWidgets: ctx.widgets, parentLevelWidgets: null }, nodeTree, {}, controledDataContext, widgets);
    return { widgets: widgetsResult, nodeMap };
    /**
     * @param {*} pre widgets
     * @param {*} curForNode
     * @param {*} forItems
     * @param {*} parentLevelWidgets
     * @returns for dispose
     */
    function runFor(apiContext, preWidgets, curForNode, forItems, parentLevelWidgets, controledDataContext) {
        const parentWidget = getNodeParentWidget(curForNode, parentLevelWidgets);
        /**
         * 组件节点
         */
        const nodeId = curForNode.id;
        const { _waForKey } = curForNode.value;
        const timer = {};
        const dispose = (0, mobx_1.autorun)((r) => {
            let forList = [];
            try {
                clearTimeout(timer[nodeId]);
                /**
                 * untrace 收集context 阶段不添加observer
                 * 消费数据阶段再添加
                 */
                const dataContext = (0, mobx_1.untracked)(() => (0, common_1.generateDataContext)(parentWidget));
                // TODO: forContext
                const $scope = (0, mobx_1.untracked)(() => generateScopeContext(parentWidget));
                const $w = (0, mobx_1.untracked)(() => { var _a; return generateWidgetAPIContext((_a = apiContext === null || apiContext === void 0 ? void 0 : apiContext.__internal__) === null || _a === void 0 ? void 0 : _a.$w, parentWidget, forItems, $scope); });
                const list = dataBinds[nodeId]._waFor(forItems, undefined, dataContext, $w, $scope);
                if (!Array.isArray(list)) {
                    timer[nodeId] = setTimeout(() => {
                        console.warn(nodeId, 'For 循环绑定的数据并不是数组，请检查');
                    }, 1000);
                }
                else {
                    forList = list;
                }
            }
            catch (e) {
                if ((window === null || window === void 0 ? void 0 : window.__WEDA_LOG_LEVEL__) === 'debug') {
                    timer[nodeId] = setTimeout(() => {
                        console.debug(`${nodeId}循环绑定错误`, e);
                    }, 1000);
                }
            }
            // r.trace();
            // 让 forList 进行监听
            forList.forEach(() => { });
            (0, mobx_1.untracked)(() => {
                // dispose widgets before reused instead
                // disposeWidgets(parentLevelWidgets[curForNode.id]);
                let exsitMap = forList.reduce((map, item, index) => {
                    const cache = item === null || item === void 0 ? void 0 : item[_waForKey];
                    if (map[cache] === undefined) {
                        map[cache] = index;
                    }
                    return map;
                }, {});
                // exsitMap = {};
                const forWidgets = (parentLevelWidgets === null || parentLevelWidgets === void 0 ? void 0 : parentLevelWidgets[nodeId]) || [];
                const existingWidgetMap = {};
                const existingWidgetIndexMap = {};
                const extraWidgetsIndexMap = {};
                forWidgets.forEach((widget, index) => {
                    if (exsitMap[widget._key] !== undefined) {
                        existingWidgetMap[widget._key] = { index: exsitMap[widget._key], widgets: { [widget.id]: widget } };
                        /**
                         * 此处依赖了 existingWidgetMap[widget._key].widgets 的引用，
                         * 为了直接可以通过 index 访问到 existingWidgetMap 里的值进行编辑
                         * 但是依赖引用关系，存在维护风险
                         */
                        existingWidgetIndexMap[index] = existingWidgetMap[widget._key].widgets;
                        // need to use uqique key
                        exsitMap[widget._key] = undefined;
                    }
                    else {
                        extraWidgetsIndexMap[index] = widget;
                    }
                });
                // clean nodes of previouse for run
                dfsTree(curForNode, (node) => {
                    const arr = parentLevelWidgets[node.id] || [];
                    /**
                     * clone 上次 for 已有的 widgets
                     */
                    if (node.id !== curForNode.id) {
                        arr.forEach((item, index) => {
                            if (existingWidgetIndexMap[index]) {
                                existingWidgetIndexMap[index][node.id] = item;
                            }
                        });
                    }
                    /**
                     * 重头开始生成
                     * 清空原有 arr 并保持引用不变
                     */
                    arr.splice(0, arr.length);
                }, undefined);
                /**
                 * 明确已经不会复用的节点，清除 mobx observer
                 * 并递归清理子节点
                 */
                for (const index in extraWidgetsIndexMap) {
                    disposeWidgets([extraWidgetsIndexMap[index]]);
                }
                if (parentWidget) {
                    (0, lodash_es_1.remove)(parentWidget.children, ({ id }) => id === nodeId);
                }
                const isInRepeaterChild = (parentWidget === null || parentWidget === void 0 ? void 0 : parentWidget.widgetType) === `${constants_1.REPEATER.MODULE_NAME}:${constants_1.REPEATER.REPEATER_NAME}` ||
                    (parentWidget === null || parentWidget === void 0 ? void 0 : parentWidget.widgetType) === `${constants_1.REPEATER.DEBUG_MODULE_NAME}:${constants_1.REPEATER.REPEATER_NAME}`;
                forList.forEach((item, index) => {
                    let forContextListAlias;
                    const { forStack = [] } = forItems;
                    const listMeta = { currentItem: item, currentIndex: index };
                    if (isInRepeaterChild) {
                        forContextListAlias = {
                            [`${parentWidget.forIndex}` || 'currentIndex']: listMeta.currentIndex,
                            [`${parentWidget.forItem}` || 'currentItem']: listMeta.currentItem,
                        };
                    }
                    const subForItems = Object.assign(Object.assign({}, forItems), { [nodeId]: item, forIndexes: [...((forItems === null || forItems === void 0 ? void 0 : forItems.forIndexes) || [])].concat(listMeta.currentIndex), forStack: [
                            ...forStack,
                            Object.assign(Object.assign({}, listMeta), { alias: forContextListAlias }),
                        ] });
                    createSubTree({ apiContext, preWidgets, parentLevelWidgets, existingWidgetMap }, curForNode, subForItems, controledDataContext);
                });
                // 非初始化时遇到需要重新构建 dataBinds
                retryDataBinds();
            });
        });
        return dispose;
    }
    function createSubTree(ctx, curForNode, subForItems, controledDataContext, widgets = { _disposers: [] }) {
        const { apiContext, preWidgets, parentLevelWidgets, existingWidgetMap = {} } = ctx;
        const parentWidget = getNodeParentWidget(curForNode, parentLevelWidgets);
        /**
         * 因为 构建 widgets 过程中对 $page.widgets 进行了访问，需要使用副作用的方式，保持 $page.widgets 引用
         */
        // const widgets = { _disposers: [] };
        if (!widgets._disposers) {
            widgets._disposers = [];
        }
        const { forIndexes = [] } = subForItems;
        const currentIndex = forIndexes[(forIndexes === null || forIndexes === void 0 ? void 0 : forIndexes.length) - 1];
        // traverse down the tree to set all widgets
        dfsTree(curForNode, (node, parentNode, cache) => {
            var _a, _b, _c;
            const parentForWidgetArr = (parentLevelWidgets === null || parentLevelWidgets === void 0 ? void 0 : parentLevelWidgets[node.id]) || [];
            const { _waForKey } = node.value;
            const key = (_a = subForItems[node.id]) === null || _a === void 0 ? void 0 : _a[_waForKey];
            let forExistingWidgetMap = {};
            let forExsitWidget;
            /**
             * for 起始节点，根据 existingWidgetMap 判断复用
             */
            if (node.id === curForNode.id) {
                if (existingWidgetMap[key] && existingWidgetMap[key].index === currentIndex) {
                    forExistingWidgetMap = existingWidgetMap[key].widgets || {};
                    forExsitWidget = forExistingWidgetMap[node.id];
                    if (forExsitWidget) {
                        cache[node.id] = {
                            widgets: forExistingWidgetMap,
                        };
                    }
                }
            }
            else if (cache[parentNode === null || parentNode === void 0 ? void 0 : parentNode.id]) {
                forExsitWidget = cache[parentNode === null || parentNode === void 0 ? void 0 : parentNode.id].widgets[node.id] || null;
                if (forExsitWidget) {
                    cache[node.id] = cache[parentNode === null || parentNode === void 0 ? void 0 : parentNode.id];
                }
            }
            const preExistWidget = node.exist
                ? Array.isArray(preWidgets === null || preWidgets === void 0 ? void 0 : preWidgets[node.id])
                    ? (0, lodash_es_1.get)(preWidgets === null || preWidgets === void 0 ? void 0 : preWidgets[node.id], subForItems.forIndexes)
                    : preWidgets === null || preWidgets === void 0 ? void 0 : preWidgets[node.id]
                : null;
            const existedWidget = forExsitWidget || preExistWidget || null;
            if (node.forCount === curForNode.forCount) {
                /**
                 * 同一层循环作用域内，当前节点与 curForNode（循环根节点）在同一级循环作用域中
                 * 即没有再开辟新级别的 for 循环
                 * Leaf node
                 */
                let w = existedWidget;
                if (!existedWidget) {
                    w = (0, mobx_1.observable)(Object.assign(Object.assign({}, node.value), { id: node.id, 
                        /**
                         * TODO: parent children 的维护，
                         * widget 挂载/卸载 要修改对应的 _descendants
                         */
                        parent: null, children: [], _key: key, _scope: {
                            id: node.id,
                            dataContext: controledDataContext[node.id] || undefined,
                            forContext: subForItems[node.id],
                        }, _disposers: [], _listeners: new eventProxy_1.EventProxy(), _descendants: {} }));
                    // 提供一个给 Node 挂载 API 的方式
                    (0, mobx_1.untracked)(() => {
                        if (!w.getConfig) {
                            // 兼容非复合组件调用 getConfig 能力
                            w.getConfig = () => ({});
                        }
                        w.findWidgets = (type, includeInvisibleDescendants) => findWidgets(w, w.widgetType, type, includeInvisibleDescendants);
                        w.getWidgetsByType = (type, includeInvisibleDescendants) => w.findWidgets((currentWidget) => currentWidget.widgetType === type, includeInvisibleDescendants);
                        w.extends = (name, fnOrData) => Object.defineProperty(w, name, {
                            value: fnOrData,
                            writable: true,
                        });
                        w.addWidgetsListener = (name, listener) => w._listeners.on(name, listener);
                        w.removeWidgetsListener = (name, listener) => w._listeners.cancel(name, listener);
                        w.getOwnerWidget = () => null; // 默认带上一个 widget
                        // eslint-disable-next-line react/display-name
                        const instanceRef = React.createRef();
                        if (checkObserverInstanceRef(w.widgetType)) {
                            w._instanceRef = instanceRef;
                        }
                        else {
                            Object.defineProperty(w, '_instanceRef', {
                                get() {
                                    return instanceRef;
                                },
                            });
                        }
                        w._getInstanceRef = () => w._instanceRef; // 默认初始值
                        Object.defineProperty(w, '_methods', {
                            get() {
                                var _a;
                                const instance = this._getInstanceRef();
                                return (_a = instance === null || instance === void 0 ? void 0 : instance.current) === null || _a === void 0 ? void 0 : _a.methods;
                            },
                        });
                        /**
                         * UserWidget
                         */
                        w._userWidget = new UserWidget(w);
                        /**
                         * 该 widget 为 Repeater 组件
                         */
                        switch (w.widgetType) {
                            case `${constants_1.REPEATER.MODULE_NAME}:${constants_1.REPEATER.REPEATER_NAME}`:
                            case `${constants_1.REPEATER.DEBUG_MODULE_NAME}:${constants_1.REPEATER.REPEATER_NAME}`: {
                                // Repeater.items 代理
                                if (!w.items) {
                                    Object.defineProperty(w, 'items', {
                                        get() {
                                            return (w.children || []).map((item) => {
                                                const descendants = {};
                                                Object.keys((item === null || item === void 0 ? void 0 : item._descendants) || {}).forEach((key) => {
                                                    descendants[key] = item._descendants[key]._userWidget;
                                                });
                                                return descendants;
                                            });
                                        },
                                    });
                                }
                            }
                        }
                    });
                }
                else {
                    if (!forExsitWidget && preExistWidget) {
                        if (node.changed) {
                            for (const key of node.changed) {
                                w[key] = node.value[key];
                            }
                        }
                        /**
                         * 复用
                         * 组件 tree 发生变化时，叶子节点 children 进行一次清理
                         */
                        if (!((_b = node.children) === null || _b === void 0 ? void 0 : _b.length)) {
                            w.children = [];
                        }
                        // 仅仅为了受控模式下更新 dataContext
                        if (controledDataContext === null || controledDataContext === void 0 ? void 0 : controledDataContext[w.id]) {
                            w._scope.dataContext = controledDataContext[w.id];
                        }
                    }
                    disposeWidgets([existedWidget], true);
                }
                /**
                 * 1. 正常新建widgets，父亲记录一下
                 * 2. for 循环清理了 curForNode.parent.children，此处若children 复用，则继承一下
                 */
                if (!existedWidget || (node.id === curForNode.id && !forExsitWidget && preExistWidget)) {
                    const parent = parentNode ? widgets[parentNode.id] : parentWidget;
                    if (parent) {
                        w.parent = parent;
                        // 只有可显示 visible 的才存入 children 里
                        if (checkVisible(w)) {
                            parent.children.push(w);
                        }
                    }
                }
                (_c = parentForWidgetArr.push) === null || _c === void 0 ? void 0 : _c.call(parentForWidgetArr, w);
                widgets[node.id] = w;
                /**
                 * TODO: _descendants map 的维护，widget 挂载/卸载 要修改对应的 _descendants
                 */
                if (widgets === null || widgets === void 0 ? void 0 : widgets[node._ancestorId]) {
                    // 在虚拟项 RepeaterItem 下挂载所有的子孙 _descendants
                    // 这里不要用 lodashSet，否则会出现刷新几次，出现 Error: [mobx.array] Index out of bounds 的错误，原因暂不明
                    widgets[node._ancestorId]._descendants = widgets[node._ancestorId]._descendants || {};
                    widgets[node._ancestorId]._descendants[node.id] = widgets[node.id];
                }
                /**
                 * 组件节点的表达式执行
                 * 只在编辑器中开启调试表达式的功能
                 */
                const forDebugExprDispose = global_api_1.$w.wedaContext.isEditorMode
                    ? (0, debugExpr_1.watchForDebugExpr)({
                        getDataContextFn: () => (0, common_1.generateDataContext)(w),
                        get$wFn: () => { var _a; return generateWidgetAPIContext((_a = apiContext === null || apiContext === void 0 ? void 0 : apiContext.__internal__) === null || _a === void 0 ? void 0 : _a.$w, w, subForItems, generateScopeContext(w)); },
                        get$scopeFn: () => generateScopeContext(w),
                        widgetId: node.id,
                        subForItems,
                        globalCtx,
                    })
                    : lodash_es_1.noop;
                if (curForNode.id) {
                    widgets[curForNode.id]._disposers.push(forDebugExprDispose);
                }
                else {
                    widgets._disposers.push(forDebugExprDispose);
                }
                // Setup data binds
                Object.keys(dataBinds[node.id] || {})
                    .sort((a, b) => {
                    return a.length - b.length > 0 ? 1 : -1;
                })
                    .map((prop) => {
                    if (prop === '_waFor') {
                        return;
                    }
                    let timer = null;
                    function getBindData(curForNodeId, options = { silent: false }) {
                        let disposeError = false;
                        const dispose = (0, mobx_1.autorun)((reaction) => {
                            // reaction.trace();
                            try {
                                clearTimeout(timer);
                                /**
                                 * untrace 收集context 阶段不添加observer
                                 * 消费数据阶段再添加
                                 */
                                const dataContext = (0, mobx_1.untracked)(() => (0, common_1.generateDataContext)(w));
                                const $scope = (0, mobx_1.untracked)(() => generateScopeContext(w));
                                const $w = (0, mobx_1.untracked)(() => { var _a; return generateWidgetAPIContext((_a = apiContext === null || apiContext === void 0 ? void 0 : apiContext.__internal__) === null || _a === void 0 ? void 0 : _a.$w, w, subForItems, $scope); });
                                // Computed data bind in the next tick since data bind may read widgets data
                                let value = dataBinds[node.id][prop](subForItems, undefined, dataContext, $w, $scope);
                                if ((prop === '_visible' || prop === '_display') && value === undefined) {
                                    value = false;
                                }
                                const paths = prop.split('.').filter((key) => !!key);
                                if (paths.length > 1) {
                                    // 一定要 untracked 不然爆栈了
                                    (0, mobx_1.untracked)(() => {
                                        const pre = (0, lodash_es_1.get)(w, prop);
                                        (0, lodash_es_1.set)(w, prop, value);
                                        /**
                                         * 深层的对象内部值变化不会触发 obverver，因此浅拷贝对象
                                         */
                                        if (Array.isArray(w[paths[0]])) {
                                            if (!(0, lodash_es_1.isEqual)(pre, value)) {
                                                w[paths[0]] = [...w[paths[0]]];
                                            }
                                        }
                                        else if ((0, common_1.isPlainObject)(w[paths[0]])) {
                                            if (!(0, lodash_es_1.isEqual)(pre, value)) {
                                                w[paths[0]] = Object.assign({}, w[paths[0]]);
                                            }
                                        }
                                    });
                                }
                                else {
                                    // 普通 key 直接赋值
                                    w[prop] = value;
                                }
                                disposeError = false;
                            }
                            catch (e) {
                                if (prop === '_visible' || prop === '_display') {
                                    w[prop] = false;
                                }
                                else {
                                    retryQueue.push((opts) => {
                                        reaction.dispose();
                                        return getBindData(curForNodeId, opts);
                                    });
                                    disposeError = true;
                                }
                                if (!(options === null || options === void 0 ? void 0 : options.silent)) {
                                    if ((window === null || window === void 0 ? void 0 : window.__WEDA_LOG_LEVEL__) === 'debug') {
                                        timer = setTimeout(() => {
                                            console.debug(`数据绑定计算错误 ${w.id}.${prop}`, e);
                                            timer = null;
                                        }, 1000);
                                    }
                                }
                            }
                        });
                        if (!disposeError) {
                            if (curForNodeId) {
                                widgets[curForNode.id]._disposers.push(dispose);
                            }
                            else {
                                widgets._disposers.push(dispose);
                            }
                        }
                    }
                    getBindData(curForNode.id);
                });
            }
            else {
                if (existedWidget && forExsitWidget) {
                    widgets[node.id] = existedWidget;
                }
                else {
                    widgets[node.id] = (0, mobx_1.observable)([]);
                }
                if (parentLevelWidgets) {
                    parentLevelWidgets[node.id].push(widgets[node.id]);
                }
            }
        }, undefined);
        /**
         * run for of next level
         * 此处本质上再递归调用多级 For 生成 tree，
         * dfs 前(enter) 保证本级 widgets 建立完成，下一级 For 绑定都为空数组
         * dfs 中 递归下级 For 来向空数组中 push 值
         * dfs 后(exit) 本级所有的 widgets 都已经建立完成可以使用
         * 但当 root -> A -> B 三层结构时发现顺序为
         * root enter -> A enter -> A exit -> B enter -> B exit -> root exit
         * 不符合递归处理期望
         * 查到进展为：dfs B 时 runfor autorun 直接返回了值，然后 callback 才会再触发，导致先走到了 A 的退出逻辑
         * autorun 收 mobx Reaction 调度， 根据 autorun 的逻辑，最终 reaction.getDisposer()，dispose 里会 startBath 可能和此处有关
         */
        dfsTree(curForNode, (node, parentNode) => {
            if (node.forCount === curForNode.forCount + 1 && dataBinds[node.id] && dataBinds[node.id]._waFor) {
                /**
                 * node 上有 for 绑定，widgets[node.id] 为数组，不应对数组赋值
                 * 此外 _disposers 都为数组类型，非对象类型
                 * orgin: widgets[node.id]._disposers = { dataBinds: [] };
                 */
                const dispose = runFor(apiContext, preWidgets, node, subForItems, widgets, controledDataContext);
                curForNode.id && widgets[curForNode.id]._disposers.push(dispose);
            }
        }, undefined);
        return widgets;
    }
}
exports.createWidgets = createWidgets;
function getNodeParentWidget(node, widgets) {
    var _a;
    return ((_a = node === null || node === void 0 ? void 0 : node.parent) === null || _a === void 0 ? void 0 : _a.id) && (widgets === null || widgets === void 0 ? void 0 : widgets[node.parent.id]);
}
function createWidgetTree(preNodes, preWidgets, widgets, dataBinds) {
    const virtualRoot = { children: [], forCount: 0 };
    const nodes = Object.keys(widgets).reduce((result, id) => {
        const currentValue = widgets[id];
        result[id] = {
            id,
            value: currentValue,
            children: [],
            parent: null,
            forCount: 0,
            exist: false,
            changed: [],
        };
        return result;
    }, {});
    // Create widgets tree API
    Object.keys(nodes).map((id) => {
        const curNode = nodes[id];
        const 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;
            const { widgetType } = node.parent.value;
            if (widgetType === `${constants_1.REPEATER.MODULE_NAME}:${constants_1.REPEATER.REPEATER_ITEM_NAME}` ||
                widgetType === `${constants_1.REPEATER.DEBUG_MODULE_NAME}:${constants_1.REPEATER.REPEATER_ITEM_NAME}`) {
                // 从 Repeater 的循环虚拟项下的结点开始，结点的循环祖先是循环虚拟项
                node._ancestorId = node.parent.id;
            }
            if (node.parent._ancestorId) {
                // Repeater 作用域内的所有子孙（排除里面的 Repeater 组件，它本身又产生深一层的作用域）继承父级的循环信息
                if (!node._ancestorId) {
                    node._ancestorId = node.parent._ancestorId;
                }
            }
        }
        if ((_a = dataBinds[node.id]) === null || _a === void 0 ? void 0 : _a._waFor) {
            node.forCount += 1;
        }
        node.children.map(addForCount);
    }
    dfsTree(virtualRoot, (node, parent) => {
        const currentValue = node.value;
        const preValue = preWidgets === null || preWidgets === void 0 ? void 0 : preWidgets[node.id];
        const preNode = preNodes === null || preNodes === void 0 ? void 0 : preNodes[node.id];
        let exist = false;
        const changed = [];
        /**
         * 多级for绑定的情况会发生父for不变化，子for更新的情况，此时子for生成会有异常，因此复用只复用一层保证渲染正常
         * https://lcap.cloud.tencent.com/preview/?u=https%3A%2F%2Fci-source-1303824488.cos.ap-shanghai.myqcloud.com%2Flcp%2Fapp-h057yqaw%2Fappdata-1666253755745.json&appId=app-h057yqaw#/nav
         */
        if ((!parent || (parent === null || parent === void 0 ? void 0 : parent.exist)) && preValue && preNode) {
            if (preValue.widgetType === (currentValue === null || currentValue === void 0 ? void 0 : currentValue.widgetType) &&
                preValue._parentId === currentValue._parentId &&
                preNode.forCount === node.forCount) {
                exist = true;
                for (let key in currentValue) {
                    if (!exports.WIDGET_API_MAP[key] && !(0, lodash_es_1.isEqual)(preValue[key], currentValue[key])) {
                        changed.push(key);
                    }
                }
                for (let key in preValue) {
                    if (!exports.WIDGET_API_MAP[key] && !Object.prototype.hasOwnProperty.call(currentValue, key)) {
                        changed.push(key);
                    }
                }
            }
            node.exist = exist;
            node.changed = changed;
        }
    }, undefined);
    return { nodeTree: virtualRoot, nodeMap: nodes };
}
function dfsTree(node, fn, parent, cache = {}, scope = { dataContext: {} }) {
    if (node.value) {
        fn(node, parent, cache, scope);
    }
    node.children.map((e) => dfsTree(e, fn, node.value ? node : null, cache, scope));
}
/**
 * dispose autorun
 * TODO: 是否要同步清理 parent children 引用，防止内存泄漏
 */
function disposeWidgets(widgets = [], noRecursive = false) {
    widgets.forEach((widget) => {
        /**
         * 解除 parent 依赖
         */
        // widget.parent = null;
        const disposers = widget._disposers;
        if (disposers) {
            disposers.map((dispose) => dispose());
            disposers.splice(0, disposers.length);
        }
        if (!noRecursive) {
            disposeWidgets(widget.children);
            /**
             * 解除children 的引用
             */
            // widget.children = [];
        }
    });
}
/**
 * 对外（用户）的 Widget
 */
class UserWidget {
    constructor(widget) {
        this._widget = null;
        this._widget = widget;
        return new Proxy(this, {
            get(target, prop) {
                if (prop in target) {
                    return target[prop];
                }
                const custom = target.custom;
                /**
                 * @important
                 * 优先 custom
                 * 会出现一些 target 时序还不存在的情况
                 * 因此 custom[prop] 先添加进依赖
                 */
                if ((custom === null || custom === void 0 ? void 0 : custom[prop]) !== undefined && prop in custom) {
                    return custom[prop];
                }
                // 再尝试内置的方法和属性
                if (prop in target.sys) {
                    return target.sys[prop];
                }
                return undefined;
            },
            /**
             * userwidget 不可写
             */
            set() {
                return false;
            },
        });
    }
    get description() {
        const { id } = this._widget;
        return `
    使用说明：
    1. sys 命名空间下为内置方法，可通过 $w.${id}.sys.<成员> 或 $w.${id}.<成员> 访问。
       访问 id 示例: $w.${id}.id 或 $w.${id}.sys.id
    2. custom 命名空间下为用户自定义对外暴露的成员，可通过 $w.${id}.custom.<成员> 或 $w.${id}.<成员> 访问。
       访问自定义方法 hello 示例: $w.${id}.hello() 或 $w.${id}.custom.hello()
    3. 通过 $w.${id}.<成员> 访问时，如果自定义成员与内置成员重名，则自定义成员覆盖内置成员。
    4. [注意]: 请不要直接访问 _widget，它里面的成员为内部实现，随时可能进行调整
    `;
    }
    // 内置属性和方法命名空间
    get sys() {
        var _a, _b;
        const widget = this._widget;
        const [module, component] = ((_b = (_a = widget.widgetType) === null || _a === void 0 ? void 0 : _a.split) === null || _b === void 0 ? void 0 : _b.call(_a, ':')) || [];
        return {
            /**
             * 内置属性
             */
            get id() {
                return widget.id;
            },
            get module() {
                return module;
            },
            get component() {
                return component;
            },
            get name() {
                return widget.widgetType;
            },
            get parent() {
                var _a;
                return (_a = widget.parent) === null || _a === void 0 ? void 0 : _a._userWidget;
            },
            get children() {
                var _a;
                return ((_a = widget.children) === null || _a === void 0 ? void 0 : _a.map((item) => item._userWidget)) || [];
            },
            /**
             * 内置方法
             */
            closest(filter) {
                let { parent } = this;
                if (!filter)
                    return parent;
                while (parent) {
                    const matched = filter(parent);
                    if (matched)
                        return parent;
                    parent = parent.parent;
                }
                return null;
            },
        };
    }
    get custom() {
        const userCustomMember = (0, mobx_1.untracked)(() => {
            var _a, _b;
            const instanceRef = (_b = (_a = this._widget)._getInstanceRef) === null || _b === void 0 ? void 0 : _b.call(_a);
            const _c = (instanceRef === null || instanceRef === void 0 ? void 0 : instanceRef.current) || {}, { methods = {} } = _c, restInstance = tslib_1.__rest(_c, ["methods"]);
            const widget = this._widget;
            const proxyTarget = Object.assign(Object.assign(Object.assign({}, widget._methods), restInstance), methods);
            return new Proxy(proxyTarget, {
                get(_, prop) {
                    var _a, _b;
                    if (prop in methods) {
                        return methods[prop];
                    }
                    /**
                     * 此处要保持从 ref 开始访问
                     * 因为proxy 内部如果直接从 ref.current 开始访问，刚开始是可能遇到的是空值，并且空对象兜底
                     * 那么此处实际上监听的即为兜底的空对象，非 observer 对象
                     */
                    if (prop !== 'methods' && prop in (instanceRef.current || {})) {
                        return (_a = instanceRef.current) === null || _a === void 0 ? void 0 : _a[prop];
                    }
                    return (_b = widget === null || widget === void 0 ? void 0 : widget._methods) === null || _b === void 0 ? void 0 : _b[prop];
                },
            });
        });
        return userCustomMember;
    }
}
exports.UserWidget = UserWidget;
function generateWidgetAPIContext($w = {}, widget, forContext, scopeContext = {}) {
    return new Proxy($w, {
        get(target, prop) {
            // 尝试代理 currentItem 和 currentIndex
            // 用 slice 确保 forStack 的长度不会超过 forIndexes
            const { forStack = [] } = forContext;
            /**
             * for context 变量优先
             * 从后往前找，即最深层循环优先
             */
            for (let i = forStack.length - 1; i >= 0; i--) {
                const meta = forStack[i];
                const map = meta.alias || {};
                if (prop in map) {
                    return map[prop];
                }
            }
            const res = (function findWidgets() {
                var _a;
                // 尝试代理同级 widget
                if (widget) {
                    let { parent } = widget;
                    while (parent) {
                        if ((_a = parent._descendants) === null || _a === void 0 ? void 0 : _a[prop]) {
                            return parent._descendants[prop]._userWidget;
                        }
                        parent = parent.parent;
                    }
                }
                // 尝试代理到全局的 $w
                // 这里如果不加 $page.widgets[prop] 会因为 window 还没有 $page 而发生错误
                return target[prop];
                // return ($page.widgets[prop] && $page.widgets[prop]._userWidget) || window.$w[prop];
            })();
            /**
             * 如果是作用域插槽，则合并插槽动态访问 与 widget 静态访问
             * 否则只包含 widget
             */
            const id = prop;
            const scope = (0, mobx_1.untracked)(() => scopeContext[id]);
            return !scope
                ? res
                : new Proxy(res, {
                    get(target, prop) {
                        if (prop in scope) {
                            /**
                             * 一定要访问原始值，不然就 observer 不到
                             */
                            return scopeContext[id][prop];
                        }
                        return target[prop];
                    },
                });
        },
    });
}
exports.generateWidgetAPIContext = generateWidgetAPIContext;
function generateScopeContext(widget) {
    var _a;
    const scopeContext = {};
    while (widget === null || widget === void 0 ? void 0 : widget._scope) {
        const current = widget;
        const { id } = current._scope;
        if (id.includes('.')) {
            const [componentId, slotName] = id.split('.');
            Object.defineProperty(scopeContext, componentId, {
                get() {
                    return { [slotName]: current._scope.scopeContext };
                },
            });
            widget = (_a = widget.parent) === null || _a === void 0 ? void 0 : _a.parent;
        }
        else {
            // 此处采用代理的方式，是为了可以获取到最新的 _scope.dataContext 防止 dataContext 引用被重新赋值
            Object.defineProperty(scopeContext, id, {
                get() {
                    return current._scope.scopeContext;
                },
            });
            widget = widget.parent;
        }
    }
    return scopeContext;
}
function checkObserverInstanceRef(sourceKey = '') {
    return !(sourceKey === null || sourceKey === void 0 ? void 0 : sourceKey.startsWith('CLOUDBASE_BUSSINESS')) && common_1.ENABLE_OBSERVER_REF;
}
exports.checkObserverInstanceRef = checkObserverInstanceRef;
//# sourceMappingURL=widgets.js.map