(function($) {
	
	var validateBeforeSubmit = 0;
	var firstError = false;
	var validators = {}; 
	var validateRules = [];
	var forms = {};
	var methods = {
			
			add: function(key, rules) {
				
				// get related form
				if (typeof(forms[key]) == 'undefined') {		
					$('form :input').each(function(i, o) {
						if (o.name && o.name == 'wgFormKey' && o.value == key) {
							forms[key] = o.form;
						}						
					});
					
					// stop any validation occurring before submit
					$(forms[key]).data('validate', validateBeforeSubmit);
					
					// bind validation to submit
					$(forms[key]).submit(function(e) { 
						
						firstError = false;
						$(this).data('validate', 1);						
												
						// perform validation
						$.each(validateRules, function(i, ruleObj) {
							$.fn.formValidator('performValidation', ruleObj.input[0], ruleObj.rule);					
						});	
						
						// stop the form submitting
						if (firstError) {
							
							e.preventDefault();
							
							// scroll to first error
							$('html, body').animate(
								{ scrollTop: $(":input[name='" + firstError.name + "']").offset().top - 100 }, 
								{'duration':750,'complete':function() { $(":input[name='" + firstError.name + "']").focus() }}
							);
						}
					});
				}
				
				// now apply element rules
				$.each(rules, function(name, ruleset) {
					
					// each rule for this element
					$.each(ruleset, function(i, rule) {
						
						// make sure we have a valid validator for this
						if (rule.validator && validators[rule.validator]) {
													
							// find input (should update to work with form for better speed)
							var inp = $(":input[name='" + key + "[" + name + "]']");
							
							if (rule.events) {
								
								// split events
								$.each(rule.events.split(','), function(x, ev) {
									
									// attach for each input (may be required for date elements)
									$.each(inp, function(index, input) {
										
										// attach for each event
										$(input).bind(ev, function() { 										
											$.fn.formValidator('performValidation', this, rule);																				
										});
										
									});
									
								});
								
							}
							
							// add global rule for form submission
							validateRules.push({'input':inp,'rule':rule});
						}
																
					});					
				});				
				
			},
			
			/**
			 * Use wrapper function to ensure form does not validate always
			 */
			performValidation: function(obj, rule)
			{
				var key = obj.form.elements['wgFormKey'].value;				
				if ($(forms[key]).data('validate') == 1) {
					validators[rule.validator].validate(obj, rule);
				}
			},
			
			/**
			 * Adds a validation rule handler
			 */
			addValidator: function(name, validator)
			{
				validators[name] = validator;
			},
			
			/**
			 * Something went horribly wrong!
			 */
			addError: function(el, msg)
			{
				if (! firstError) {
					firstError = el;
				}
				
				// remove previous error (if form was submitted via php and then js run on it
				var p = $(el).parent().find('.error');
				if (p) {
					p.remove();
				}
				
				// remove previous js set error
				var e = $(el).data('errorObj');
				if (e) {
					e.clearQueue();
					e.remove();
				}	
			
				var d = $('<div>');
				d.addClass('error').text(msg);				
				$(el.parentNode).append(d);
				$(el).data('errorObj', d);
			},
			
			/**
			 * show tick
			 */
			addSuccess: function(el)
			{
				var e = $(el).data('errorObj');
				if (e) {							
					e.text('valid');
					e.removeClass('error').addClass('success');
					e.delay(2000).fadeOut('fast');
				}
			}
	};	

	// Method calling logic
	$.fn.formValidator = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(
					arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		} else {
			$.error('Method ' + method + ' does not exist on jQuery.formValidator');
		}
	};
})(jQuery);
