salmon.namespace.addNamespace("boots.forms");
boots.forms.validation = {
	FORM_ELEMENTS: {
		PASSWORD: "password"
	},
	formsSelector: "form.validate",
	validateAllClass: "validateAll",
	errorContainerID: "formErrorContainer",
	lastFiredButton: null,
	formsCollection: [],
	messages: {},
	
	/*
	 * Create instances of forms to be validated
	 */
	init: function(formID){
		boots.forms.validation.prepareServersideErrors();
		if(formID != null) {
			var form = document.getElementById(formID);
			if(form == null) {
				return;
			}
			
			// before instantiating, delete instance and remove from array
			var formsCollection = boots.forms.validation.formsCollection;
			for (var i=0;i<formsCollection.length;i++) {
				if(formsCollection[i].validationform == form) {
					delete formsCollection[i];
					formsCollection.remove(i);
					break;
				}
			}
		
			var form = new boots.forms.validation.Form(form);
			boots.forms.validation.formsCollection.push(form);
		}
		else {
			var forms = $(this.formsSelector);
			for( var i=0; i<forms.length; i++ ) {
				var form = new boots.forms.validation.Form(forms[i]);
				boots.forms.validation.formsCollection.push(form);
			}
		}
	},
	prepareServersideErrors: function(){
		var errorContainer = document.getElementById(boots.forms.validation.errorContainerID);
		if(errorContainer == null) {
			return;	
		}
		var errors = $("a.error",errorContainer);
		for(var i=0; i<errors.length;i++) {
			errors[i].onclick = boots.forms.validation.error_onClick;
		}
	},
	error_onClick:function() {
		var field = document.getElementById(this.hash.slice(1));
		if(field === null) {
			return;
		}
		var tag = field.tagName.toUpperCase();
		if (tag == "INPUT" || tag == "TEXTAREA" || tag == "SELECT") {
			field.focus();
			return false;
		}
		return true;
	}	
}


/*
 * Validation form Class:
 * @param form as DOM reference
 * @return "this" as Object Reference
 */
