;(function($) {

	jQuery.validator.addMethod("inList",  function(value, element, param) {
		param = typeof param == "string" ? param.replace(/,/g, '|') : "";
		return this.optional(element) || value.match(new RegExp("^(" + param + ")$", "i")); 
	}),
	
	jQuery.validator.addMethod("zipcode", function(zip, el) {
		return this.optional(el) || /^\d{5}([- ]?\d{4})?$/.test(zip);
	}, "Please specify a valid US ZIP code");
	
	/**
	 * If there is a min date and the date isn't blank and the date is >= to the date.
	 */
	jQuery.validator.addMethod("datemin", function(date, el, min) {
		var d1 = new Date(date);
		var d2 = new Date(min);
		return this.optional(el) ||  $.trim(min).length == 0 || d1 >= d2;
	}, "Date must be greater than the minimum");
	
	/**
	 * must be in the form mm/dd/yyyy
	 */
	jQuery.validator.addMethod("rpmdate", function(date, el) {
		return this.optional(el) || date.match(/^\d{2}\/\d{2}\/\d{4}$/) ;
	}, "Date must be in the form of mm/dd/yyyy");
	
	/**
	 * If there is a max date and the date isn't blank and the date is <= to the date.
	 */
	jQuery.validator.addMethod("datemax", function(date, el, max) {
		var d1 = new Date(date);
		var d2 = new Date(max);
		return this.optional(el) || $.trim(max).length == 0 || d1 <= d2;
	}, "Date must be less than the maximum");
	
	jQuery.validator.addMethod("currency", function(value, el, decimal) {
		return this.optional(el) || /^\$?(?:\d+|\d{1,3}(?:,\d{3})+)$/.test(value);
	}, "Value must be a dollar amount");
	
	
	//this should be combined with the previous currency, but at the moment this is just easier.
	jQuery.validator.addMethod("currencydecimal", function(value, el, decimal) {
		return this.optional(el) || /^\$?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d{2})?$/.test(value);
	}, "Value must be a dollar amount");
	
	jQuery.validator.addMethod("integer", function(value, el) {
		return this.optional(el) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)$/.test(value);
	}, "Value must be an integer");
	
	/**
	 * Strip the currency symbols stripped and do a >= compare
	 */
	jQuery.validator.addMethod("integermin", function(value, el, min) {
		var parsed = parseFloat(value.replace(/^\$|,/g, ''));
		return this.optional(el) || parsed >= parseFloat(min);
	}, "Value must be greater than the minimum");
	
	/**
	 * Strip the currency symbols stripped and do a <= compare
	 */
	jQuery.validator.addMethod("integermax", function(value, el, max) {
		var parsed = parseFloat(value.replace(/^\$|,/g, ''));
		return this.optional(el) || parsed <= parseFloat(max);
	}, "Value must be less than the maximum");
	
	/**
	 * From the old validation code
	 */
	jQuery.validator.addMethod("time", function(value, el) {
		var dated = new Date('01/01/1900 '+value);
		return this.optional(el) || !isNaN(dated);
	}, "The value must be a proper time.");
	
	/**
	 * Splits the incoming value by a known set of common email address delimiters, and then calls
	 * the internal email method on each value to check each one is in fact an email address
	 * 
	 * As with the current email validator method, email addresses in the form of "Name<email@address.com>" will not validate
	 */
	jQuery.validator.addMethod("emaillist", function(value, el) {
		var valid = true;
		$this = this;
		$.each(value.split(/\s?[,;:]\s?/), function() {
			return valid = jQuery.validator.methods.email.call($this, this, el);
		});
		return this.optional(el) || valid;
	}, "All emails must be separated with a comma, semicolon or colon, with optional spacing before or after the delimiter");

	

	/* if one of the fields is filled in all must be filled in */
	// A custom constructor so that we can do this more than once to a single form.
	function addAllOrNone(classname, mesg) {
		jQuery.validator.addMethod(classname, function(val, el) {
			var group = $("." + classname);
			return $(el).get(0) != group.eq(0).get(0) || group.filter(":blank").length == group.length
				|| group.filter(":filled").length == group.length;
		});
	}
	
	function addRequireOneOf(classname, mesg) {
		jQuery.validator.addMethod(classname, function(val, el) {
			var group = $("." + classname);
			return $(el).get(0) != group.eq(0).get(0) || group.filter(":filled").length > 0;
		});
	}
	
	/* 
	 * attaches the validator to a form.  The selector must select a single form and that form id must have rules in the Params.Global.formDefinitions
	 * matching the form name.
	 */
	
	function processGroup(form, group, groupname) {
		$.each(group.rules, function(i, rule) {
			switch(rule) {
			case "allornone":
				addAllOrNone(groupname, group.message);
				break;
			case "requireoneof":
				addRequireOneOf(groupname, group.message);
				break;
			default:
				break;
				//console.log(rule + " not recognized");
			}
			$.each(group.ids, function(i,id ) {
				var target = $("#" + id);
				if (target.length > 0) {
					var ruleobj = {};
					ruleobj[groupname] = true;
					ruleobj["messages"] = {};
					ruleobj["messages"][groupname] = group.message; 
					target.addClass(groupname).rules("add", ruleobj);
				}
			});
		});
	}
	
	$.fn.extend({
		attachValidator: function(formSubmit) {
			var $this = $(this);
			
			if (!Params.Global.formDefinitions[$this.attr('id')]) {
				return false;
			}
			
			$this.data("validatorattached", true);
			var validator = $this.validate({
				meta: "validate" ,
				errorClass: "error",
				onclick: false,
				onfocusout: false,
				onkeyup: false,
				debug: true,
				submitHandler: formSubmit,
				ignore: Params.Global.formDefinitions[$this.attr('id')].ignore ? Params.Global.formDefinitions[$this.attr('id')].ignore.value : [],
				rules: Params.Global.formDefinitions[$this.attr('id')].rules,
				messages: Params.Global.formDefinitions[$this.attr('id')].messages,
				showErrors: function(errorMap, errorList) {
					var content = $('#content');
					
					if (!$('#error-message-container').length) {
						content.before('<div id="error-message-container" class="error-message hidden-onload"><div class="message-title"><img alt="" src="/static/images/error-icon.gif"> Error:</div><div class="message-contents"><ul>Please correct the following form error(s):<ul></ul></ul></div><div class="clear">&nbsp;</div></div>');
					} else {
						$('#error-message-container .message-contents ul ul').empty();
					}
					
					var errors = new Array();
					
					$this.find("label").removeClass("error");
					
					//The errors are a map with the second element being the message we want.
					$.each(errorMap, function(a, b) { 
						$("#" + a).parent().find("label[for=" + a + "]").addClass("error");
						errors.push(b);
					});
					
					if (errors.length > 0) {
						$('#error-message-container').removeClass('hidden-onload');
						for (var i = 0; i < errors.length; i++) {
							$('#error-message-container .message-contents ul ul').append('<li>' + errors[i] + '</li>');
						}
					} else {
						$('#error-message-container .message-contents ul ul').empty();
					}
				}
			});
		}
	});
	
})(jQuery);