/**
 * Static utility class providing validation-related functionality.
 * @static
 */
HD.util.Validation = {
	/**
	 * Predefined regular expressions.
	 */
	regex : {
		required : /^\S[^\t]*$/,
		alpha : /^([a-zA-Z\s]*)$/,
		notAlpha : /^([^a-z^A-Z]*)$/,
		numeric : /^([0-9]*)$/,
		notNumeric : /^\D*$/,
		alphaNumeric : /^([a-zA-Z0-9\s]*)$/,
		email : /^([\_]*([a-zA-Z0-9\._\-#]+)+@([a-zA-Z][a-zA-Z0-9\-]+(\.|\-*\.))+[a-zA-Z]{2,6})*$/,
		phone : /^(\d?[\( -\.]*\d{3}[\) -\.]*\d{3}[ -\.]*\d{4})*$/,
		year : /^((19|20)[\d]{2,2})*$/,
		name : /^[a-zA-Z0-9- \/'‘:#\(\)\.\u0100-\u017F]*$/,
		license : /^([a-zA-Z0-9\s\.-]*)$/,
		street :  /^[a-zA-Z0-9_\-\. '#\u0100-\u017F]*$/,
		zip : /^(\d{5})*$/,
		date : function(form, monthName, dayName, yearName, isFuture, spread) {
			try {
				var common = HD.util.Common;
				form = HD.util.Dom.get(form);
				var month = form[monthName].value;
				var day = form[dayName].value;
				var year = form[yearName].value;
				
				if(common.hasValue(month) && common.hasValue(day) && common.hasValue(year)) {
					var date = HD.util.Date.setDate(month, day, year);
					if(common.hasValue(spread)) {
						return (new Date().getTime() - date.getTime()) >= spread;
					} else {
						return HD.util.Date[isFuture ? "isFuture" : "isPast"](date);
					}
				}
			} catch(e) {}
			return false;
		}
	},
	
	/**
	 * Adds CSS classes to form elements to indicate errors.
	 * 
	 * @param {String|HTMLForm} form the form element
	 * @param {Object} validator the validation object
	 * @param {Array} errors the list of error produced for the form using the validator
	 */
	showErrors : function(form, validator, errors) {
		var dom = HD.util.Dom;
		var classes = HD.CSS_CLASSES;
		
		var formEl = dom.get(form);
		if(formEl == null) {
			alert("ERROR: could not obtain a reference to the form");
		}
		
		var fieldToLabel = {};
		var fields = validator.fields;
		// loop over all validation fields to remove error classes and create a field to label mapping
		for(var i = 0, len = fields.length; i < len; i++) {
			var fieldDef = fields[i];
			fieldToLabel[fieldDef.name] = fieldDef.label;
			var fieldLabels = fieldDef.label;
			fieldLabels = fieldLabels.split(",");
			for(var j = 0, len1 = fieldLabels.length; j < len1; j++) {
				dom.removeClass(form[fieldLabels[j]], classes.ERROR);
			}
			var field = HD.util.Dom.getField(form, fieldDef.name);
			dom.removeClass(field, classes.ERROR);
		}
		
		// loop over the errors and add error classes to the fields and their labels
		for(var k = 0, len2 = errors.length; k < len2; k++) {
			var fieldName = errors[k].fieldName;
			fieldLabels = fieldToLabel[fieldName];
			fieldLabels = fieldLabels.split(",");
			for(j = 0, len1 = fieldLabels.length; j < len1; j++) {
				dom.addClass(form[fieldLabels[j]], classes.ERROR);
			}
			field = HD.util.Dom.getField(form, fieldName);
			dom.addClass(field, classes.ERROR);
		}
	},
	
	/**
	 * Removed CSS classes from form elements to clear errors.
	 * 
	 * @param {String|HTMLForm} form the form element
	 * @param {Object} validator the validation object
	 */
	hideErrors : function(form, validator) {
		var dom = HD.util.Dom;
		var classes = HD.CSS_CLASSES;
		
		var formEl = dom.get(form);
		if(formEl == null) {
			alert("ERROR: could not obtain a reference to the form");
		}

		var fields = validator.fields;
		for(var i = 0, len = fields.length; i < len; i++) {
			var fieldDef = fields[i];
			var field = HD.util.Dom.getField(form, fieldDef.name);
			dom.removeClass(field, classes.ERROR);
			
			var fieldLabels = fieldDef.label.split(",");
			for(var j = 0, len1 = fieldLabels.length; j < len1; j++) {
				dom.removeClass(form[fieldLabels[j]], classes.ERROR);
			}
		}
	},
	
	/**
	 * Validates the form using the provided validator and returns the list of errors.
	 * 
	 * @param {String|HTMLForm} form the form to validate
	 * @param {Object} validator the validator object containing validation rules
	 * @return the list of errors
	 */
	validateForm : function(form, validator) {
		var dom = HD.util.Dom;
		
		var formEl = dom.get(form);
		if(formEl == null) {
			alert("ERROR: could not obtain a reference to the form");
			return false;
		}
		
		this.overrideRegexes = validator.regexes;
		var formErrors = [];
		var errorMessages = [];
		var fields = validator.fields;
		for(var i = 0, len = fields.length; i < len; i++) {		
			this.validateField(fields[i], form, formErrors, errorMessages, validator);
		}
		return {formErrors:formErrors, errorMessages:errorMessages};
	},
	
	/**
	 * Validates the field.
	 * 
	 * @param {Object} fieldDef field definition containing element id, label id, and validation rules
	 * @param {String|HTMLElement} form the form the field belongs to
	 * @param {Array} formErrors the list of errors generated for the form so far
	 * @param {Array} errorMessages the list of errors messages generated so far
	 */
	validateField : function(fieldDef, form, formErrors, errorMessages, validator) {
		var fieldName = fieldDef.name;		
		var field = HD.util.Dom.getField(form, fieldName);
		var fieldValue = HD.util.Dom.getFieldValue(field);
		var regexes = fieldDef.regex.split(",");
		
		var isValid = true;
		for(var i = 0, len = regexes.length; i < len; i++) {
			var regex = regexes[i];			
			var regexType = this.getRegexType(regex);
			var regexValue = this.getRegexValue(regex);
			// some validation only happens under certain conditions
			var isConditionMet = true;
			if(!!fieldDef.condition) {
				// validate condition
				isConditionMet = this.validateField(fieldDef.condition, form);				
			}
			
			if(isConditionMet) {
				var isMatch = false;			
				if(regexType=="2") {
					if(typeof regexValue == "function"){
					isMatch = regexValue();				
					}
				} else {	
					isMatch = fieldValue.match(regexValue);
				}
				
				if(!isMatch) {
					if(formErrors != null) {
						formErrors.push({fieldName : fieldName, errorMessage : regex, errorType : regex});
					}
					if(errorMessages != null) {
						var messageTemplate = validator.messages[regex];
						if(!HD.util.Common.hasValue(messageTemplate)) {
							if(fieldDef.messages != null) {
								messageTemplate = fieldDef.messages[regex];
							}
						}
						if(HD.util.Common.hasValue(messageTemplate)) {
							var message = messageTemplate.replace(/\{0\}/, fieldDef.displayName);
							errorMessages.push(message);
						}
					}
					isValid = false;
				}
			}
		}
		
		return isValid;
	},
	
	/**
	 * Gets the regular expression by name.  Checks override regexes first, falls back on predefined one, 
	 * and returns null if no matches are found.
	 * 
	 * @param {String} regexName the name of the regular expression
	 * @return the regular expression or null
	 */	
	getRegexValue : function(regexName) {
		if(this.overrideRegexes != null 
				&& this.overrideRegexes[regexName] != null) {			
			return this.overrideRegexes[regexName];
		}			
		return this.regex[regexName];
	},
	
	getRegexType : function(regexName) {
		if(this.overrideRegexes != null 
				&& this.overrideRegexes[regexName] != null) {			
			return "2";
		}		
		return "1";
	}
};
