if( undefined === SBS.validation) {
	SBS.validation = {};
	SBS.validation.active = true;
	SBS.validation.doingPreCheckField = false;
	SBS.validation.doingPostCheckField = false;
}

SBS.validation.messages = {

	"text-empty":"You must enter a value",
	"text-too_long":"Please enter less text",
	"text-too_short":"Please enter more text",

	"email-empty":"Please enter an email address",
	"email-invalid":"Please enter a valid email address",

	"url-empty":"Please enter a web address",
	"url-invalid":"Please enter a valid web address",

	"equality-not_equal":"These two values do not match",

	"confirm-unconfirmed":"You must agree to the terms and conditions",

	"digits-empty":"You must enter a value",
	"digits-invalid":"This field may only contain digits",
	"digits-too_long":"Please enter fewer digits",
	"digits-too_short":"Please enter more digits",

	"phone_number-invalid":"Please enter a valid phone number (0-9 .+-)",
	"phone_number-too_short":"Please enter a valid phone number",
	"phone_number-too_long":"Please enter a valid phone number",

	"credit_card-invalid":"Please enter a valid card number",

	"cc_date_check-year_in_past":"year in past",
	"cc_date_check-month_in_past":"Expiry month is not valid (for date of payment)"

};

$( document).ready( function() {

	if( !SBS.validation.active){
		return;
	}
	
	//select all fields that have a defined validation:
	
	//text fields:
	$("form input[v_def][type='text']").blur( function() {
		var field = $( this);
		SBS.validation.checkField( field);
	});
	//password fields:
	$("form input[v_def][type='password']").blur( function() {
		var field = $( this);
		SBS.validation.checkField( field);
	});	
	
	
	//select fields:
	$("form select[v_def]").change( function() {
		var field = $( this);
		SBS.validation.checkField( field);
	});
	
	//checkbox fields:
	$("form input[v_def][type='checkbox']").change( function() {
		var field = $( this);
		SBS.validation.checkField( field);
	});

});

SBS.validation.submitFormIfValid = function( form) {
	
	if( SBS.validation.checkForm( form) ) {
		//submit form
		form.submit();
	}
};

SBS.validation.checkForm = function( form) {
	
	if( !SBS.validation.active){
		return true;
	}
	
	var isFormValid = true;
	
	form.find("input[v_def], select[v_def]").each( function() {
		var field = $( this);
		var isFieldValid = SBS.validation.checkField( field);
		if( !isFieldValid) {
			isFormValid = false;
		}
	});

	/*
	//hack - form specific stuff:
	var dateIsVaild = SBS.validation.checkExpiryDateIsInFuture();
	if( !dateIsVaild) {
		isFormValid = false;
	}*/
	
	if( !isFormValid) {
		SBS.common.msg( "Please correct the form errors indicated and try again.", "Form Errors");
	}
	
	return isFormValid;
};

SBS.validation.checkFieldById = function( fieldId) {
	return SBS.validation.checkField( $('#' + fieldId) );
};

SBS.validation.checkField = function( field) {
	
	//trim field value:
	field.val( $.trim( field.val() ) );
	
	var vDefString = field.attr('v_def');
	var vDef = eval( vDefString);

	//do pre-check-field: be careful of cyclic dependancies
	if( undefined != vDef.pre_check_field) {
		if( SBS.validation.doingPreCheckField) {
			//do nothing
		} else {
			SBS.validation.doingPreCheckField = true;
			var fieldToPreCheck = $('#' + vDef.pre_check_field);
			var preCheckFieldResult = SBS.validation.checkField( fieldToPreCheck);
			SBS.validation.doingPreCheckField = false;
		}
	}

	var result;
	
	//if mandatory, check we have a value:
	if( true) { //assume all mandatory for the momment.
		if( 0 === field.val().length ) {
			//NOT YET REALLY USED - PROBLEMS CLASHING WITH 'text' field type
			result = {};
			result.isValid = false;
			result.reason = 'empty';
			
		} else {
			
		}
	}
	
	if("text" === vDef.type) {
		result = SBS.validation.textFieldIsValid( field, vDef);
	}
	if("email" === vDef.type) {
		result = SBS.validation.emailFieldIsValid( field, vDef);
	}
	if("equality" === vDef.type) {
		result = SBS.validation.equalityFieldIsValid( field, vDef);
	}
	if("url" === vDef.type) {
		result = SBS.validation.urlFieldIsValid( field, vDef);
	}
	if("digits" === vDef.type) {
		result = SBS.validation.digitsFieldIsValid( field, vDef);
	}
	if("confirm" === vDef.type) {
		//assuming checkbox, and assuming behaviour like 'agree to terms conditions' type thing
		result = SBS.validation.confirmFieldIsValid( field, vDef);
	}
	if("credit_card" === vDef.type) {
		//assuming checkbox, and assuming behaviour like 'agree to terms conditions' type thing
		result = SBS.validation.creditCardFieldIsValid( field, vDef);
	}
	if("phone_number" === vDef.type) {
		result = SBS.validation.phoneNumberFieldIsValid( field, vDef);
	}
	

	if( undefined === result) {
		//hmmm, probably a typo, we dont know how to handle that type of field
	}
	
	
	//run any app specific 'auxilliary' validation checks - for example specific date/credit card checks.
	if( result.isValid) {
		//if invalid, don't worry about auxilliary check until basic validation passes.
		if( undefined != vDef.aux) {
			var auxFunction = eval( vDef.aux);
			if( $.isFunction( auxFunction) ) {
				var auxResult = auxFunction();
				//console.log( auxResult.isValid);
				//console.log( auxResult.reason);
				if( !auxResult.isValid) {
					vDef.type = vDef.aux_type;	//so we can find the appropriate error message.
					result = auxResult;	//overwrite result with 'auxResult'.
				} else {
					//clear aux errors:
					//SBS.validation.removeErrorMsg( field);
				}
			}
		}
	}
	
	
	
	if( result.isValid) {
		//great - clear any errors and continue:
		SBS.validation.removeErrorMsg( field);
	} else {
		var messageText = SBS.validation.getErrorMsgText( vDef, result);
		SBS.validation.createErrorMsg( field, result, messageText);
	}	

	//do post-check-field: be careful of cyclic dependancies
	if( undefined != vDef.post_check_field) {
		if( SBS.validation.doingPostCheckField) {
			//do nothing
		} else {
			SBS.validation.doingPostCheckField = true;
			var fieldToPostCheck = $('#' + vDef.post_check_field);
			var postCheckFieldResult = SBS.validation.checkField( fieldToPostCheck);
			SBS.validation.doingPostCheckField = false;
		}	
	}
	
	return result.isValid;
};

