// vim: et:ts=4:sw=4:sts=4:ft=js

/*@
Ox.Form <f:Ox.Element> Form Object
    () ->              <f> Form Object
    (options) ->       <f> Form Object
    (options, self) -> <f> Form Object
    options <o> Options object
        error  <s> error
        id     <s> id
        items  <a|[]> []
        submit <f|null> on submit function
    self    <o> shared private variable
@*/

Ox.Form = function(options, self) {

    var self = self || {},
        that = new Ox.Element({}, self)
            .defaults({
                error: '',
                id: '',
                items: [],
                submit: null
            })
            .options(options || {}) // fixme: the || {} can be done once, in the options function
            .addClass('OxForm');

    $.extend(self, {
        $items: [],
        $messages: [],
        formIsValid: false,
        itemIds: [],
        itemIsValid: []
    });

    // fixme: form isn't necessarily empty/invalid
    self.options.items.forEach(function(item, i) {
        self.itemIds[i] = item.options('id') || item.id;
        self.itemIsValid[i] = !!item.value().length;
        that.append(self.$items[i] = new Ox.FormItem({element: item}));
        item.bindEvent({
            /*
        	blur: function(event, data) {
            	validate(i, data.valid);
            	if (data.valid) {
                	self.$messages[i].html('').hide();
            	} else {
                	self.$messages[i].html(data.message).show();
            	}
        	},
        	*/
        	autovalidate: function(event, data) {
        	    data.valid = !!data.value.length;
        	    validate(i, data.valid);
            	data.valid && self.$items[i].setMessage('');
        	},
			submit: function(event, data) {
            	self.formIsValid && that.submit();
        	},
        	validate: function(event, data) {
            	validate(i, data.valid);
                self.$items[i].setMessage(data.valid ? '' : data.message);
        	}
		});
    });

    function getItemIndexById(id) {
        return self.itemIds.indexOf(id);
    }

    function submitCallback(data) {
        data.forEach(function(v, i) {
            self.$items[i].setMessage(v.message);
        });
    }

    function validate(pos, valid) {
        //Ox.print('FORM validate', pos, valid)
        self.itemIsValid[pos] = valid;
        if (Ox.every(self.itemIsValid) != self.formIsValid) {
            self.formIsValid = !self.formIsValid;
            that.triggerEvent('validate', {
                valid: self.formIsValid
            });
        }
    }

    that.addItem = function(pos, item) {
        Ox.print('addItem', pos)
        self.options.items.splice(pos, 0, item);
        self.$items.splice(pos, 0, new Ox.FormItem({element: item}));
        pos == 0 ?
            self.$items[pos].insertBefore(self.$items[0]) :
            self.$items[pos].insertAfter(self.$items[pos - 1]);
    }

    that.removeItem = function(pos) {
        Ox.print('removeItem', pos);
        self.$items[pos].removeElement();
        self.options.items.splice(pos, 1);
        self.$items.splice(pos, 1);
    }

    that.submit = function() {
        //Ox.print('---- that.values()', that.values())
        self.options.submit(that.values(), submitCallback);
    };

    that.values = function() { // fixme: can this be private?
        /*
            get/set form values
            call without arguments to get current form values
            pass values as array to set values (not implemented)
        */
        var values = {};
        if (arguments.length == 0) {
            self.$items.forEach(function($item, i) {
                values[self.itemIds[i]] = self.$items[i].value();
            });
            //Ox.print('VALUES', values)
            return values;
        } else {
            Ox.forEach(arguments[0], function(value, key) {
                var index = getItemIndexById(key);
                //index > -1 && Ox.print(':::::::', key, value)
                index > -1 && self.options.items[index].options({value: value});
            });
            return that;
        }
    };

    return that;

};
