formit = {};

// given selected DOM object(s) for a field, assign them to a particular field
// e.g. you might $('form input[name=username]').formitField(formit.form.fields.username);
jQuery.prototype.formitField = function(formitField) {
    this.parents('.row').each(function() { this.formitField = formitField });
    this.change(function() { formitField.validate(this); });
    if (formitField.setup) { formitField.setup(this); }
}

function FormitForm(name, validMsg, reqMsg) {
    this.name = name;
    this.validMsg = validMsg;
	this.reqMsg = reqMsg;
}

FormitForm.prototype.setup = function(options) {
    for (var name in this.fields) {
        this.findElement(name).formitField(this.fields[name]);
    }

    var form = this;
    form.options = options ? options : {};
    if (options && options.submit) {
        form.find().find('input[type=submit], a.submit').click(function() {
            form.validate(options.submit);
            return false;
        });
    } else if (options && options.ajaxSubmit) {
		form.find().find('input[type=submit], a.submit').click(function() {
			form.validate(function() {
				$.post(form.getAction(), form.val(), options.ajaxSubmit, options.dataType ? options.dataType : 'json');
			});

			return false;
		});
	} else {
	    form.find().find('input[type=submit], a.submit').click(function() {
	        form.validate(function() { form.find().submit(); });
	        return false;
	    });
	}
};

FormitForm.prototype.find = function() {
    return $('#form_' + this.name);
};

FormitForm.prototype.findRow = function(name) {
    return $('div.f_' + name);
}

FormitForm.prototype.message = function(name, message, isSubmit) {
    this.fields[name].message(findRow(name), message, isSubmit);
}

FormitForm.prototype.findElement = function(name) {
    return this.fields[name].find(this.findRow(name));
}

FormitForm.prototype.validate = function(onSuccess) {
    this.ajaxValidate(this.val(), onSuccess, true);
};

FormitForm.prototype.getAction = function() {
	var action = this.find().attr('action');
	return action ? action : document.location;
}

FormitForm.prototype.getMethod = function() {
	var method = this.find().attr('method');
	return method ? method : 'post';
}

FormitForm.prototype.ajaxValidate = function(data, onSuccess, isSubmit) {
    var form = this;
    var postData = { _formit_validate: this.name };
    for (k in data) {
		if (data[k] instanceof Array) {
			postData[k + "[]"] = data[k];
		} else if (data[k] == null) {
			postData[k] = '';
		} else {
        	postData[k] = data[k];
		}
    }
    $.post(this.options.validate_uri ? this.options.validate_uri : this.getAction(), postData, function(json) {
        if (json.valid == 1 && onSuccess) {
            onSuccess(data);
        } else {
            // not valid -- assign error messages etc
            for (name in json.fields) {
                var row = form.findRow(name);
                if (json.fields[name].visible === true) {
                    row.show();
                } else if (json.fields[name].visible === false) {
                    row.hide();
                }
                form.fields[name].message(row, json.fields[name], isSubmit);
                if (json.fields[name].val) {
                    form.fields[name].val(row, json.fields[name].val);
                }
            }
			if (isSubmit) {
				form.find().find('.errors').show().html(json.errors);
				scroll(0, 0);
			}
        }
    }, 'json');
}

FormitForm.prototype.val = function(val) {
    if (val instanceof Object) {
        for (name in val) {
            if (this.fields[name]) {
                this.fields[name].val(this.findRow(name), val[name] == null ? '' : val[name]);
            }
        }
    } else if (val) {
        return this.fields[val].val(this.findRow(val));
    } else {
        data = {};
        for (name in this.fields) {
            data[name] = this.fields[name].val(this.findRow(name));
        }
        return data;
    }
}

function FormitField(form, name, find, val, setup, dep, req, ajax) {
    this.form = form;
    this.name = name;
    this.find = find;
    this.val = val;
    this.setup = setup;
    this.req = req;
    this.dep = dep;
    this.ajax = ajax;
}

