// STATIC =========================================================================================
PastelValidator.options = {
	highlightClass: ""
};

PastelValidator.removeAllMessages = function() {
	$("ins[class*='id_validate_']").remove();
};

/**
 * Configuration example: 
 * 	{{validatorName : {value : validatorValue,
 *	  message: "Message to override the default ot '' for and empty message",
 *	  popup: true, // set to false to disable validation popups (default is true);
 *	  higlight: false // set to false to disable validation highlights (default is true); 
 *	 }};
 * 
 * @constructor
 */
function PastelValidator() {
	/**
	 * Contains the validators.
	 * 
	 * 
	 * Validator example
	 * <code>
	 * validatorName : {
	 * 	   check: function(optionValue, value) {
	 * 		   // optionValue is the value of the validator, value is the value that need validation
	 * 		   var isValid = true;
	 * 		   // return false if you want to display the validation message
	 * 	       return isValid;
	 * 	   }
	 * 	   message: "The message to display if invalid!"
	 * } 
	 * </code>
	 */
	var descriptors = {
						// !!! always write this validator on top of the validation queue
						required : {
					 		check : function(optionValue, value) {
								return optionValue && value == "";
							},
							message : translate('Field_required')
					  	},
						min : {
					  		check : function(optionValue, value) {
					  			return value != "" && optionValue > value;
					  		},
					  		message : translate('Min_value_is')
						},
					  	max : {
					  		check : function(optionValue, value) {
					  			return value != "" && optionValue < value;
					  		},
					  		message : translate('Max_value_is')
						},
						range : {
							check : function(optionValue, value) {
								return value != "" && (value < optionValue[0] || value > optionValue[1]);
					  		},
					  		message : translate('Min_Max_value_is')
				  		},	  		
				  		exactlen : {
				  			check : function(optionValue, value) {
					  			return value != "" && value.length != optionValue;
					  		},
					  		message : translate('Length_must_be_N_symbols')
				  		},				  		
				  		minlen : {
				  			check : function(optionValue, value) {
				  				return value != "" && value.length < optionValue;
					  		},
					  		message : translate('Min_length_is')
				  		},
				  		maxlen : {
							check : function(optionValue, value) {
								return value != "" && value.length > optionValue;
					  		},
					  		message : translate('Max_length_is')
				  		},
						rangelen : {
							check : function(optionValue, value) {
								return value != "" && (value.length < optionValue[0] || value.length > optionValue[1]);
					  		},
					  		message : translate('Length_between')
				  		},				  		
						digit : {
							check : function(optionValue, value) {
			  					// http://docs.jquery.com/Plugins/Validation/Methods/digits
			  					var isDigit = /^\d+$/.test(value);
								return value != "" && (optionValue && !isDigit);
					  		},
					  		message : translate('Value_must_be_integer')
				  		},
				  		number : {
							check : function(optionValue, value) {
			  					// http://docs.jquery.com/Plugins/Validation/Methods/number
			  					var isNumber = /^-?(?:\d+(?:\.\d+)?)$/.test(value);
								return value != "" && (optionValue && !isNumber);
					  		},
					  		message : translate('Value_must_be_number')
				  		},
				  		// check if a given value is a valid e-mail
				  		email : {
							check : function(optionValue, value) {
			  					// http://docs.jquery.com/Plugins/Validation/Methods/email
			  					var isEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
								return value != "" && (optionValue && !isEmail);
					  		},
					  		message : translate('Valid_email_required')
				  		},
				  		emailList: {
				  			check : function(optionValue, value) {
				  				if($.trim(value) == "") {
				  					return false;
				  				}
					  			var values = value.split(optionValue);
					  			var hasOne = false;
					  			for(var i = 0; i < values.length; i++) {
					  				value = $.trim(values[i]);
					  				if( value == "") {
					  					continue;
					  				}
					  				
					  				var isEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
					  				if(!isEmail) {
					  					return true;
					  				}
					  				hasOne = isEmail;
					  			}
					  			return !hasOne;
					  		},
					  		message : translate('Valid_email_list_with_delimiter')
				  		},
				  		// match the given value againts regular expression
				  		regex : {
			  				check : function(optionValue, value) {
			  					return value != "" && !optionValue.test(value);
			  				},
			  				message : translate('Invalid_value')	
						},
				  		// check if a given value is equal to another
				  		equalTo : {
			  				check : function(optionValue, value) {
			  					return ($.isFunction(optionValue) && value != optionValue());
			  				},
			  				message : translate('Invalid_value')	
						}				  		
					 };
	
	/**
	 * Contains the elements that will be validated.
	 */
	var elements = [];
	/**
	 * The container of the validation messages.
	 */
	var $container = null;
	var messageId = "messageId";
	
	/**
	 * This method should be called immediately after instantiation to set the messages container.
	 * @param {String} selector
	 */
	this.setContainerSelector = function(selector) {
		$container = $(selector);
	};
	
	this.setMessageId = function(id) {
		messageId = id;
	};
 
	/**
	 * Validates the elements. 
	 * Displays messages and/or highlights the element
	 * @param {jQuery} $element
	 * @return true if the element is valid and false otherwise
	 */
	var validateElement = function($element) {
		
		var validators = $element.data("validators");
		if (validators == undefined || 
			validators.length == 0) {
			return true;
		}
		
		var value = $element.val();
		value = $.trim(value);
 
		for(var i = 0; i<validators.length; i++) {
			var validator = validators[i];
			
			var descriptor = validator.descriptor;
			var option = validator.option;
			
			// set default message if there is none defined
			if (option.message == undefined) {
				option.message = descriptor.message; 
			}
			
			var check = descriptor.check;
			
			var popup = option.popup == undefined || option.popup;
			var highlight = option.highlight == undefined || option.highlight;
			var message = option.message;
			var optionValue = option.value;
			
			// remove any previous messages
			if (popup) {
				removeMessages();
			}
			if (highlight) {
				removeHighlight($element);
			}
			
			
			var elementDefaultValue = $element.data("defaultValue");
			if (elementDefaultValue != undefined && 
				elementDefaultValue == value) {
				value = "";
			}
			
			if (check(optionValue, value)) {
				// substitute message params
				if (optionValue.constructor != Array) {
					if (message.indexOf("{0}") != -1) {
						message = message.replace("{0}", optionValue);							
					}
				} else {
					for(var j = 0; j < optionValue.length; j++) {
						var param = "{"+j+"}";
						var idx = 0;
						if ((idx = message.indexOf(param, idx)) != -1) {
							message = message.replace(param, optionValue[j]);
						}
					}
				}
				
				if (popup) {
					addMessage($element, message);
				}
				if (highlight) {
					addHighlight($element);
				}
				// if the field is not required do not consider it invalid if empty
				return false;
			} // end for check
			
		} // end for
		return true;
	};
	
	var stopTimer = false;
	/**
	 * Adds an element to the validation array.
	 * @param {jQuery} $element - the element
	 * @param {Object} options - validator configuration options
	 */
	this.add = function($element, options, group) {
		if (group === undefined) {
			group = "default";
		}
		
		
		// create validator-options pairs
		var validators = [];
		for(var option in options) {
			if (descriptors[option] != undefined) {
				var descriptor = descriptors[option];
				var option = options[option];
				validators.push({descriptor: descriptor,
								 option: option});
			}
		}
		// and assign them to the element
		$element.data("validators", validators);
		$element.data("isFirstFocusIn", true);
		$element.data("hasHighlight", false); 
		$element.data("validationGroup", group); 
		elements.push($element);
		
		var isTimerRunning = false;
		var delayMS = 1000;
		$element.keyup(function(event){
			if(isTimerRunning === false) {
				setTimeout(function() {
								if (stopTimer) {
									isTimerRunning = false;
									return;
								}
								validateElement($element);							
								isTimerRunning = false;
							}, delayMS);
				isTimerRunning = true;
			}
		});		
		$element.focusin(function(event){
			// do not show validation on the first user click!
			var firstFocusIn = $element.data("isFirstFocusIn");
			if(firstFocusIn) {
				$element.data("isFirstFocusIn", false);
			} else {
				if(isTimerRunning === false) {
					setTimeout(function() {
									if (stopTimer) {
										isTimerRunning = false;
										return;
									}
									validateElement($element);							
									isTimerRunning = false;
								}, delayMS);
					isTimerRunning = true;
				}
			}
		});
		$element.focusout(function(event){
			removeMessages();
		});
	};

	/**
	 * Removes an element from the validation array.
	 * @param {jQuery} $element - the element
	 */
	this.remove = function($element) {
		if (!($element instanceof jQuery)) {
			throw ("$element must be instance of jQuery!");
		}

		var i = 0;
		while (i < elements.length) {
			var $elem = elements[i];
			if ($elem.get(0) == $element.get(0)) {
				$elem.removeData("validators");
				$elem.removeData("isFirstFocusIn");
				$elem.removeData("hasHighlight"); 
				$elem.removeData("validationGroup"); 

				elements.splice(i, 1);
			} else {
				i++;
			}
		}
	};
 
	/**
	 * Call this method to trigger all validators on all elements.
	 * It either stops on the first invalid element or runs trought all if they are all valid.
	 * @return true if all validatiors are valid and false if only a single one is invalid.
	 */
	this.validate = function(group) {
		stopTimer = false;
		var isValid = true;
		for(var i = 0; isValid && i < elements.length; i++) {
			var $element = elements[i];
			var validationGroup = $element.data("validationGroup");
			if (group === undefined || 
				(typeof group == "string" && validationGroup == group) ||
				($.isArray(group) && $.inArray(validationGroup, group) != -1)) {
					isValid = validateElement($element);
			}
		}
		return isValid;
	};
 
	/**
	 * Reset the validator messages and highlights.
	 */
	this.reset = function() {
		stopTimer = true;
		for(var i = 0; i < elements.length; i++) {
			var $element = elements[i];
			$element.data("isFirstFocusIn", true);
			removeHighlight($element);
			removeMessages();
		}
	};
	
	var addHighlight = function($element) {
		var hasHighlight = $element.data("hasHighlight");
		if (!hasHighlight) {
			$element.addClass(PastelValidator.options.highlightClass);
			$element.data("hasHighlight", true);
		}
	};
	
	var removeHighlight = function($element) {
		var hasHighlight = $element.data("hasHighlight");
		if (hasHighlight) {
			$element.removeClass(PastelValidator.options.highlightClass);
			$element.data("hasHighlight", false);			
		}
	};
	
	var addMessage = function($element, message){
		var elemLeft = $element.offset().left;
		var elemTop = $element.offset().top;
		var elemOuterHight = $element.outerHeight();
	
		// container top left - should be subtracted from the item's
		var contLeft = $container.offset().left;
		var contTop = $container.offset().top;
		var contWidth = $container.width();
		
		var $popup = $("<ins class='w_hunt id_validate_" + messageId + "'>" +
					   		"<div class='w_h_expand'>" +
					   			"<div class='w_h_text'>" + message +
									"<div class='w_h_t_l'></div>" +
									"<div class='w_h_t_r'></div>" +
									"<div class='w_h_b_l'></div>" +
									"<div class='w_h_b_r'></div>" +
								"</div>" +				
							"</div>" +	
						"</ins>");
		
		var popupTop = elemTop - contTop + elemOuterHight;
		$popup.css("top", popupTop);
		$popup.css("left", contLeft);
		$popup.css("position", "absolute");
		$popup.css("z-index", "1102");
		$popup.css("display", "none");
		$container.append($popup);
		
		var popupOuterWidth = $popup.outerWidth();
		var popupLeft = 0;

		if(elemLeft - contLeft + popupOuterWidth <= contWidth) {
			popupLeft = elemLeft - contLeft;
		} else {
			var elemOuterWidth = $element.outerWidth();
			$popup.addClass("w_hunt_r");
			popupOuterWidth = $popup.outerWidth();
			popupLeft = elemLeft - contLeft + elemOuterWidth - popupOuterWidth;
		}
		$popup.css("left", popupLeft);
		$popup.css("display", "block");
		
		$element.data("hasMessage", true); 		
	};
	
	var removeMessages = function() {
		$("ins.id_validate_"+messageId, $container).remove();
		for(var i = 0; i < elements.length; i++) {
			var $element = elements[i];
			$element.data("hasMessage", false); 
		}
	};
}
