/**
 * General ILR form validation framework. This file includes the core validation routine and calls out to
 * functions in ILR.form.validation.util for specific tests. The user receives the error messages and 
 * displays them.
 * @namespace ILR.form
 * @module ILR.form.validation
 * @requires ILR.form.validation.util
 * @author Rebecca Younes
 */
ILR.namespace("form.validation");

	
/**
 * @description Set all form fields to required. Use when there are more required than optional fields, so only
 * the optional ones have to be explicitly listed.
 * @method setAllFieldsRequired
 * @param {Object|String|Array} f A form element or id, or an array of form elements and form element ids
 * @return {void}
*/
ILR.form.validation.setAllFieldsRequired = function(f) {
	// RY 6/15/07 Technically, we could just as well use this instead of 
	// ILR.form.validation, since formally there's no difference between 
	// a class and a module in Javascript. For the sake of clarity, since
	// we don't conceptualize this as a class but only as a module/
	// namespace, we'll use the more verbose version.
	ILR.form.validation._setDefaultRequiredVal(f, true);
};

/**
 * @description Set all form fields to optional. Use when there are more optional than required fields, so only
 * the required ones have to be explicitly listed. Note that the default assumption is that a field is optional
 * unless specified otherwise, so this function doesn't normally need to be called except for clarity.
 * @method setAllFieldsOptional
 * @param {Object|String|Array} f A form element or id, or an array of form elements and form element ids
 * @return {void}
 */
ILR.form.validation.setAllFieldsOptional = function(f) { 
	ILR.form.validation._setDefaultRequiredVal(f, false);
};

/**
 * @description Sets all form fields to either required or optional. 
 * @method _setDefaultRequiredVal
 * @param {Object|String|Array} f A form element or id, or an array of form elements and form element ids
 * @param {Boolean} reqd Specifies whether the fields should be set to required (true) or optional (false)
 * @return {void}
 */
ILR.form.validation._setDefaultRequiredVal = function(f, reqd) {

	var i,
		j,
		el;

	f = toArray(f); // now f is an array of form elements and form ids
		
	for (i = 0; i < f.length; i++) {
		el = getFormInputFields(f[i]);		
		for (j = 0; j < el.length; j++) {
			el[j].required = reqd;
		}
	}
};

/**
 * @description Sets the options for all form fields based on a structure.
 * @method setFormOptions
 * @param {Object|String|Array} f A form element or id, or an array of form elements and form element ids
 * @param {Object} options A structure specifying the objects for the form and individual form inputs
 */
ILR.form.validation.setFormOptions = function(f, options) {
	var i,
		j,
		el,
		key,
		elname,
		formObj;

	f = toArray(f); // now f is an array of form elements and form ids
		
	for (i = 0; i < f.length; i++) {
		if( ILR.util.string.isString(f[i]) ) {
			formObj = document[f[i]];
		}
		
		if( options[formObj.name] ) {
			
			// assign a custom validation function to the form
			if (options[formObj.name].custom) {
				formObj.customValidation = options[formObj.name].custom
			}
			
			el = getFormInputFields(formObj);
			
			// set default values for all input properties
			for( key in options[formObj.name].defaults ) {
				for(j = 0; j < el.length; j++ ) {
					el[j][key] = options[formObj.name].defaults[key];
				}
			}
			
			// set individual input properties
			for( elName in options[formObj.name].inputs ) {
				// make sure this element is in the DOM on this rendering
				if( formObj[elName] ) {
					for( key in options[formObj.name].inputs[elName] ) {
						if( formObj[elName][0] && /^(radio|checkbox)/.test(formObj[elName][0].type) ) {
							formObj[elName][0][key] = options[formObj.name].inputs[elName][key];
						} else {
							formObj[elName][key] = options[formObj.name].inputs[elName][key];
						}
					}
				}
			}
		}
	}
};

/**
 * @description Return the descriptor of the field to be used in reporting an input error. If a 
 * descriptor has been specified for the field, use it; else derive one from the element name value.
 * This step imposes a naming convention on form elements that don't have a special error message assigned.
 * E.g., first_name => first name
 * @method getFieldDescriptor
 * @param {HTMLElement} e The form element
 * @param {Boolean} sentInitial Return the sentence initial (true) or medial (false) version.
 * @return {String} The descriptor
 */
ILR.form.validation.getFieldDescriptor = function(e, sentInitial) {

	var descr = e.descr ? e.descr : e.name.replace(/_/g, " ");
	
	return sentInitial ? descr.substring(0,1).toUpperCase() + descr.substring(1,descr.length) :
						 descr.substring(0,1).toLowerCase() + descr.substring(1,descr.length);
	
};

/**
 * @description Validate form input. Invoked from another function that controls display of 
 * the error messages returned. It should not be bound directly to an event, because inside 
 * the function "this" refers to the module, whereas if used as an event handler "this" would 
 * refer to the element it is bound to. 
 * @method validate
 * @param {Event} event The event (e.g., form submit, submit button click) that triggered the action
 * @param {String|HTMLElement} form The form id string or element
 * @return {Object} An object mapping form element ids to their error types. If an element id is not 
 * included, there was no error in the input. 
 * Error types:
 * "required": a required field is empty
 * "format": format error - e.g., invalid email format
 * "filetype": invalid filetype for file upload
 */
