
/**
 * 监听属性值变化对象
 *
 * @class IBizListenProperty
 */
class IBizListenProperty {

    /**
     * 监听对象
     *
     * @static
     * @type {IBizListenProperty}
     * @memberof IBizListenProperty
     */
    public static listenObj: IBizListenProperty = null;

    /**
     * 数组需监听的方法集合
     *
     * @type {Array<String>}
     * @memberof IBizListenProperty
     */
    public methods: Array<String> = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

    /**
     * 数组对象的原型
     *
     * @type {*}
     * @memberof IBizListenProperty
     */
    public arrayProto: any = Array.prototype;

    /**
     * 根据数组原型创建的对象
     *
     * @type {*}
     * @memberof IBizListenProperty
     */
    public arrayMethods: any = null;

    /**
     *Creates an instance of IBizListenProperty.
     * @memberof IBizListenProperty
     */
    constructor() {
        let _this = this;
        _this.arrayMethods = Object.create(_this.arrayProto);
        _this.methods.forEach((method: string) => {
            Object.defineProperty(_this.arrayMethods, method, {
                value: function mutator() {
                    let original = _this.arrayProto[method];
                    let args = [];
                    let len: number = arguments.length;
                    while(len--) {
                        args[ len ] = arguments[ len ];
                    }
                    let data: Array<any> = [];
                    this.forEach((item) => {
                        data.push(item);
                    })
                    let result = original.apply(data, args);
                    let bo = this.__bo__;
                    delete this.__bo__;
                    bo.obj[bo.key] = data;
                    _this.listenArray(bo.obj, bo.key).subscribe((data) => {
                        bo.subscribe(data);
                    })
                }
            })
        });
    }

    /**
     * 创建对象
     *
     * @static
     * @memberof IBizListenProperty
     */
    public static create(): void {
        this.listenObj = new IBizListenProperty();
    }

    /**
     * 注册需监听的对象
     *
     * @static
     * @param {*} obj
     * @returns
     * @memberof IBizListenProperty
     */
    public static regListenObject(obj: any) {
        const subject: Subject<any> = new rxjs.Subject();
        if(obj instanceof Object) {
            let keys = Object.keys(obj);
            keys.forEach((key) => {
                let val = obj[key];
                let args: any = {name: key};
                if(Array.isArray(val)) {
                    this.listenObj.listenArray(obj, key).subscribe((data) => {
                        Object.assign(args, data);
                        subject.next(args);
                    });
                } else if (val instanceof Object) {
                    this.listenObj.listenObject(val).subscribe((data) => {
                        Object.assign(args, data);
                        subject.next(args);
                    });
                }
                this.listenObj.defineProperty(obj, key).subscribe((data) => {
                    Object.assign(args, data);
                    subject.next(args);
                });
            });
        }
        return subject;
    }

    /**
     * 监听数组
     *
     * @param {*} obj
     * @param {string} key
     * @memberof IBizListenProperty
     */
    public listenArray(obj: any, key: string): Subject<any> {
        let _this = this;
        const subject: Subject<any> = new rxjs.Subject();
        if(obj instanceof Object && key) {
            let arr  = obj[key];
            arr.__bo__ = {obj: obj, key: key, subscribe: subject.next};
            var augment = arr.__proto__ ? this.protoAugment : this.copyAugment;
            augment(arr, this.arrayMethods, key);
            _this.listenObject(arr).subscribe((data) => {
                subject.next(data);
            });
        }
        return subject;
    }

    /**
     * 监听对象
     *
     * @param {*} obj
     * @returns {Subject<any>}
     * @memberof IBizListenProperty
     */
    public listenObject(obj: any): Subject<any> {
        let _this = this;
        const subject: Subject<any> = new rxjs.Subject();
        if(obj instanceof Object) {
            let keys: Array<String> = Object.keys(obj);
            keys.forEach((key: string) => {
                let val = obj[key];
                if(val instanceof Object) {
                    if(Array.isArray(val)) {
                        _this.listenArray(obj, key);
                    }
                }
                _this.defineProperty(obj, key).subscribe((args) => {
                    subject.next(args)
                });
            })
        }
        return subject;
    }

    /**
     * 监听属性值
     *
     * @param {*} obj
     * @param {string} key
     * @returns
     * @memberof IBizListenProperty
     */
    public defineProperty(obj: any, key: string): Subject<any> {
        const subject: Subject<any> = new rxjs.Subject();
        if(obj instanceof Object && key) {
            let value = obj[key];
            let args = {};
            Object.defineProperty(obj, key, {
                enumerable: true,
                configurable: true,
                get: function(){
                    return value;
                },
                set: function(newVal){
                    if(newVal === value) 
                        return ;
                    var val = value;
                    value = newVal;
                    Object.assign(args, {newVal: value, oldVal: val, property: key});
                    subject.next(args);
	            }
            });
        }
        return subject;
    }

    /**
     * 辅助方法
     *
     * @param {*} obj
     * @param {*} key
     * @param {*} val
     * @memberof IBizListenProperty
     */
    public define(obj, key, val): void {
        Object.defineProperty(obj, key, {
            value: val,
			enumerable: true,
			writable: true,
			configurable: true
        })
    }

    /**
     * 重新赋值Array的__proto__属性
     *
     * @param {*} target
     * @param {*} src
     * @param {*} opt
     * @memberof IBizListenProperty
     */
    public protoAugment(target, src, opt: any) {
        target.__proto__ = src
    }

    /**
     * 不支持__proto__的直接修改相关属性方法
     *
     * @param {*} target
     * @param {*} src
     * @param {*} keys
     * @memberof IBizListenProperty
     */
    public copyAugment(target, src, keys) {
        let _this = this;
        keys.forEach((key) => {
            _this.define(target, src, key);
        });
    }
}

/**
 * 创建对象
 */
IBizListenProperty.create();