"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importDefault(require("react"));
var qs_1 = tslib_1.__importDefault(require("qs"));
var index_1 = require("./store/index");
var mobx_state_tree_1 = require("mobx-state-tree");
var api_1 = require("./utils/api");
var helper_1 = require("./utils/helper");
var mobx_react_1 = require("mobx-react");
var filter_schema_1 = tslib_1.__importDefault(require("./utils/filter-schema"));
var hoistNonReactStatic = require("hoist-non-react-statics");
var omit = require("lodash/omit");
var difference = require("lodash/difference");
var isPlainObject = require("lodash/isPlainObject");
var Scoped_1 = tslib_1.__importDefault(require("./Scoped"));
var theme_1 = require("./theme");
var find = require("lodash/find");
var Alert2_1 = tslib_1.__importDefault(require("./components/Alert2"));
var components_1 = require("./components");
;
;
;
;
;
var renderers = [];
var rendererNames = [];
var schemaFilters = [];
var anonymousIndex = 1;
function addSchemaFilter(fn) {
    schemaFilters.push(fn);
}
exports.addSchemaFilter = addSchemaFilter;
function filterSchema(schema, render, props) {
    return schemaFilters.reduce(function (schema, filter) { return filter(schema, render, props); }, schema);
}
exports.filterSchema = filterSchema;
function Renderer(config) {
    return function (component) {
        var renderer = registerRenderer(tslib_1.__assign({}, config, { component: component }));
        return renderer.component;
    };
}
exports.Renderer = Renderer;
function registerRenderer(config) {
    if (!config.test) {
        throw new TypeError("config.test is required");
    }
    else if (!config.component) {
        throw new TypeError("config.component is required");
    }
    config.weight = config.weight || 0;
    config.Renderer = config.component;
    config.name = config.name || "anonymous-" + anonymousIndex++;
    if (~rendererNames.indexOf(config.name)) {
        throw new Error("The renderer with name \"" + config.name + "\" has already exists, please try another name!");
    }
    if (config.storeType && config.component) {
        config.component = HocStoreFactory({
            storeType: config.storeType,
            extendsData: config.storeExtendsData
        })(mobx_react_1.observer(config.component));
    }
    if (config.isolateScope) {
        config.component = Scoped_1.default(config.component);
    }
    var idx = helper_1.findIndex(renderers, function (item) { return config.weight < item.weight; });
    ~idx ? renderers.splice(idx, 0, config) : renderers.push(config);
    rendererNames.push(config.name);
    return config;
}
exports.registerRenderer = registerRenderer;
function unRegisterRenderer(config) {
    var idx = typeof config === 'string' ? helper_1.findIndex(renderers, function (item) { return item.name === config; }) : renderers.indexOf(config);
    ~idx && renderers.splice(idx, 1);
    // 清空渲染器定位缓存
    cache = {};
}
exports.unRegisterRenderer = unRegisterRenderer;
function renderChildren(prefix, node, props) {
    if (Array.isArray(node)) {
        return node.map(function (node, index) { return renderChild(prefix + "/" + index, node, tslib_1.__assign({}, props, { key: "" + (props.key ? props.key + "-" : '') + index })); });
    }
    return renderChild(prefix, node, props);
}
exports.renderChildren = renderChildren;
function renderChild(prefix, node, props) {
    if (Array.isArray(node)) {
        return renderChildren(prefix, node, props);
    }
    var typeofnode = typeof node;
    var schema = typeofnode === 'string' || typeofnode === 'number' ? { type: 'tpl', tpl: String(node) } : node;
    var detectData = props[schema.detectField || 'data'];
    var exprProps = detectData ? filter_schema_1.default(schema, detectData) : null;
    if (exprProps
        && (exprProps.hidden
            || exprProps.visible === false
            || schema.hidden
            || schema.visible === false
            || props.hidden
            || props.visible === false)) {
        return null;
    }
    return (react_1.default.createElement(SchemaRenderer, tslib_1.__assign({}, props, exprProps, { schema: schema, "$path": "" + (prefix ? prefix + "/" : '') + (schema && schema.type || '') })));
}
exports.renderChild = renderChild;
;
;
var RootStoreContext = react_1.default.createContext(undefined);
var RootRenderer = /** @class */ (function (_super) {
    tslib_1.__extends(RootRenderer, _super);
    function RootRenderer() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    RootRenderer.prototype.render = function () {
        var _a = this.props, schema = _a.schema, rootStore = _a.rootStore, env = _a.env, pathPrefix = _a.pathPrefix, location = _a.location, data = _a.data, rest = tslib_1.__rest(_a, ["schema", "rootStore", "env", "pathPrefix", "location", "data"]);
        var theme = env.theme;
        var query = location && location.query
            || location && location.search && qs_1.default.parse(location.search.substring(1))
            || window.location.search && qs_1.default.parse(window.location.search.substring(1));
        var finalData = query ? tslib_1.__assign({}, data, query, { query: query }) : data;
        return (react_1.default.createElement(RootStoreContext.Provider, { value: rootStore },
            react_1.default.createElement(theme_1.ThemeContext.Provider, { value: this.props.theme || 'default' }, renderChild(pathPrefix || '', isPlainObject(schema) ? tslib_1.__assign({ type: 'page' }, schema) : schema, tslib_1.__assign({}, rest, { data: finalData, env: env, classnames: theme.classnames, classPrefix: theme.classPrefix })))));
    };
    return RootRenderer;
}(react_1.default.Component));
exports.RootRenderer = RootRenderer;
exports.ScopedRootRenderer = Scoped_1.default(RootRenderer);
;
var defaultOmitList = [
    'type', 'className', 'data', 'children',
    'ref', 'visible', 'visibleOn', 'hidden',
    'hiddenOn', 'disabled', 'disabledOn',
    'children'
];
var SchemaRenderer = /** @class */ (function (_super) {
    tslib_1.__extends(SchemaRenderer, _super);
    function SchemaRenderer(props) {
        var _this = _super.call(this, props) || this;
        _this.refFn = _this.refFn.bind(_this);
        _this.renderChild = _this.renderChild.bind(_this);
        _this.reRender = _this.reRender.bind(_this);
        return _this;
    }
    SchemaRenderer.prototype.componentWillMount = function () {
        this.resolveSchema(this.props);
    };
    SchemaRenderer.prototype.componentWillReceiveProps = function (nextProps) {
        var props = this.props;
        if (props.schema.type !== nextProps.schema.type ||
            props.schema.$$id !== nextProps.schema.$$id) {
            this.resolveSchema(nextProps);
        }
    };
    // 限制：只有 schema 除外的 props 变化，或者 schema 里面的某个成员值发生变化才更新。
    SchemaRenderer.prototype.shouldComponentUpdate = function (nextProps) {
        var props = this.props;
        var list = difference(Object.keys(nextProps), ['schema']);
        if (difference(Object.keys(props), ['schema']).length !== list.length
            || helper_1.anyChanged(list, this.props, nextProps)) {
            return true;
        }
        else {
            var list_1 = Object.keys(nextProps.schema);
            if (Object.keys(props.schema).length !== list_1.length
                || helper_1.anyChanged(list_1, props.schema, nextProps.schema)) {
                return true;
            }
        }
        return false;
    };
    SchemaRenderer.prototype.resolveSchema = function (props) {
        var schema = props.schema;
        var path = props.$path;
        var rendererResolver = props.env.rendererResolver || resolveRenderer;
        this.renderer = rendererResolver(path, schema, props);
    };
    SchemaRenderer.prototype.getWrappedInstance = function () {
        return this.ref;
    };
    SchemaRenderer.prototype.refFn = function (ref) {
        this.ref = ref;
    };
    SchemaRenderer.prototype.renderChild = function (region, node, subProps) {
        if (subProps === void 0) { subProps = {}; }
        var _a = this.props, schema = _a.schema, $path = _a.$path, env = _a.env, rest = tslib_1.__rest(_a, ["schema", "$path", "env"]);
        var omitList = defaultOmitList.concat();
        if (this.renderer) {
            var Component = this.renderer.component;
            Component.propsList && omitList.push.apply(omitList, Component.propsList);
        }
        return renderChild("" + $path + (region ? "/" + region : ''), node || '', tslib_1.__assign({}, omit(rest, omitList), subProps, { data: subProps.data || rest.data, env: env }));
    };
    SchemaRenderer.prototype.reRender = function () {
        this.resolveSchema(this.props);
        this.forceUpdate();
    };
    SchemaRenderer.prototype.render = function () {
        var _this = this;
        var _a = this.props, schema = _a.schema, $path = _a.$path, rest = tslib_1.__rest(_a, ["schema", "$path"]);
        var theme = this.props.env.theme;
        if (Array.isArray(schema)) {
            return renderChildren($path, schema, rest);
        }
        else if (schema.children) {
            return typeof schema.children === 'function' ? schema.children(tslib_1.__assign({}, rest, { $path: $path, render: this.renderChild, ref: this.refFn }), schema) : schema.children;
        }
        else if (!this.renderer) {
            return (react_1.default.createElement(components_1.LazyComponent, tslib_1.__assign({}, rest, { getComponent: function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                    var result;
                    return tslib_1.__generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, rest.env.loadRenderer(schema, $path, this.reRender)];
                            case 1:
                                result = _a.sent();
                                if (result && typeof result === "function") {
                                    return [2 /*return*/, result];
                                }
                                else if (result && react_1.default.isValidElement(result)) {
                                    return [2 /*return*/, function () { return result; }];
                                }
                                this.reRender();
                                return [2 /*return*/, function () { return loadRenderer(schema, $path); }];
                        }
                    });
                }); }, "$path": $path, retry: this.reRender })));
        }
        var renderer = this.renderer;
        schema = filterSchema(schema, renderer, rest);
        var defaultData = schema.data, restSchema = tslib_1.__rest(schema, ["data"]);
        var Component = renderer.component;
        return (react_1.default.createElement(Component, tslib_1.__assign({}, theme.getRendererConfig(renderer.name), restSchema, rest, { defaultData: defaultData, "$path": $path, ref: this.refFn, render: this.renderChild })));
    };
    SchemaRenderer.displayName = 'Renderer';
    return SchemaRenderer;
}(react_1.default.Component));
function HocStoreFactory(renderer) {
    return function (Component) {
        var StoreFactory = /** @class */ (function (_super) {
            tslib_1.__extends(StoreFactory, _super);
            function StoreFactory() {
                return _super !== null && _super.apply(this, arguments) || this;
            }
            StoreFactory.prototype.getWrappedInstance = function () {
                return this.ref;
            };
            StoreFactory.prototype.refFn = function (ref) {
                this.ref = ref;
            };
            StoreFactory.prototype.formatData = function (data) {
                if (Array.isArray(data)) {
                    return {
                        items: data
                    };
                }
                return data;
            };
            StoreFactory.prototype.componentWillMount = function () {
                var rootStore = this.context;
                this.renderChild = this.renderChild.bind(this);
                this.refFn = this.refFn.bind(this);
                var store = this.store = rootStore.addStore({
                    id: helper_1.guid(),
                    path: this.props.$path,
                    storeType: renderer.storeType,
                    parentId: this.props.store ? this.props.store.id : ''
                });
                if (renderer.extendsData === false) {
                    store.initData(helper_1.createObject(this.props.data ? this.props.data.__super : null, tslib_1.__assign({}, this.formatData(this.props.defaultData), this.formatData(this.props.data))));
                }
                else if (this.props.scope || this.props.data && this.props.data.__super) {
                    if (this.props.store && this.props.data === this.props.store.data) {
                        store.initData(helper_1.createObject(this.props.store.data, tslib_1.__assign({}, this.formatData(this.props.defaultData))));
                    }
                    else {
                        store.initData(helper_1.createObject(this.props.data.__super || this.props.scope, tslib_1.__assign({}, this.formatData(this.props.defaultData), this.formatData(this.props.data))));
                    }
                }
                else {
                    store.initData(tslib_1.__assign({}, this.formatData(this.props.defaultData), this.formatData(this.props.data)));
                }
            };
            StoreFactory.prototype.componentWillReceiveProps = function (nextProps) {
                var props = this.props;
                var store = this.store;
                if (renderer.extendsData === false) {
                    (props.defaultData !== nextProps.defaultData || helper_1.isObjectShallowModified(props.data, nextProps.data))
                        && store.initData(helper_1.extendObject(nextProps.data, tslib_1.__assign({}, store.hasRemoteData ? store.data : null, this.formatData(nextProps.defaultData), this.formatData(nextProps.data))));
                }
                else if (helper_1.isObjectShallowModified(props.data, nextProps.data)) {
                    if (nextProps.store && nextProps.store.data === nextProps.data) {
                        var newData = helper_1.createObject(nextProps.store.data, helper_1.syncDataFromSuper(store.data, nextProps.store.data, props.scope, nextProps.dataUpdatedAt !== props.dataUpdatedAt));
                        // todo fix: dialog 种数据从孩子 form 同步过来后，会走这个逻辑让 form 更新 data，会导致里面的 __prev 丢失。
                        store.initData(newData);
                    }
                    else if (nextProps.data.__super) {
                        store.initData(helper_1.extendObject(nextProps.data));
                    }
                    else {
                        store.initData(helper_1.createObject(nextProps.scope, nextProps.data));
                    }
                }
                else if ((!nextProps.store || nextProps.data !== nextProps.store.data) && nextProps.data && nextProps.data.__super) {
                    (!props.data || nextProps.data.__super !== props.data.__super)
                        && store.initData(helper_1.createObject(nextProps.data.__super, store.data));
                }
                else if (nextProps.scope !== props.scope) {
                    store.initData(helper_1.createObject(nextProps.scope, store.data));
                }
            };
            StoreFactory.prototype.componentWillUnmount = function () {
                var rootStore = this.context;
                var store = this.store;
                rootStore.removeStore(store);
                delete this.store;
            };
            StoreFactory.prototype.renderChild = function (region, node, subProps) {
                if (subProps === void 0) { subProps = {}; }
                var render = this.props.render;
                return render(region, node, tslib_1.__assign({ data: this.store.data, dataUpdatedAt: this.store.updatedAt }, subProps, { scope: this.store.data, store: this.store }));
            };
            StoreFactory.prototype.render = function () {
                var _a = this.props, detectField = _a.detectField, rest = tslib_1.__rest(_a, ["detectField"]);
                var exprProps = {};
                if (!detectField || detectField === "data") {
                    exprProps = filter_schema_1.default(rest, this.store.data);
                    if (exprProps.hidden || exprProps.visible === false) {
                        return null;
                    }
                }
                return (react_1.default.createElement(Component, tslib_1.__assign({}, rest /* todo */, exprProps, { ref: this.refFn, data: this.store.data, dataUpdatedAt: this.store.updatedAt, store: this.store, scope: this.store.data, render: this.renderChild })));
            };
            StoreFactory.displayName = "WithStore(" + (Component.displayName || Component.name) + ")";
            StoreFactory.ComposedComponent = Component;
            StoreFactory.contextType = RootStoreContext;
            StoreFactory = tslib_1.__decorate([
                mobx_react_1.observer
            ], StoreFactory);
            return StoreFactory;
        }(react_1.default.Component));
        hoistNonReactStatic(StoreFactory, Component);
        return StoreFactory;
    };
}
exports.HocStoreFactory = HocStoreFactory;
function loadRenderer(schema, path) {
    return (react_1.default.createElement(Alert2_1.default, { level: "danger" },
        react_1.default.createElement("p", null, "Error: \u627E\u4E0D\u5230\u5BF9\u5E94\u7684\u6E32\u67D3\u5668"),
        react_1.default.createElement("p", null,
            "Path: ",
            path),
        react_1.default.createElement("pre", null,
            react_1.default.createElement("code", null, JSON.stringify(schema, null, 2)))));
}
var defaultOptions = {
    session: 'global',
    affixOffsetTop: 50,
    affixOffsetBottom: 0,
    richTextToken: '',
    loadRenderer: loadRenderer,
    fetcher: function () {
        return Promise.reject('fetcher is required');
    },
    isCancel: function () {
        console.error('Please implements this.');
        return false;
    },
    alert: function (msg) {
        alert(msg);
    },
    updateLocation: function () {
        console.error('Please implements this.');
    },
    confirm: function (msg) {
        return confirm(msg);
    },
    notify: function (msg) {
        alert(msg);
    },
    jumpTo: function () {
        console.error('Please implements this.');
    },
    isCurrentUrl: function () {
        return false;
    },
    copy: function (contents) {
        console.error('copy contents', contents);
    },
    rendererResolver: resolveRenderer
};
var stores = {};
function render(schema, props, options, pathPrefix) {
    if (props === void 0) { props = {}; }
    if (options === void 0) { options = {}; }
    if (pathPrefix === void 0) { pathPrefix = ''; }
    options = tslib_1.__assign({}, defaultOptions, options);
    var store = stores[options.session || 'global'] || (stores[options.session || 'global'] = index_1.RendererStore.create({}, tslib_1.__assign({}, options, { fetcher: options.fetcher ? api_1.wrapFetcher(options.fetcher) : defaultOptions.fetcher, confirm: options.confirm ? helper_1.promisify(options.confirm) : defaultOptions.confirm })));
    window.amisStore = store; // 为了方便 debug.
    var env = mobx_state_tree_1.getEnv(store);
    var theme = props.theme || options.theme || 'default';
    env.theme = theme_1.getTheme(theme);
    return (react_1.default.createElement(exports.ScopedRootRenderer, tslib_1.__assign({}, props, { schema: schema, pathPrefix: pathPrefix, rootStore: store, env: env, theme: theme })));
}
exports.render = render;
function clearStoresCache(sessions) {
    if (Array.isArray(sessions) && sessions.length) {
        sessions.forEach(function (key) { return delete stores[key]; });
    }
    else {
        stores = {};
    }
}
exports.clearStoresCache = clearStoresCache;
var cache = {};
function resolveRenderer(path, schema, props) {
    if (cache[path]) {
        return cache[path];
    }
    else if (path && path.length > 1024) {
        throw new Error('Path太长是不是死循环了？');
    }
    var renderer = null;
    renderers.some(function (item) {
        var matched = false;
        if (typeof item.test === "function") {
            matched = item.test(path, schema);
        }
        else if (item.test instanceof RegExp) {
            matched = item.test.test(path);
        }
        if (matched) {
            renderer = item;
        }
        return matched;
    });
    // 只能缓存纯正则表达式的后者方法中没有用到第二个参数的，因为自定义 test 函数的有可能依赖 schema 的结果
    if (renderer !== null &&
        (renderer.test instanceof RegExp
            || typeof renderer.test === 'function' && renderer.test.length < 2)) {
        cache[path] = renderer;
    }
    return renderer;
}
exports.resolveRenderer = resolveRenderer;
function getRenderers() {
    return renderers.concat();
}
exports.getRenderers = getRenderers;
function getRendererByName(name) {
    return find(renderers, function (item) { return item.name === name; });
}
exports.getRendererByName = getRendererByName;
//# sourceMappingURL=./factory.js.map