salmon.namespace.addNamespace("boots.forms.validation");
boots.forms.validation.Form = function(form) {
	var me = this;
	this.validationform = form;
	this.validators = [];
	this.errorContainer = document.getElementById(boots.forms.validation.errorContainerID);
	
	/*
	 * Constructor function
	 */
	var Form = (function() {
		prepareFormElements();
		addValidators();
		prepareSubmitButtons();
		me.validationform.onsubmit = form_onSubmit;
	})();
	
	
	/*
	 * Setup events for handling field highlighting
	 */
	function prepareFormElements() {
		var fields = me.validationform.elements;
		for( var i=0; i<fields.length; i++ ) {
			var field = fields[i];
			var tagName = field.tagName.toUpperCase();
			
			var isInput = 
				tagName == "INPUT" && ( field.type == "text" || field.type == "checkbox" || field.type == "radio" || field.type == "password" ) ||
				/*tagName == "SELECT" || commented out as IE returns false based on addClass JQuery Function*/
				tagName == "TEXTAREA";
				
				
			if ( isInput ) {
				field.onfocus = field_onFocus;
				field.onblur = field_onBlur;
			}
		}
	}

	/*
	 * When field receives focus
	 */
	function field_onFocus() {
		$(this).addClass("highlight");
	}
	
	/*
	 * When field loses focus 
	 */
	function field_onBlur() {
		$(this).removeClass("highlight");
	}	
	
	/*
	 * This will find all fields in the form that require validation
	 * and add an instance of the validator class to the validators collection
	 */
	function addValidators() {
		if (!boots.forms.validation.routines) {
			return;
		}
		
		var fields = me.validationform.elements;
		for( var i=0; i<fields.length; i++ ) {
			
			var field = fields[i];
			if(field.tagName.toUpperCase() == "FIELDSET") {
				continue;
			}
			
			var fieldID = fields[i].id || null;
			var fieldLabel = $(fields[i]).parents("div.field").children("div").children("label")[0] || $(fields[i]).next("label")[0] || $(fields[i]).prev("label")[0] ||  null;

			if (field.type.toUpperCase() == "RADIO") {
				fieldLabel = $(fields[i]).parents("div.field").children("div").children("span.label")[0];
			}
			
			addValidatorsByClassNames(field, fieldID, fieldLabel);
			addValidatorsByCustomRoutines(field, fieldID, fieldLabel);
		}
	}		
	
	/*
	 * @param field as DOM reference
	 * @param fieldID as string
	 * @param fieldLabel as DOM reference
	 */
	function addValidatorsByClassNames(field, fieldID, fieldLabel) {
		var field = field;
		var type = "";
		var method = null;
		var message = "";
		var routine = null;
		var classNames = field.className.split(" ");
		for( var j=0; j < classNames.length; j++ ) {
			type = classNames[j];
			var routine = getValidatorRoutine(boots.forms.validation.routines[type]);
			if (!routine) continue;
			method = getValidatorMethod(routine);
			
			message = getValidatorMessage(fieldID, fieldLabel, routine, type);

			if (!validatorExists(field, type)) {
				var validator = new boots.forms.validation.Validator(field, method, message, type);
				me.validators.push(validator);
			}
		}			
	}
	
	/*
	 * @param field as DOM reference
	 * @param fieldID as string
	 * @param fieldLabel as DOM reference
	 * @return nothing is returned
	 */	
	function addValidatorsByCustomRoutines(field, fieldID, fieldLabel) {
		var field = field;
		var type = "";
		var method = null;
		var message = "";
		var routine = null;
		for (var customRules in boots.forms.validation.customroutines[fieldID]) {
			type = customRules;
			routine = getValidatorRoutine(boots.forms.validation.customroutines[fieldID][type]);
			if (!routine) continue;
			method = getValidatorMethod(routine);
			message = getValidatorMessage(fieldID, fieldLabel, routine, type);
			
			if (!validatorExists(field, type)) {
				var validator = new boots.forms.validation.Validator(field, method, message, type);
				me.validators.push(validator);
			}
		}			
	}		
	
	/*
	 * @param validationRoutine as object reference
	 * @return validatorRoutine as object, otherwise null
	 */
	function getValidatorRoutine(validatorRoutine) {
		var routine = validatorRoutine || null;
		if (routine && typeof routine.method == "function" && typeof routine.message == "string") {
			return routine;
		}
		else {
			return null;
		}
	}		

	/*
	 * @param validationRoutine as object reference
	 * @return reference to function otherwise null
	 */
	function getValidatorMethod(validatorRoutine) {
		return validatorRoutine.method || null;
	}

	/*
	 * @param fieldID
	 * @param fieldLabel
	 * @param validatorRoutine
	 * @param validatorType
	 * @return message
	 */
	function getValidatorMessage(fieldID, fieldLabel, validatorRoutine, validatorType) {
		var message = "No message available";
		var customMessage = boots.forms.validation.messages[fieldID] || null;
		if(fieldLabel==null) {
			labelMessage = "Label";
		}
		else {
			labelMessage = $(fieldLabel).text();
		}
		var defaultMessage = validatorRoutine.message;
		
		if(customMessage && typeof customMessage[validatorType] == "string") {
			message = customMessage[validatorType];
		}
		else if(labelMessage != null && defaultMessage) {
			message = labelMessage + ": " + defaultMessage;
		}
		else if(defaultMessage) {
			message = defaultMessage;
		}
				
		return message;				
	}

	/*
	 * @param validatorField as DOM reference
	 * @param validatorType as string
	 * @return true if instance exists, otherwise false
	 */
	function validatorExists(validatorField, validatorType) {
		var exists = false;
		for(var validators in me.validators) {
			if(me.validators[validators].field == validatorField && me.validators[validators].type == validatorType) {
				exists = true;
				break;
			}
		}
		return exists;
	}
	
	/*
	 * Nasty patch to capture the last button to be clicked
	 */
	function prepareSubmitButtons() {
		var submits = $("input[type='submit'], input[type='image'], input[type='button'], button[type='submit']", me.form);
		for( var i=0; i<submits.length; i++ ) {
			submits[i].onclick = function() {
				boots.forms.validation.lastFiredButton = this;
			}
		}
	}
	
	/*
	 * @return true if form is valid, false otherwise
	 */
	function form_onSubmit(){
		clearErrors();
		clearSuccessMessage();
		var valid = true;
		
		// Validate all fields
		if ($(boots.forms.validation.lastFiredButton).hasClass(boots.forms.validation.validateAllClass)) {
			valid = validateFields(me.validators);
		}
		
		// Validate contextual fields
		for(var buttons in boots.forms.validation.contextualbuttons) {
			var button = document.getElementById(buttons);
			if(boots.forms.validation.lastFiredButton == button ) {
				var contextualCollection = [];
				for(var names in me.validators) {
					var validator = me.validators[names]; //object
					var fieldID = validator["fieldID"]; //string
					
					var requiredFields = boots.forms.validation.contextualbuttons[buttons];
					for(var i = 0; i < requiredFields.length; i++) {
						// if there is a validator for the required field then add to collection
						if(requiredFields[i] == fieldID) {
							contextualCollection.push(validator);
						}
					}
				}
				valid = validateFields(contextualCollection);
			}
		}
		return valid;
	}
	
	/*
	 * Clear existing errors
	 */
	function clearErrors() {
		clearErrorContainer();
		clearFieldErrors();
	}
	
	/*
	 * If the success message is showing and the user submits the form, 
	 * then clear success message.
	 */
	function clearSuccessMessage() {
		var successMessage = document.getElementById("formSuccessContainer");
		if(successMessage == null) {
			return;
		}
		$(successMessage).remove();		
	}
	
	function clearErrorContainer() {
		var errorListContainer = me.errorContainer;
		if (errorListContainer == null) {
			return;
		}
		$(errorListContainer).addClass("hide");
		
		if(errorListContainer.getElementsByTagName("div")[0].getElementsByTagName("ul").length > 0) {
			var errorList = errorListContainer.getElementsByTagName("div")[0].getElementsByTagName("ul")[0];
			errorList.innerHTML = "";
		}
	}
	
	function clearFieldErrors() {
		var formsCollection = boots.forms.validation.formsCollection;
		for(var i=0; i<formsCollection.length;i++) {
			var validators = formsCollection[i].validators;
			for (var property in validators) {
				$(validators[property].fieldIndicator).removeClass("error");
			}
		}	
	}
	
	/*
	 * @param collection in array format
	 * @return true if no errors, false otherwise
	 */
	function validateFields(collection) {
		var errorCollection = [];
		for(var i=0; i<collection.length; i++) {
			if(!collection[i].validate()) {
				errorCollection.push(collection[i]);
			}
		}
		if (errorCollection.length > 0) {
			showErrors(errorCollection);
		}	
		return (errorCollection.length == 0)
	}
	
	function getHTMLFirstChild(html) {
        var root = document.createElement("div");
        root.innerHTML = html;
        return root.firstChild;
	}
	
	/*
	 * @param errorCollection in array format
	 */
	function showErrors(errorCollection) {
		var errorListContainer = me.errorContainer;
		if (errorListContainer == null) {
			alert("The form has errors.");
			return;
		}
		
		if(errorListContainer.getElementsByTagName("div")[0].getElementsByTagName("ul").length == 0) {
			$("div.messageerrorInner div.container", errorListContainer).append("<ul></ul>");
		}
		
		var errorList = errorListContainer.getElementsByTagName("div")[0].getElementsByTagName("ul")[0];
		for(var i=0;i<errorCollection.length;i++) {
			var li = salmon.common.getHTMLFirstChild(createErrorItem(errorCollection[i].fieldID, errorCollection[i].message));
			var a = li.getElementsByTagName("a")[0];
			a.className="error";
			a.onclick = boots.forms.validation.error_onClick;
			errorList.appendChild(li);
			$(errorCollection[i].fieldIndicator).addClass("error");		
		}
		$(errorListContainer).removeClass("hide");
		$("a#errors").focus();
	}
	
	/*
	 * @param fieldID
	 * @param message
	 * @returns HTML snippet
	 */
	function createErrorItem(fieldID, message) {
		var listItem = '';
		listItem += '<li>';
		listItem +=		'<a href="#'+ fieldID + '">' + message + '</a>';
		listItem += '</li>';
		return listItem;
	}	
}