ILR.form.validation.validate = function(event, form) {

	var errors = {},
		formObj = getElement(form),
		elements = getFormInputFields(formObj),
		i,
		j,
		e;
		
	if (typeof event == "undefined") {
		event = window.event;
	}
	// RY Try this instead: event = event || window.event;
	
		
	for (i = 0; i < elements.length; i++) {
		e = elements[i];
		
		if (e.disabled) { 
			continue; 
		}
		
		if (e.requireOne) {	
			// If this is a radio/checkbox element, test only the 0th element
			if (!formObj[e.name][0] || e == formObj[e.name][0]) {
				els = e.requireOne.concat(e.name);
				if (!ILR.form.validation.util.oneIsFilledIn(els, formObj)) {
					errors[e.name] = "required";
					continue;
				}
			}	
 		}

		// Text input elements, textareas, and select elements:
		// Single-select element = type "select-one", multiple-select element = type "select-multiple".
		// Text input = type "text", textarea = type "textarea".
		if ( /^(text|select)/.test(e.type) ) {	
			
			// 1. Check for blank required elements
			if (ILR.form.validation.util.isblank(e.value)) {
				if (e.required) {
					errors[e.name] = "required"; 
				}
			}

			// 2. Non-blank form fields: validate as specified in the form validation rules
			else if (e.type == "text") { 
		
				if (e.format != null) {
		
					switch (e.format) {
			
						case "email": 
							if (ILR.form.validation.util.isValidEmail(e.value)) { continue; }
							break;

						// Could match against state vs province selection (i.e., only allow Canadian postal
						// code format with Canadian province selection).					
						case "zipcode":
							if (ILR.form.validation.util.isValidUSOrCanZipcode(e.value)) { continue; }
							break;
							
						case "us_zipcode":
							if (ILR.form.validation.util.isValidUSZipcode(e.value)) { continue; }
							break;
					
						case "phone":
							if (ILR.form.validation.util.isValidPhoneNumber(e.value)) { continue; }
							break;
							
						case "local_phone":
							if (ILR.form.validation.util.isValidLocalPhoneNumber(e.value)) { continue; }
							break;
							
						case "full_phone":
							if (ILR.form.validation.util.isValidFullPhoneNumber(e.value)) { continue; }
							break;

						case "intnl_phone":
							if (ILR.form.validation.util.isValidInternationalPhoneNumber(e.value)) { continue; }
							break;
													
						case "year":
							if (ILR.form.validation.util.isYear(e.value)) { continue; }
							break;
					
						case "year2":
							if (ILR.form.validation.util.isTwoDigitYear(e.value)) { continue; }
							break;
							
						case "year4":
							if (ILR.form.validation.util.isFourDigitYear(e.value)) { continue; }
							break;
					
						case "date_mmyyyy":
							if (ILR.form.validation.util.isDate_mmyyyy(e.value)) { continue; }
							break;		
					
						case "date_mdy":
							if (ILR.form.validation.util.isDate_mdy(e.value)) { continue; }
							break;		
					
						case "date_mmddyyyy":
							if (ILR.form.validation.util.isDate_mmddyyyy(e.value)) { continue; }
							break;
											
						case "int":
							if (parseInt(e.value)) { continue; }
							break;
					
						case "float":
							if (parseFloat(e.value)) { continue; }
							break;
					
						default: 
							continue;											
					}
			
					errors[e.name] = "format";
				}
			}
		}
	
		// Radio buttons and checkboxes
    	else if ( e.type == "radio" || e.type == "checkbox" ) {

			var elementName = e.name;
		
			// Check only once per group of radio buttons/checkboxes.
			// Optionality and special error messages are defined as attributes of the first element
			// of the array.
			// In the case of singleton checkboxes, no array is defined, so send the single
			// element to hasOptionSelected().
			if ( formObj[elementName][0] && e != formObj[elementName][0] ) { continue; }
		
			if (e.required) {
				if (!ILR.form.validation.util.hasOptionSelected(formObj, elementName)) {
					errors[e.name] = "required";
				}
			}
		}
	
		// File input elements
		else if (e.type == "file") {
	
			// Missing required files
			if (ILR.form.validation.util.isblank(e.value)) {
				if (e.required) {
					errors[e.name] = "required";
				}
			}
			// Invalid filetype
			else if (e.filetypes != null) {
				if (!ILR.form.validation.util.hasValidFileType(e.value, e.filetypes)) {
					errors[e.name] = "filetype";
				}
			}		
		}	

	}  // end for

	// Execute custom form validation code
	if (formObj.customValidation) {
		formObj.customValidation(formObj, errors); 		
	}	

	return errors;
};


/*******************************************************************************
Another way of handling radio buttons/checkboxes - handle each element as it
passes through the main loop.  checkedOptions is declared at top of function as
checkedOptions = new Array();

		else if ( e.type == "radio" || e.type == "checkbox" ) {
		
			var elementName = e.name;
			var firstElement = formObj[elementName][0];
			var lastElement = formObj[elementName][formObj[elementName].length - 1];
			
			// Optionality for the group is defined as an attribute of the first element, for convenience.
			if (!firstElement.optional) {	
				// We've already seen a checked option for this group
				if (checkedOptions[elementName] == true) {
					continue;
				}
				// If this element is checked, add its name to the array of checked options
				else if (e.checked == true) {
					checkedOptions[elementName] = true;
				}
				// If we've reached the last element and no options have been checked, issue an error message
				else if (e == lastElement) {
					// An error message for the group is defined as an attribute of the first 
					// element, for convenience.
					emptyFields += getMsg(firstElement);
					
				}
			}
		}
********************************************************************************/