FormitField.prototype.validate = function(row) {
    while (row && !$(row).is('.row')) {
        row = row.parentNode;
    }
    if (!row) {
        return false;
    }

    var value = this.val($(row));
    if (!value) {
        if (this.req) {
			var reqMsg = this.form.reqMsg;
			reqMsg.error = 1;
			reqMsg.required = 1;
            this.message(row, reqMsg);
        } else {
            this.message(row, { });
        }
        return false;
    }

    if (this.ajax) {
        var data = {};
        data[this.name] = value;
        if (this.dep) {
            for (i=0; i<this.dep.length; i++) {
                var dep_name = this.dep[i];
                var val = this.form.val(dep_name);
                data[dep_name] = val;
            }
        }
        this.form.ajaxValidate(data);
    } else {
        this.message(row, { valid: 1 });
    }

    return true;
};

FormitField.prototype.message = function(row, msg, isSubmit) {
    if (msg.error) {
        // special-case: don't show a required message if:
        //   (1) it's not a full submit
        //   (2) there is no message there previously
        // this prevents users from getting annoying required messages on fields they
        // haven't gotten a chance to input yet (unless it's a submit)
        if (msg.required && !isSubmit && !$(row).is('.valid') && !$(row).is('.error')) {
            return;
        } else {
            $(row).addClass('error').removeClass('valid');
            $(row).find('.msg, .emsg').html(msg.small).attr('title', msg.large);
            $(row).find('.vmsg').html('&nbsp;');
        }
    } else if (msg.valid) {
        $(row).addClass('valid').removeClass('error');
        $(row).find('.msg, .vmsg').html(this.form.validMsg);
        $(row).find('.emsg').html('&nbsp;');
    } else {
        $(row).removeClass('error').removeClass('valid');
        $(row).find('.msg, .vmsg, .emsg').html('&nbsp;').attr('title', '');
    }
};

FormitField.prototype.lib = {
	textarea: {
		maxlength: function(field, max) {
			var f = $(field);
			var length = f.val().length;
			if (length > max) {
				f.val(f.val().substr(0, max));
				length = max;
			}
			f.prev().find('.remain').html(max - length);
		}
	},
	
    radiogroup: {
        val: function(field, row, setval) {
            var result = '';
            if (setval != undefined) {
                field.find(row).each(function() {
                    if ($(this).attr('value') == setval) {
                        $(this).attr('checked', 'checked');
                    } else {
                        $(this).attr('checked', '');
                    }
                });
            } else {
                field.find(row).each(function() {
                    if ($(this).attr('checked')) {
                        result = $(this).attr('value');
                    }
                });
                return result;
            }
        }
    },

	checkboxgroup: {
		checkall: function(element) {
			$(element).parents('.row').eq(0).find('ul input[type="checkbox"]').attr('checked', $(element).attr('checked') ? 'checked' : '');
			$(element).change(); // re-run the change() routine for safari
		},
		
		val: function(field, row, setval) {
			var result = [];
			if (setval != undefined) {
				field.find(row).each(function() {
					if (setval.indexOf($(this).attr('value')) >= 0) {
						$(this).attr('checked', 'checked');
					} else {
						$(this).attr('checked', '');
					}
				});
			} else {
				field.find(row).each(function() {
					if ($(this).attr('name') && $(this).attr('checked')) {
						result.push($(this).attr('value'));
					}
				});
				return result.length ? result : null;
			}
		}
	},

    dateselect: {
        val: function(field, row, setval) {
            // TODO: setval
            var month = new Date().getMonth() + 1;
			if (month < 10) {
				month = '0' + month;
			}
            var day = '01';
            var year = new Date().getFullYear();
            var incomplete = false;
            field.find(row).each(function() {
                var v = $(this).val();
                if (v == '') {
                    incomplete = true;
                } else {
                    var type = $(this).attr('name');
                    type = type.substring(type.length - 1);
                    if (type == 'y' || type == 'Y') {
                        year = v;
                    } else if (type == 'd' || type == 'j') {
                        day = v;
                    } else {
                        month = v;
                    }
                }
            });
            if (incomplete) {
                return '';
            }
            if (year) {
                return year + '-' + month + '-' + day;
            } else {
                return month + '-' + day;
            }
        }
    }
}