/*
 * Validator Class:
 * @param field as DOM reference
 * @param routine as reference to function
 * @param message as string
 * @param type as string
 * @return "this" as Object Reference
 */

boots.forms.validation.Validator = function(field, routine, message, type) {
	var me = this;
	this.field = field;
	this.fieldID = field.id;
	this.type = type;
	this.routine = routine;
	this.message = message;
	this.isValid = false;
	this.label = getLabel();
	this.fieldIndicator = getFieldIndicator();
	this.fieldContainer = getFieldContainer();
	
	/*
	 * @return DOM reference to label
	 */	
	function getLabel() {
		return $(me.field).parents("div.field").children("div").children("label")[0] || $(me.field).next("label")[0] || $(me.field).prev("label")[0] ||  null;
	}
	
	/*
	 * @return DOM reference to indicator
	 */		
	function getFieldIndicator() {
		return $(me.field).parents("div.field").children("div").children("span.label")[0] || $(me.field).parents("div.field").children("div").children("label")[0] || null;
	}
	
	/*
	 * @return DOM reference to field container
	 */	
	function getFieldContainer() {
		return $(me.field).parents("div.field")[0] || null;
	}	
}

boots.forms.validation.Validator.prototype = {
	/*
	 * @return true if valid, otherwise false
	 */
	validate: function() {
		this.isValid = this.routine.call(this);
		return this.isValid;
	}
}

