/// <reference path="leaf.ts" />
namespace Leaf {
    export class Model<TAttribute, TField extends string> {
        public events = new EventEmitter<string>()
        public readonly fields: TField[]
        protected _data: TAttribute = {} as TAttribute
        public readonly data: TAttribute
        [index: string]: any
        constructor(public readonly _name: string, protected _fields: TField[] = []) {
            for (let field of this._fields) {
                this.define(field)
            }
            Object.defineProperty(this, "fields", {
                get: () => {
                    return this._fields.slice(0)
                }
            })
            Object.defineProperty(this, "data", {
                get: () => {
                    return this as any as TAttribute
                },
                set: (values: TAttribute) => {
                    this.sets(values)

                }
            })
        }
        private define(name: TField, defaultValue: any = null) {
            Object.defineProperty(this, name, {
                get: () => {
                    return this.get(name)
                },
                set: (value) => {
                    return this.set(name, value)
                }
            })
        }
        public hasKey(key: string) {
            return this._fields.indexOf(key as TField) >= 0
        }
        public sets(obj: any): boolean {
            if (!obj) {
                return
            }
            if (obj instanceof Model) {
                obj = obj.toJSON()
            }
            let changed = false
            let diff = {}
            for (let key in obj) {
                if (!this.hasKey(key)) {
                    continue
                }
                let old = this._data[key]
                let value = obj[key]
                if (this.set(key as TField, value, true)) {
                    changed = true
                    this.events.emit(`change/${key}`, value, old)
                    diff[key as string] = old
                }
            }
            if (changed) {
                this.events.emit("change", diff)
            }
        }
        public set(key: TField, value: any, silent?: boolean): boolean {
            let old = this._data[key as string]
            if (!this.hasKey(key)) return false;

            if (old === value) {
                return false
            }
            this._data[key as string] = value
            if (silent) return true

            this.events.emit(`change/${key}`, value, old)
            let diff: { [index: string]: any } = {}
            diff[key as string] = old
            this.events.emit("change", diff)
            return true
        }
        public get(key: TField) {
            return this._data[key as string] || null
        }
        public toJSON() {
            return JSON.parse(JSON.stringify(this._data))
        }
    }
}