SBS.validation.getErrorMsgText = function( vDef, result) {
	var messageArrayKey = vDef.type + "-" + result.reason;
	return SBS.validation.messages[ messageArrayKey];
};

SBS.validation.createErrorMsg = function( field, result, errorMsgText) {
	
	var fieldErrMessageId = field.attr("id") + "_err-msg";
	
	if( $("#" + fieldErrMessageId).size() > 0 ) {
		//already an error message - delete it and generate a new one with latest text
		$("#" + fieldErrMessageId).remove();
	}
	
	//we dont have an error message, lets create one.
//	var errMsg = $('<span id="' +  fieldErrMessageId + '" class="validation-err-msg">' + errorMsgText + '</span>').insertAfter( field);
	var errMsg = $('<span id="' +  fieldErrMessageId + '" class="validation-err-msg">' + errorMsgText + '</span>');
//	field.closest('div.form-row').append( errMsg);
	field.parent().append( errMsg);
	errMsg.fadeIn("slow");
};

SBS.validation.removeErrorMsg = function( field) {
	var fieldErrMessageId = field.attr("id") + "_err-msg";
	$("#" + fieldErrMessageId).fadeOut("slow", function() { $( this).remove() });
};


//========================== SPECIFIC FIELD TYPE FUNCTIONS ==========================

SBS.validation.textFieldIsValid = function( field, vDef) {
	
	var result = {};
	result.isValid = true;
	
	if( 0 === field.val().length) {
//	if( vDef.minLength > 0 && 0 === field.val().length) {
		result.isValid = false;
		result.reason = 'empty';
		return result;
	}
	
	if( undefined != vDef.minLength) {
		if( field.val().length < vDef.minLength ) {
			result.isValid = false;
			result.reason = 'too_short';
			return result;
		}
	}

	if( undefined != vDef.maxLength) {
		if( field.val().length > vDef.maxLength ) {
			result.isValid = false;
			result.reason = 'too_long';
			return result;
		}	
	}
	
	return result;
};

SBS.validation.equalityFieldIsValid = function( field, vDef) {
	
	var result = {};	
	
	var equalityFieldVal = $.trim( $("#" + vDef.equalTo).val() );
	if( equalityFieldVal === field.val() ) {
		result.isValid = true;
		return result;	
	}

	result.reason = 'not_equal';
	result.isValid = false;
	
	return result;
};

SBS.validation.digitsFieldIsValid = function( field, vDef) {
	
	var result = {};	
	
	if( /\D/.test( field.val() ) == false) {
		//do basic text check first.
		var textResult = SBS.validation.textFieldIsValid( field, vDef);
		if( !textResult.isValid) {
			return textResult;
		}
		result.isValid = true;
	} else {
		result.isValid = false;
		result.reason = 'invalid';
	}
	return result;	

};

SBS.validation.emailFieldIsValid = function( field, vDef) {

	var result = {};

	if( 0 === field.val().length ) {
		result.reason = 'empty';
		result.isValid = false;
		return result;
	}
	
	var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
	if(reg.test( field.val() ) == false) {
		result.reason = 'invalid';
		result.isValid = false;
		return result;
	}
	
	result.isValid = true;	
	return result;
};

SBS.validation.confirmFieldIsValid = function( field, vDef) {
	
	var result = {};
	
	if( field.is(':checked') ) {
		result.isValid = true;	
	} else {
		result.isValid = false;
		result.reason = 'unconfirmed';
	}
	
	return result;	
};