$(document).ready(function(){ boots.forms.validation.init(); });

salmon.namespace.addNamespace("boots.forms.validation.routines");
boots.forms.validation.routines = {
	eVoucherDateRequired: {
		method: function() {
			var result = false;
			var field = this.field;
			var value = $.trim(field.value);
			
			var specificDate = document.getElementById("sendOption1");
			if(specificDate == null) {
				return true;
			}
			
			if(specificDate.checked) {
				result = (value.length > 0);
			}
			else {
				result = true;
			}

			return result;
		},
		message: "The date is required for eVoucher."
	},
	addressRequiredBFPO: {
		method: function() {
			var result = false;
			var field = this.field;
			var value = $.trim(field.value);
			var tagName = field.tagName.toUpperCase();
			
			var prefix="";
			var indexI = field.name.indexOf("_");
			if (indexI!=-1) {
				prefix = field.name.substring(0, indexI+1);
			}

			var postcode = document.getElementById(prefix+"zipCode");
			var regex = new RegExp("^(B|b)(F|f)(P|p)(O|o)[ ]?[0-9]{1,3}$");
			if (postcode && regex.test(postcode.value)) {
				return true;
			}

			switch(tagName) {
				case "INPUT":
					switch (field.type) {
						case "text":
							// trimmed version
							result = value.length > 0;
							break;
						case "password":
							// non trimmed version
							result = field.value.length > 0;
							break;							
						case "checkbox":
							result = field.checked;
							break;
						case "radio":
							var radios = $($(this.field).parent().parent()[0]).find("input");
							for(var i=0;i<radios.length;i++) {
								if(radios[i].checked) {
									result = true;
									break;
								}
							}
							break;							
					}
					break;
				case "SELECT":
					result = value.length > 0;
					break;
				case "TEXTAREA":
					result = value.length > 0;
					break;
				default:
					result = true;
			}
			return result;
		},
		message: "This field is required."
	},
	required: {
		method: function() {
			var result = false;
			var field = this.field;
			var value = $.trim(field.value);
			var tagName = field.tagName.toUpperCase();
			switch(tagName) {
				case "INPUT":
					switch (field.type) {
						case "text":
							// trimmed version
							result = value.length > 0;
							break;
						case "password":
							// non trimmed version
							result = field.value.length > 0;
							break;							
						case "checkbox":
							result = field.checked;
							break;
						case "radio":
							var radios = $($(this.field).parent().parent()[0]).find("input");
							for(var i=0;i<radios.length;i++) {
								if(radios[i].checked) {
									result = true;
									break;
								}
							}
							break;							
					}
					break;
				case "SELECT":
					result = value.length > 0;
					break;
				case "TEXTAREA":
					result = value.length > 0;
					break;
				default:
					result = true;
			}
			return result;
		},
		message: "This field is required."
	},
	email: {
		method: function() {
			var field = this.field;
			var value = field.value;
			
			//build the regex
            var SP        = "\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~";
            var ATEXT     = "[a-zA-Z0-9" + SP + "]";
            var ATOM      = ATEXT + "+";
            var DOTATOM   = "\\." + ATOM;
            var LOCALPART = ATOM + "(" + DOTATOM + ")*";
            //RFC 1035 tokens for domain names:
            var LETTER    = "[a-zA-Z]";
            var LETDIG    = "[a-zA-Z0-9]";
            var LETDIGHYP = "[a-zA-Z0-9-]";
            var RFCLABEL  = LETDIG + "(" + LETDIGHYP + "{0,61}" + LETDIG + ")?";
            var DOMAIN    = RFCLABEL + "(\\." + RFCLABEL + ")*\\." + LETTER + "{2,6}";
            //Combined together, these form the allowed email regexp allowed by RFC 2822:
            var ADDRSPEC  = "^" + LOCALPART + "@" + DOMAIN + "$";

			//var regex = new RegExp("^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$|^$");
			var regex = new RegExp(ADDRSPEC+"|^$");
			return regex.test(value);
		},
		message: "Invalid email address."
	},
	namecharacters: {
		method: function() {
			var field = this.field;
			var regex = new RegExp("^([a-zA-Z0-9- '`])+$|^$");
			var value = field.value;
			return regex.test(value);
		},
		message: "Invalid name characters. 2 or more characters. Only letters, numbers, spaces, hyphens and apostrophes will be accepted."
	},
	phonenumber: {
		method: function() {
			var field = this.field;
			var regex = new RegExp("^[+]?[0-9 ]*\\({1}[0-9]+\\){1}[0-9 ]+$|^[+]?[0-9 ]+$|^$");
			var value = field.value;
			return regex.test(value);
		},
		message: "Invalid phone number."
	},
	password: {
		method: function(){
			var field = this.field;
			var regex = new RegExp("(^[A-Za-z0-9]{6,12}$)|(^$)");
			var value = field.value;
			return regex.test(value);
		},
		message: "Invalid password. The password can be alphanumeric and has to be between 6 and 12 characters in length."
	},
	alphadigitsspecialfullstop: {
		method: function(){
			var field = this.field;
			var regex = new RegExp("^([a-zA-Z0-9- '`.])+$|^$");
			var value = field.value;
			return regex.test(value);
		},
		message: "Invalid characters. Only letters, numbers, spaces and special characters (hyphen, apostrophe and full stop) are allowed."
	},
	number: {
		method: function(){
			var field = this.field;
			var regex = new RegExp("^([0-9])+$|^$");
			var value = field.value;
			return regex.test(value);
		},
		message: "Invalid characters. Only numbers are allowed."
	}
}

