/**
 *	@author Jan De Wilde
 *	@date 29.08.2011
 *	@description Formchecking script. Uses a strict syntax for the elements and form.
 *	@version 1.4.3
 */

(function($) {
	// Outer shell of the plugin
	$.fn.formChecker = function(options, callback) {
		if($.isFunction(options)) {
			callback = options;
			options = null;
		}
		options = $.extend($.fn.formChecker.defaults, options);
		
		// Iterate over the matched node set
		return this.each(function(){
			// Fire the optional options.setup function
			$.isFunction(options.setup) && options.setup.call(this);
			
			// Fetch all form types
			var _form = this;
			
			isNOkMessagePresent(_form);
			
			$(this).bind('submit',function(e) {
				var formFields = $('.row input, .row textarea, .row select',$(_form));
				var arrFormFields = [];
				var arrFieldNames = [];
				$(formFields).each(function(i,element){
					var elementName = $(element).attr('name');
					if($.inArray(elementName,arrFieldNames) === -1){
						arrFormFields.push(element);
						arrFieldNames.push(elementName);
					}
				});

				var isValid = true;
				$(arrFormFields).each(function(i,element) {
					$parentRow = $(element).closest('.row');
					var elmValue = $.trim($(element).val());
					var elmName = $(element).attr('name');
					var checkResult = null;

					switch($(element)[0].tagName){
						case 'INPUT':
							switch($(element).attr('type')){
								case 'text':
								case 'file':
									checkResult = caseTextFile(element,$parentRow,elmValue);
								break;
								case 'checkbox':
								case 'radio':
									checkResult = caseRadioCheckbox(element,$parentRow,elmName);
								break;
								default:
									// none
								break;
							}
						break;
						case 'SELECT':
							checkResult = caseSelect(element,$parentRow,elmName);
						break;
						case 'TEXTAREA':
							checkResult = caseTextarea(element,$parentRow,elmValue);
						break;
						default:
						break;
					}

					if(isFalse(checkResult)){
						isValid = checkResult;
					}
				});
				
				if($.fn.formChecker.defaults.isValid != null) {
					isValid = $.fn.formChecker.defaults.isValid;
				}

				if(!isValid){
					setNotification(_form);
					e.preventDefault();
				}

			});

			$.isFunction(callback) && callback.call(this);
		});
	}
	
	function setNotification(_form) {
		if(!$('.notification',$(_form)).length){
			$('<p></p>')
			.hide()
			.addClass('notification')
			.text($.fn.formChecker.defaults.nOkMessage)
			.prependTo($('fieldset:eq(0)',_form))
			.show($.fn.formChecker.defaults.nOkMessageDuration, $.fn.formChecker.defaults.error);
		}
	}
	
	function isNOkMessagePresent(_form) {
		if($('.nOkMessage',_form).length) {
			$.fn.formChecker.defaults.nOkMessage = $('.nOkMessage',_form).val()
		}
	}
	
	function isRequired(element) {
		var required = false;
		if($(element).hasClass('required')) {
			required = true;
		}
		return required;
	}
	
	// Check if returned value is false
	function isFalse(checkResult){
		return (checkResult !== undefined && checkResult !== true);
	}
	
	// Case for input type text/file
	function caseTextFile(element,$parentRow,elmValue){
		if($(element).hasClass('email')){
			return handleErrorClass($parentRow,validateEmail(element,elmValue));
		} else if($(element).hasClass('number')){
			return handleErrorClass($parentRow,validateNumber(element,elmValue));
		} else if($(element).hasClass('url')){
			return handleErrorClass($parentRow,validateUrl(element,elmValue));
		} else if($(element).hasClass('date')){
			return handleErrorClass($parentRow,validateDate(element,elmValue));
		} else {
			return handleErrorClass($parentRow,validateString(element,elmValue));
		}
	}
	
	// Case for input type checkbox / radio
	function caseRadioCheckbox(element,$parentRow,elmName) {
		if(isRequired(element)){
			return handleErrorClass($parentRow,validateRadioCheckbox(elmName));
		}
	}
	
	// Case for select
	function caseSelect(element,$parentRow,elmName) {
		if(isRequired(element)){
			return handleErrorClass($parentRow,validateSelect(elmName));
		}
	}
	
	// Case for textarea
	function caseTextarea(element,$parentRow,elmValue) {
		if(isRequired(element)){
			return handleErrorClass($parentRow,validateTextarea(elmValue));
		}
	}
	
	// Set or remove error class on parentrow
	function handleErrorClass($parentRow,state){
		if(!state){
			if(!$parentRow.hasClass('error')){
				$parentRow.addClass('error');
				return false;
			}
		} else {
			$parentRow.removeClass('error');
		}
		return state;
	}
	
	// Check if value isset
	function validateString(element,elmValue){
		if(isRequired(element) && elmValue === '') {
			return false;
		}
		return true;
	}
	
	// Check if value isset
	function validateTextarea(elmValue){
		return (elmValue !== '');
	}

	// Check if value is numeric
	function validateNumber(element,elmValue){
		if((isRequired(element) && isNaN(elmValue) || isRequired(element) && elmValue === '') || (elmValue !== '' && isNaN(elmValue))) {
			return false;
		}
		return true;
	}
	
	// Check for e-mail syntax
	function validateEmail(element,elmValue) {
		if(isRequired(element) || elmValue != '') {
			var objRE = /^[\w-\.\']{1,}\@([\da-zA-Z\-]{1,}\.){1,}[\da-zA-Z\-]{2,}$/;
			return (elmValue !== '' && objRE.test(elmValue));
		}
		return true;
	}
	
	// Check radio or checkbox groups
	function validateRadioCheckbox(elmName){
		var selected = false;
		if($('input[name="'+elmName+'"]:checked').length) {
			selected = true;
		}
		return selected;
	}
	
	// Check if select value set
	function validateSelect(elmName){
		var selected = true;
		var $optionSelected = $('select[name="'+elmName+'"] option:selected');
		if($optionSelected.index() === 0 || $optionSelected.val() === -1){
			selected = false;
		}
		return selected;
	}
	
	// Check for URL
	function validateUrl(element,elmValue){
		if(isRequired(element) || elmValue != '') {
			var regexp = /^((((ftp|http|https):\/\/)?)(www\.)([a-z0-9\-]{2,})\.([a-z]{2,}))$/;
			return regexp.test(elmValue);
		}
		return true;
	}
	
	// Check for date
	function validateDate(element,elmValue){
		if(isRequired(element) || elmValue != '') {
			var dateStr = elmValue;
			var datePat = '';
			switch($.fn.formChecker.defaults.datePattern) {
				case 'DD/MM/YYYY':
					datePat = /^(\d{1,2})(\/)(\d{1,2})(\/)(\d{4})$/;
				break;
				case 'YYYY/MM/DD':
					datePat = /^(\d{4})(\/)(\d{1,2})(\/)(\d{1,2})$/;				
				break;
				case 'DD-MM-YYYY':
					datePat = /^(\d{1,2})(\-)(\d{1,2})(\-)(\d{4})$/;
				break;
				case 'YYYY-MM-DD':
				default:
					datePat = /^(\d{4})(\-)(\d{1,2})(\-)(\d{1,2})$/;
			}
			var matchArray = dateStr.match(datePat);
			if (matchArray === null) {
				return false;
			}
			year = matchArray[1];
			month = matchArray[3];
			day = matchArray[5];
			if (month < 1 || month > 12) {
				return false;
			}
			if (day < 1 || day > 31) {
				return false;
			}
			if ((month === 4 || month === 6 || month === 9 || month === 11) && day === 31) {
				return false;
			}
			if (month === 2) { // check for february 29th
				var isleap = (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0));
				if (day > 29 || (day === 29 && !isleap)) {
					return false;
				}
			}
			return true;
		}
		return true;
	}
	
	// Default values of the formchecker
	$.fn.formChecker.defaults = {
		nOkMessage: 'Please fill in all the required fields.',
		nOkMessageDuration: 'fast',
		datePattern: 'YYYY-MM-DD', // YYYY-MM-DD, YYYY/MM/DD, DD-MM-YYYY, DD/MM/YYYY
		isValid : null,
		setup: null,
		complete: null
	};
})(jQuery);