SBS.validation.urlFieldIsValid = function( field, vDef) {
	
	var result = {};
	
	if( 0 === field.val().length ) {
		result.reason = 'empty';
		result.isValid = false;
		return result;
	}	
	
	//good  but should compile these when the page loads....
	var re = new RegExp();
	re.compile( "^(http://)?[A-Za-z0-9-_]+\\.[A-Za-z0-9-_%&\?\/.=]+$", "i"); //optional protocol, explicit 'http'
	if ( !re.test( field.val() ) ) {
		result.reason = 'invalid';
		result.isValid = false;
		return result;
	}
	
	result.isValid = true;	
	return result;	
};

SBS.validation.creditCardFieldIsValid = function( field, vDef) {

	var result = {};
	result.reason = 'invalid';
	
	var cardNumber = field.val();
	var cardType = $('#' + vDef.card_type_field_id).val();

	if("" == cardType) {
		SBS.validation.checkFieldById( vDef.card_type_field_id);
		result.isValid = true;
		return result;	//return - no card type selected, force user to select card before validating card number.
	}
	
	var isValid = false;
	var ccCheckRegExp = /[^\d ]/;
	isValid = !ccCheckRegExp.test(cardNumber);

	if( isValid)
	{
		var cardNumbersOnly = cardNumber.replace(/ /g,"");
		var cardNumberLength = cardNumbersOnly.length;
		var lengthIsValid = false;
		var prefixIsValid = false;
		var prefixRegExp;

		switch(cardType)
		{
			case "MASTER_CARD":
				lengthIsValid = (cardNumberLength == 16);
				prefixRegExp = /^5[1-5]/;
				break;

			case "VISA":
				lengthIsValid = (cardNumberLength == 16 || cardNumberLength == 13);
				prefixRegExp = /^4/;
				break;

			case "AMERICAN_EXPRESS":
				lengthIsValid = (cardNumberLength == 15);
				prefixRegExp = /^3(4|7)/;
				break;

			default:
				prefixRegExp = /^$/;
				//should not get here if we use the three card above.
				console.log("Card type not found");
		}

		prefixIsValid = prefixRegExp.test(cardNumbersOnly);
		isValid = prefixIsValid && lengthIsValid;
	}

	if (isValid)
	{
		var numberProduct;
		var numberProductDigitIndex;
		var checkSumTotal = 0;

		for( digitCounter = cardNumberLength - 1; digitCounter >= 0; digitCounter--)
		{
			checkSumTotal += parseInt (cardNumbersOnly.charAt(digitCounter));
			digitCounter--;
			numberProduct = String((cardNumbersOnly.charAt(digitCounter) * 2));
			for (var productDigitCounter = 0; productDigitCounter < numberProduct.length; productDigitCounter++)
			{
				checkSumTotal += parseInt(numberProduct.charAt(productDigitCounter) );
			}
		}

		isValid = (checkSumTotal % 10 == 0);
	}

	result.isValid = isValid;
	return result;
};

//between 9 - 25 chars
SBS.validation.phoneNumberFieldIsValid = function( field, vDef) {
	var result = {};
	result.reason = 'invalid';

	var phoneNumber = field.val();
	
	if( phoneNumber.length < 9) {
		result.isValid = false;
		result.reason = 'too_short';
		return result;
	}
	if( phoneNumber.length > 25) {	//dunno why I choose 22!
		result.isValid = false;
		result.reason = 'too_long';
		return result;
	}
	
	//check for valid phone number characters (0-9 + - . ' ')
	var phoneNumberCharRegex = /[^\d\+\-\. ]/g;				//should compile these
	var matchResult = phoneNumber.match( phoneNumberCharRegex);
	
	if( null == matchResult) {
		result.isValid = true;
	} else {
		result.isValid = false;
		result.reason = 'invalid';
	}
	
	return result;
};


//---- APP SPECIFIC FUNCTIONS ------


//we check month and year, but probably only need to check month as previous years should not appear in the drop down.
SBS.validation.checkExpiryDateIsInFuture = function() {
	
	var result = {};
	result.isValid = true;	
	
	
	var selectedExpiryMonth = parseInt( $("#paymentExpiryMonth").val(), 10) - 1;	//JS date months are 0 based.
	var selectedExpiryYear = parseInt( $("#paymentExpiryYear").val(), 10);
	
	//create a date based on current time, then set it to the last day of the month
	var dateEndOfMonth = new Date();
	dateEndOfMonth.setDate( dateEndOfMonth.getDate() + 15);		//add 15 days - verify date against when we will actually take payment
	dateEndOfMonth.setMonth( dateEndOfMonth.getMonth() + 1);
	dateEndOfMonth.setDate(0);
	
	if( selectedExpiryYear < dateEndOfMonth.getFullYear() ) {
		result.isValid = false;
		result.reason = 'year_in_past';
	} else {
		
		if( selectedExpiryYear === dateEndOfMonth.getFullYear() ) {
			
			if( selectedExpiryMonth < dateEndOfMonth.getMonth() ) {
				result.isValid = false;
				result.reason = 'month_in_past';
			}
		}
		
	}
	
	return result;
};