salmon.namespace.addNamespace("boots.forms.validation.customroutines");
boots.forms.validation.customroutines = {
	reglogonPasswordVerify: {
		matches: {
			method: function() {
				var valid = true;
				var field = this.field;
				var password = document.getElementById("reglogonPassword");
				if (password != null) {
					 if(field.value != password.value) {
					 	valid = false;
					 }
				}
				return valid;
			},
			message: "The field does not match"
		}
	},
	confirmRecipientEmail: {
		matches: {
			method: function() {
				var valid = true;
				var field = this.field;
				var email = document.getElementById("recipientEmail");
				if (email != null) {
					 if(field.value != email.value) {
					 	valid = false;
					 }
				}
				return valid;
			},
			message: "The email does not match the reciever email address"
		}
	},
	youngPersonMonth: {
		required: {
			method: function() {
				var valid = true;
				var field = this.field;				
				var month_value = $.trim(field.value);
				var youngPersonYear = document.getElementById("youngPersonYear");
				if(month_value.length == 0) {
					if (youngPersonYear != null) {
						if ($.trim(youngPersonYear.value).length == 0) {
							valid = false;
						}
					}
				}
				return valid;
			},
			message: "Please enter the age of the youngest patient. Either or Both Month,Year"
		}
	},
	oldestPersonMonth: {
		required: {
			method: function() {
				var valid = true;
				var field = this.field;				
				var month_value = $.trim(field.value);
				var oldestPersonYear = document.getElementById("oldestPersonYear");
				if(month_value.length == 0) {
					if (oldestPersonYear != null) {
						if ($.trim(oldestPersonYear.value).length == 0) {
							valid = false;
						}
					}
				}
				return valid;
			},
			message: "Please enter the age of the oldest patient. Either or Both Month,Year"
		}
	},
	conTelephone: {
		required: {
			method: function() {
				var valid = true;
				var field = this.field;		
				var value = $.trim(field.value);
				var preference = document.getElementById("conPreference");
				if(preference != null && preference.value == "Phone") {
					valid = value.length > 0;
				}
				return valid;
			},
			message: "Please enter a telephone number."
		}
	}
}

salmon.namespace.addNamespace("boots.forms.validation.contextualbuttons");
boots.forms.validation.contextualbuttons = {
	dispatch_addressSearch: ["zipCode"],
	PCSValidateCustomerDetails: ["firstName", "lastName", "telphPrimary", "telphSecondary", "telphMobile", "email"],
	PCSValidatePatientDetails: ["patientFirstName", "patientLastName", "patientDOB"]	
}