/**
 * @author Sajjan Sarkar
 */
(function($)
{	$.fn.timefield = function(options)
	{		//Public Methods
		var methods = {		     /** Initializes the timefield  */
			init : function(options)
			{				return this.each(function()
				{					/** Txt entry is from RIGHT TO LEFT */
					this.style.direction = "rtl";
					/** Cache for performance*/
					var $this = $(this);	
					/** bind listeners */
					$this.bind('keydown.timefield', helpers.eventHandlers.keyDownHandler);					$this.bind('keyup.timefield', helpers.eventHandlers.keyUpHandler);					$this.bind('blur.timefield', helpers.eventHandlers.blurHandler);					
				});
			},
			/** removes all timefield effects**/
			turnOffTimeField : function(content)
			{				return this.each(function()
				{					this.style.direction = "ltr";
					var $this = $(this);
					$this.unbind('.timefield');				});
			}
			
		};
		/** Private methods */
		var helpers = {			eventHandlers : {				/** On key down, check if the pressed key is valid else dont show it.
				 * 	acts as mask
				 * */
				keyDownHandler : function(event)
				{					
					if (!helpers.utils.isValidKeyStroke(event))
					{						event.preventDefault();
						event.stopPropagation();
					}
				},
				/**
				 * When this is called the keydown event would have already rendered the 
				 * key stroke, so apply the padding with zeros.
				 * */
				keyUpHandler : function(event)
				{					var keyCode = event.keyCode;
					/** check if a special key was pressed */
					if ($.inArray(keyCode, constants.KEYS.SPECIAL_KEYS_TO_IGNORE) != -1)
						return;
					/** did this on key up instead of keydown as its easier coz the text has rendered */
					helpers.utils.applyPadding(helpers.utils.getEventSource(event));
				},
				/**
				 * On blur, check the validity of the enterred value based on selected settings.
				 * Could not do this on change as most browsers dont fire the change event 
				 * if the value has changed programatically.
				 * 
				 * If the time is invalid, call the specified(or default) onError handler
				 * */
				blurHandler : function(event)
				{					helpers.utils.getEventSource(event).value = jQuery.trim( helpers.utils.getEventSource(event).value );
					if (!helpers.utils.isValidTime(helpers.utils.getEventSource(event).value))
					{						settings.onError.call(helpers.utils.getEventSource(event));
					}
				}
			},
			utils : {				/**
				 * This function returns true if the enterred key is a number, a colon or one of the allowed special keys.
				 * */
				isValidKeyStroke : function(event)
				{					var keyCode = event.keyCode;
					/** check if a special key was pressed */
					if ($.inArray(keyCode, constants.KEYS.SPECIAL_KEYS_TO_IGNORE) != -1)
						return true;
					/** Only allow numbers and the colon key */
					if ((!event.shiftKey && (constants.KEYS.CHARACTER_ZERO <= keyCode 
							&& keyCode <= constants.KEYS.CHARACTER_NINE || constants.KEYS.KEYPAD_ZERO <= keyCode
							&& keyCode <= constants.KEYS.KEYPAD_NINE))
							|| (event.shiftKey && keyCode == constants.KEYS.COLON))
					{						return true;
					}
					return false;
				},
				/**
				 * This function pads the values with zeroes and colons.
				 * */
				applyPadding : function(textBox)
				{					var val = textBox.value;
					if (val != "")
						val = parseInt(textBox.value.replace(":", ""), 10) + "";					if (val.length > constants.NO_OF_CHARACTERS_WITHOUT_COLON)
					{						val = val.substring(val.length - constants.NO_OF_CHARACTERS_WITHOUT_COLON);
					}
					switch (val.length)
					{						case 0:
							val = val;
						break;
						case 1:
							val = "00:0" + val;
						break;
						case 2:
							val = "00:" + val;
						break;
						case 3:
							val = "0" + val.substring(0, 1) + ":" + val.substring(1);
						break;
						case 4:
							val = val.substring(0, 2) + ":" + val.substring(2);
						break;
					}
					
					//$(textbox).blur();
					//$(textbox).focus();
					/**
					 * mobile browsers seem to not place the caret in the correct place
					 * after the padding of zeroes, and IE8 doesnt support
					 * setselection range but works fine without it.
					 * 
					 * The basic idea is to add an extra space after the  last digit
					 * and "select" it,ensuring that the caret is in the correct position
					 * 
					 * */
					if(textBox.setSelectionRange)
					{						textBox.value = val+" ";
						textBox.setSelectionRange(5, 6);
					}
					else
					{						textBox.value = val;
					}
						
					return;
				},
				/**
				 * This function returns true if the enterred time is 
				 * a valid time based on the specified(or default) settings,
				 * else returns false.
				 * */
				isValidTime : function(strTime)
				{					if (strTime == "")
						return true;
					var regex = constants.REGEX.TWENTY_FOUR;
					switch (settings.hourFormat)
					{						case constants.HOUR_FORMATS.TWELVE:
							regex = constants.REGEX.TWELVE;
						break;
						case constants.HOUR_FORMATS.TWENTY_FOUR:
							regex = constants.REGEX.TWENTY_FOUR;
						break;
					}
					return regex.test(strTime);
				},
				getEventSource:function(event)
				{					return event.target || event.srcElement;
				}
			}
		};
		/**Constants*/
		var constants = {			/** Only 2 formats supported now.*/
			HOUR_FORMATS : {				TWELVE : "12",
				TWENTY_FOUR : "24"
			},
			KEYS : {				CHARACTER_ZERO : 48,
				CHARACTER_NINE : 57,
				KEYPAD_ZERO : 96,
				KEYPAD_NINE : 105,
				BACKSPACE : 8,
				TAB : 9,
				ENTER : 13,
				COLON : 186,
				SPECIAL_KEYS_TO_IGNORE : 	[ 
				                         	  	8, /**BACKSPACE*/
												9, /**DELETE*/
												46, /**TAB*/
												40, /**DOWN ARROW*/
												39, /**LEFT ARROW*/
												38, /**UP ARROW*/
												37 ,/**LEFT ARROW*/
												16 /** SHIFT*/
											]
			},
			NO_OF_CHARACTERS_WITHOUT_COLON : 4,
			REGEX : {				TWELVE : /^([0-1]?[0-2])([:][0-5]?[0-9])?$/,
				TWENTY_FOUR : /^([2][0-3]|[0-1]?[0-9])([:][0-5]?[0-9])?$/
			}
		};
		// Create some defaults, extending them with any options that were provided
		var settings = $.extend( {			'hourFormat' : '24',
			'onError' : function()
			{				this.value="";
			}
		}, options);
		// Method calling logic
		if (methods[options])
		{			return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof options === 'object' || !options)
		{			return methods.init.apply(this, arguments);
		} else
		{			$.error('Method ' + options + ' does not exist on jQuery.timefield');		}
	};
})(jQuery);