layui.define([], function (exports) {
    const MOD_NAME = "react";

    // 监听数据，后面可以用一个WeakMap + 存放key的数组替换
    let proxyData = {
        data: [], // 数据
        methods: [], // 方法
        hooks: [], // 钩子
    };

    let react = {
        /**
         * @abstract 通知并执行钩子函数
         *
         * @param {String} hook 监听的变量名
         * @param {'pre'|'post'} type 类型
         * @param  {...any} args
         */
        notifyHooks: (hook, type = 'pre', ...args) => {
            for (const f of proxyData.hooks.filter(v => {
                return v.target === hook && v.type === type;
            })) {
                f.handler(args);
            }
        },
        /**
         * @abstract 注册一些钩子，当托管变量发生改变时触发
         *
         * 注册钩子：
         * @example
         * ```javascript
         *  // 注册托管变量
         *  laytp.react.regData({
             *      hello: 0,
             *      world: 2
             *  });
         *
         *  // 注册钩子
         *  laytp.react.regHooks({
             *      // 监听名为hello的变量改变
             *      hello: () => {
             *          console.log('Here!');
             *      },
             *
             *      world: {
             *          type: 'post',  // 默认都为'pre'，为变量修改前回调，'post'为变量修改后回调
             *          handler: (target, prop, data) => {
             *              data.value = 50;
             *          }
             *      }
             *  });
         * ```
         *
         * 调用：
         * @example
         * ```javascript
         * laytp.data.hello = 10;
         * // laytp.data.hello -> 10
         *
         * laytp.data.world = 20;
         * // laytp.data.world -> 50
         * ```
         *
         * @param {Object} obj 需注册的钩子的Object
         * @returns
         */
        regHooks: function (obj) {
            if (typeof obj !== 'function' && typeof obj !== 'object') {
                console.error('请传入钩子对象或一个具名函数');
                return;
            }

            if (typeof obj === 'function') {
                if (obj.name === '') {
                    console.error('不支持传入闭包');
                    return;
                }

                proxyData.hooks.push({
                    target: obj.name,
                    type: 'pre',
                    handler: obj
                });

                return;
            }

            for (const key in obj) {
                if (Object.hasOwnProperty.call(obj, key)) {
                    const func = obj[key];

                    if (typeof func === 'object' && typeof func.type === 'string' && typeof func.handler === 'function')
                        proxyData.hooks.push({
                            target: key,
                            type: func.type,
                            handler: func.handler
                        });
                    else if (typeof func === 'function')
                        proxyData.hooks.push({
                            target: key,
                            type: 'pre',
                            handler: func
                        });
                    else
                        console.warn(`${key} 不是一个合法的钩子`);
                }
            }
        },
        /**
         * @abstract 注册一些托管方法
         *
         * 注册：
         * @example
         * ```javascript
         *  laytp.react.regMethods({
             *      alert() {
             *          alert('warning');
             *      }
             *  });
         * ```
         *
         * 调用：
         * @example
         * ```javascript
         *  laytp.methods.alert();
         * ```
         *
         * @param {Object} obj 需注册的方法的Object
         * @returns
         */
        regMethods: function (obj) {
            if (typeof obj === 'function') {
                if (obj.name === '') {
                    console.error('不支持传入闭包');
                    return;
                }

                if (proxyData.methods.findIndex(v => { return v.key === key; }) > -1) {
                    proxyData.methods.find(v => { return v.key === key }).handler = func;
                    return;
                }

                proxyData.methods.push({
                    key: obj.name,
                    handler: func
                });

                Object.defineProperty(laytp.methods, `${obj.name}`, {
                    get() {
                        let q = proxyData.methods.find(v => {
                            return v.key === obj.name;
                        });
                        return q === undefined ? undefined : q.handler;
                    }
                });
                return;
            }

            for (const key in obj) {
                if (Object.hasOwnProperty.call(obj, key)) {
                    const func = obj[key];

                    if (typeof func !== 'function') {
                        console.warn(`${key} 不是一个合法的函数`);
                        continue;
                    }

                    if (proxyData.methods.findIndex(v => { return v.key === key; }) > -1) {
                        proxyData.methods.find(v => { return v.key === key }).handler = func;
                        continue;
                    }

                    proxyData.methods.push({
                        key,
                        handler: func
                    });

                    Object.defineProperty(react.methods, `${key}`, {
                        get() {
                            let q = proxyData.methods.find(v => {
                                return v.key === key;
                            });
                            return q === undefined ? undefined : q.handler;
                        }
                    });
                }
            }
        },

        /**
         * @abstract 注册一些托管数据，当托管数据发生变化时调用同名钩子函数，暂不支持深度监听
         *
         * 注册：
         * @example
         * ```javascript
         *  laytp.react.regData({
             *      some: 1,
             *      other: 10,
             *      meow: "I'm hungry"
             *  });
         * ```
         *
         * 调用：
         * @example
         * ```javascript
         *  laytp.data.some = 10;
         *  laytp.data.other = 'Ok';
         * ```
         *
         * @param {Object} obj 需注册的数据的Object
         * @returns
         */
        regData: function (obj) {
            if (typeof obj !== 'object') {
                console.error('请传入数据对象');
                return;
            }

            const handler = {
                setByOutside(hooks, target, prop, value) {
                    for (const h of hooks) {
                        let data = {
                            get value() {
                                return value;
                            },

                            set value(v) {
                                value = v;
                            }
                        };

                        h.handler(target, prop, data);
                        target[prop] = value;
                    }
                },

                set(target, prop, value) {
                    // 写入前钩子
                    try {
                        handler.setByOutside(proxyData.hooks.filter(o => {
                            return o.target === target.key && o.type === 'pre';
                        }), target, prop, value);
                    } catch (error) {
                        console.error(error);
                        return;
                    }

                    // 写入
                    target[prop] = value;

                    // 写入后钩子
                    try {
                        handler.setByOutside(proxyData.hooks.filter(o => {
                            return o.target === target.key && o.type === 'post';
                        }), target, prop, value);
                    } catch (error) {
                        console.error(error);
                    }
                },

                get(target, prop) {
                    return target[prop];
                }
            };

            for (const key in obj) {
                if (Object.hasOwnProperty.call(obj, key)) {
                    const element = obj[key];

                    if (proxyData.data.findIndex(v => { return v.key === key }) > -1) {
                        proxyData.data.find(v => { return v.key === key }).value = element;
                        continue;
                    }

                    proxyData.data.push(new Proxy({
                        key,
                        value: element
                    }, handler));

                    Object.defineProperty(react.data, `${key}`, {
                        get() {
                            let q = proxyData.data.find(v => {
                                return v.key === key;
                            });
                            return q === undefined ? undefined : q.value;
                        },

                        set(v) {
                            let q = proxyData.data.find(v => {
                                return v.key === key;
                            });
                            (q) && (q.value = v);
                        }
                    });
                }
            }
        },
        data: new Proxy({}, {
            set(target, p, newVal) {
                react.regData({
                    [p]: newVal
                });
            },

            get(target, p) {
                return target[p];
            }
        }),
        methods: new Proxy({}, {
            set(target, p, newVal) {
                react.regMethods({
                    [p]: newVal
                });
            },

            get(target, p) {
                return target[p];
            }
        }),
        hooks: new Proxy({}, {
            set(target, p, newVal) {
                react.regHooks({
                    [p]: newVal
                });
            },

            get(target, p) {
                return target[p];
            }
        })
    };

    //输出模块
    exports(MOD_NAME, react);
});