include('observable.js');

/**
 *
 * @example
 * myModel = uki.newClass(uki.data.Model, function() {
 *     uki.data.model.addField(this, ['name', 'age', 'sex']);
 * })
 *
 * var m = new myModel({ age: 22, name: 'Jonh Smith', sex: 'm' })
 * m.bind('change.sex', function() {
 *     alert('omg! wtf!');
 * });
 *
 * m.bind('change', function(e) {
 *     console.log(e.fields);
 * });
 *
 * m.name('Joe Black') // triggers 'change' and 'change.name'
 * m.sex('w') // triggers'change' and 'change.sex'
 */
uki.data.Model = uki.newClass(uki.data.Observable, function(Observable) {
    
    this._fields = [];
    
    this.init = function(values) {
        this.update(values || {});
    };
    
    this.fields = function() {
        return this._fields;
    };
    
    this.update = function(values) {
        var fields = [], changes = {};
        
        uki.each(values, function(name, value) {
            var field = uki.isFunction(this[name]) ? '_' + name : name;
            if (this[field] != value) {
                changes[name] = true;
                fields.push(name);
                this[field] = value;
                this.trigger('change.' + name, { model: this });
            }
        }, this);
        
        if (fields.length) this.trigger('change', {changes: changes, fields: fields, model: this});
        return this;
    };
    
});

uki.data.model = {
    _newProp: function(name) {
        return uki.newProp('_' + name, function(v) {
            var changes = {};
            changes[name] = v;
            this.update(changes);
        });
    },
    
    addFields: function(target, names) {
        for (var i=0; i < names.length; i++) {
            target[names[i]] = uki.data.model._newProp(names[i]);
        }
        // do not break prototypes
        target._fields = names.concat(target._fields || []);
    },
    
    newLoader: function(name, options) {
        return function(callback) {
            callback = callback || uki.F;
            if (this['loaded.' + name]) {
                callback.call(this, this[name]());
            } else {
                this.bind('load.' + name, callback);
                if (!this['loading.' + name]) {
                    this['loading.' + name] = true;
                    uki.ajax(uki.extend({
                        url: uki.isFunction(options.url) ? options.url.call(this) : options.url,
                        data: uki.isFunction(options.data) ? options.data.call(this) : options.data ? options.data : { id: this.id() },
                        success: uki.proxy(function(val) {
                            this['loading.' + name] = false;
                            this['loaded.' + name] = true;
                            options.setter ? options.setter.call(this, val) : this['_' + name] = val;
                            this.trigger('load.' + name, val);
                            this.unbind('load.' + name);
                        }, this)
                    }, options.ajaxOptions || {}));
                }
            }
        };
    }
};