/**
 *	This class manages javascript validation mechanisms for an HTML form.
 *
 *	Requirements: Prototype / Scriptaculous
 */

function Validation(formId) {
	var formId = formId;

	/**
	 * An array of validation rules to apply. Each rules is a struct with the following
	 * properties:
	 *	fieldID : The ID of the form field to be validated.
	 *	isRequired : True if the field is required, false otherwise.
	 *	checkType : The validation strategy for the field (ex: none, email, phone, custom...).
	 *	errorMessage : The error message to be displayed when the rule is broken.
	 *	isValidFunction : Used when checkType is "custom". A custom function to execute
	 *		that receives the fieldID and returns true if the field is valid, false
	 *		otherwise.
	 */
	var rules = new Array();

	/**
	 * Validates all rules for this validation object and calls the appropriate
	 * callback function depending on success or failure.
	 * Returns: True if validation succeeds, false otherwise.
	 */
	this.validate = function validate() {
		var errors = new Array();
		var success = new Array();
		var currRule = null;
		var currValue = null;

		for (var i = 0; i < rules.length; i++) {
			currRule = rules[i];
			currValue = $F(rules[i].fieldID);
			// Check required fields and validator function
			if ((currRule.isRequired && this.isEmpty(currValue))
				|| (! currRule.isValidFunction(currValue))) {
				errors.push({fieldID: currRule.fieldID, errorMessage: currRule.errorMessage});
			}
			else {
				success.push({fieldID: currRule.fieldID});
			}
		}

		if (errors.length > 0) {
			this.onValidateFailure(errors);

			this.doFailureDisplay(errors);
			this.doSuccessDisplay(success);

			return false;
		}
		else {
			this.onValidateSuccess();

			this.doFailureDisplay(errors);
			this.doSuccessDisplay(success);

			return true;
		}
	}

	this.onValidateSuccess = function onValidateSuccess() {}

	this.doSuccessDisplay = function doSuccessDisplay(fieldArray) {
		var searchPrefix = "#" + formId + " .validationFail_";
		fieldArray.each(function(currField) {
			Element.removeClassName(currField.fieldID, "exceptionField");
			var elements = $$(searchPrefix + currField.fieldID);
			for (var i = 0; i < elements.length; i++) {
				Element.hide(elements[i]);
			}
		});
	}

	this.onValidateFailure = function (errors) {
		if (errors.length == 1)
			var alertMessage = "The following problem was found when processing your submission:\n\n";
		else
			var alertMessage = "The following problems were found when processing submission:\n\n";

		for (var i = 0; i < errors.length; i++) {
			alertMessage += errors[i].errorMessage + "\n";
		}

		if (errors.length == 1)
			alertMessage += "\nPlease scroll down to correct the problem and submit the form again, or contact us if the problem persists.";
		else
			alertMessage += "\nPlease scroll down to correct the problems and submit the form again, or contact us if the problems persist.";

		alert(alertMessage);
	}

	this.doFailureDisplay = function doFailureDisplay(fieldArray) {
		var searchPrefix = "#" + formId + " .validationFail_";
		fieldArray.each(function(currField) {
			Element.addClassName(currField.fieldID, "exceptionField");
			var elements = $$(searchPrefix + currField.fieldID);
			for (var i = 0; i < elements.length; i++) {
				Element.show(elements[i]);
			}
		});
	}

	/**
	 * Adds a rule to the list of rules to enforce for validation.
	 */
	this.addRule = function addRule(fieldID, isRequired, checkType, errorMessage, customValidatorFunction) {
		var validatorFunction = null;

		// Apply the correct callback based on checkType
		switch (checkType) {
		case "phone":
			validatorFunction = this.isValidPhone;
			break;
		case "email":
			validatorFunction = this.isValidEmail;
			break;
		case "custom":
			validatorFunction = customValidatorFunction;
			break;
		default:
			validatorFunction = function (value) { return true; };
			break;
		}

		rules.push({
			fieldID : fieldID,
			isRequired : isRequired,
			checkType : checkType,
			errorMessage : errorMessage,
			isValidFunction : validatorFunction
		});
	}

	/**
	 * Clears all validation rules so no validation is performed.
	 */
	this.clearRules = function clearRules() {
		rules = new Array();
	}

	/**
	 * Returns true if a value has been entered into the given field,
	 * false otherwise.
	 */
	this.isEmpty = function (value) {
		return (value.replace(/^\s+/,'').replace(/\s+$/,'').length == 0) ? true : false;
	}

	/**
	 * Returns true if the given value is a valid phone number.
	 * Algorithm:
	 * A simple check that there are 10 - 15 numeric digits in the value.
	 */
	this.isValidPhone = function (value) {
		// Remove all non-digits
		value = value.replace(/\D/g, '');

		// Check for a length between 10 and 15
		if (value.length >= 10 && value.length <= 15) {
			return true;
		}
		else {
			return false;
		}
	}

	/**
	 * Returns true if the given value is a valid email.
	 * Algorithm:
	 * Uses a regular expression taken from the ColdFusion MX 7.02 cfform.js code
	 * since it is trustworthy.
	 */
	this.isValidEmail = function (value) {
		var emailRegex = /^[a-zA-Z_0-9-'\+~]+(\.[a-zA-Z_0-9-'\+~]+)*@([a-zA-Z_0-9-]+\.)+[a-zA-Z]{2,7}$/;

		// Trim whitespace
		value = value.replace(/^\s+/,'').replace(/\s+$/,'');
		// Test email regex
		return emailRegex.test(value);
	}
}