(function($) {

    function Jsonform () {
        this.defaults = {
            response : null,
            controller : null,
            action : null,
            redirect : false,
            clearFormOnSuccess : true,
            extraData : null,//object, array or function,
            method : 'post'
        };
    }

   $.extend(Jsonform.prototype, {

        error : function (e) {
            alert(e);
        },

        idClassPrefix : 'jsonformid_',

        defaultvaluekey : 'jsonformdefaultvalue',

        currentId : null,

        settings : {},

        setting : function (param) {
            var data = this.currentId? this.settings[this.currentId] : this.defaults;
            return param? data[param] : data;
        },


        attach : function(target, options) {
            var uid = $.unique_id();
            var s = $.extend({}, this.defaults, options);
            if ($(target).get(0).tagName.toLowerCase() == 'form') {
                $.jsonform.saveDefaults(target);
                $(target).bind('submit', function(e) {
                     e.preventDefault();
                     $.jsonform.submit(this);
                });
                $(target).addClass(this.idClassPrefix + uid);
                this.settings[uid] = s;
            }
        },

        saveDefaults : function(target) {
            var key = this.defaultvaluekey;
            $(target).find('input, textarea, select').each(function() {
                $(this).data(key, $(this).val());
            });
        },

        setDefaults : function(target) {
            var key = this.defaultvaluekey;
            $(target).find('input, textarea, select').each(function() {
                $(this).val($(this).data(key));
            });
        },

        collectData : function(target) {
            var data = {};
            $(target).find('input[type=hidden], input[type=text], input[type=password], textarea, select').each(function() {
                name = $(this).attr('name');
                if (name) {
                    val = $(this).val();
                    data[name] = val? val : '';
                }
            });
            $(target).find('input[type=radio]:checked').each(function() {
                name = $(this).attr('name');
                if (name) {
                    val = $(this).val();
                    data[name] = val? val : '';
                }
            });
            $(target).find('input[type=checkbox]:checked').each(function() {
                name = $(this).attr('name');
                if (name) {
                    val = $(this).val();
                    data[name] = val? val : '';
                }
            });

            return data;
        },

        submit : function (target) {
            try {
                var newId = this.getId(target);
                if (this.currentId && this.currentId != newId)
                    throw new Error('double_submit');


                this.currentId = newId;
                var data = this.collectData(target);
                if (this.setting('extraData'))
                    data = $.extend({}, data, $.isFunction(this.setting('extraData'))? this.setting('extraData').call() : this.setting('extraData'));
                $.json(this.setting('controller'), this.setting('action'), data, function(j, status) {
                    $.jsonform.response(target, j, status);
                }, this.setting('method'));
                this.unsetErrors(target);
            } catch(e) {
                this.error(e);
            }
        },
        response : function (target, j, status) {
            try {
                if (!this.currentId)
                    throw new Error('no_submit_id');
                var target = $('.' + this.idClassPrefix + this.currentId);
                if (j.fielderrors || j.subseterrors) {
                    this.setErrors(target, j);
                } else if (status == 'success') {
                    if (this.setting('redirect') && j.redirect)
                        return document.location = j.redirect;
                    else if (this.setting('clearFormOnSuccess'))
                        this.setDefaults(target);
                }

                var response = this.setting('response');
                if ($.isFunction(response))
                    response(j, status);

                this.currentId = null;
            } catch(e) {
                this.error(e);
            }
        },

        getId: function(target) {
            var classes = $(target).attr('class').split(' ');
            var pl = this.idClassPrefix.length;
            for (var i = 0, l = classes.length; i < l; i++ ) {
                if (classes[i].length > pl && classes[i].substr(0, pl) == this.idClassPrefix) {
                    return classes[i].substr(pl);
                }
            }
        },

        setErrors : function(target, j) {
            if (j.fielderrors) {
                for (field in j.fielderrors) {
                    target.find('[name=' + field + ']').parent().parent().addClass('error')
                }
            }
            if (j.subseterrors) {
                var row, l;
                for (subset in j.subseterrors) {
//                  for (row = 0, l = j.subseterrors[subset].length; row < l; row++ ) {
                    for (row in j.subseterrors[subset]) {
                        for (field in j.subseterrors[subset][row].data) {
                            target.find(':name(' + [subset, row, field].join(',') + ')').parent().parent().addClass('error')
                        }
                    }
                }
            }
        },

        unsetErrors : function(target) {
           $(target).find('.error').removeClass('error');
        }


    });

    $.fn.jsonform = function(options){
        return this.each(function() {
            $.jsonform.attach(this, options);
        });
    };

   $.jsonform = new Jsonform();

})(jQuery);

