/**
 * jQuery Plugin Find As You Type
 *
 * Abstract goes here
 *
 * @version
 * 
 * @author		Tomi Kulmala  [tomi dot kulmala at creanor dot com]
 * @contributor Erkki-Ilmari Rajakoski [erkki dot rajakoski at creanor dot com]
 * @copyright	Creanor (c) 2008. All rights reserved.
 * @license     http://www.opensource.org/licenses/gpl-license.php
 *
 * @package     jQuery Plugins
 * @subpackage  FAYT
 *
 * @todo        Everything
 */
 
(function($) {
	
	/**
	 * Create new Find As You Type objects
	 * @access      Public
	 * @param       Object options
	 */	
	$.fn.fayt = function(options) {
		return this.each(function() {
			new $.fayt(this, options);
		});
	};
	
	/**
	 * FAYT Prototype Class
	 * @access      Public
	 * @param       Element
	 * @param       Object options
	 */
	$.fayt = function(element, options) {
		//store options etc
		var self = this;
		
		this.element = $(element);
						
		this.options = $.extend({}, {
			//obligatory parameters
			resultsTarget		: $('div#faytWrapper'),
			getResults			: function(needle) {}, //function to get array of results, this should be implemented outside this plugin
			selectItem			: function(value) {}, //function to do something with selected item, this should be implemented outside this plugin
			ajaxEnabled			: false,
			ajaxLocation		: '',

			//optional parameters
			triggerLength		: 3,			
			domInsertType		: 'append', //append, prepend, after, before, replace
			classPrefix			: 'fayt_',
			resultsParentNode	: 'ul',
			resultsChildNode	: 'li'
		},options);
		
		$(this.options.resultsTarget).hide();
		
		//bind needed events
		this.element.bind('blur', function() { 
			self.clearTimer(); 
            self.createTimer('blur'); 
		});
		this.element.bind('focus', function() {
			self.createTimer();
		});
		
		this.element.bind('keypress', function(e) {
			switch( e.keyCode ) {
				case 40: //down
					self.selectNextResult();
					break;
				case 38: //up
					self.selectPreviousResult();
					break;
				case 13: //return
					if (self.resultCurrent !== null) {
                    	e.preventDefault();
					}
					self.selectCurrentResult();
					break;
				case 27: //esc
				    self.clearResults();
				    break;
			}
		});
	};
	
	/**
	 * Protected members
	 */
	$.extend($.fayt.prototype, {
	
		timer : null,
		hideTimer : false,
		lastSentValue : null,
		resultCount : 0,
		resultCurrent : null,
		
		/**
		 * Checks typing for valid input
		 * @access		Private
		 */
		checkTyping : function() {
			var inputValue = this.element.val();
			//check valid input 
			if( inputValue.length >= this.options.triggerLength && inputValue != this.lastSentValue ) {
				//send request if no other allready sent
				this.submitRequest(inputValue);
			} else if( this.resultCount && inputValue.length === 0 ) {
				this.clearResults();
			}
		},
		
		/**
		 * Clear the results container and needed variables.
		 * @access		Private
		 */
		clearResults : function() {
			this.clearTimer('blur');
			this.resultCount = 0;
			this.resultCurrent = null;
			if ($("." + this.options.classPrefix + 'results').length) {
				$("." + this.options.classPrefix + 'results').remove();
			}
			this.options.resultsTarget.hide();
			if (this.element.val() === '') {
				this.lastSentValue = null;
			}	
		},
		
		/**
		 * Clear the current timer
		 * @access		Private
		 * @param       String event name: focus/blur.
		 */
		clearTimer : function(event) {
			if (!event) {
				event = 'focus';
			}
			if( event == 'focus' ) {
                if( this.timer ) {
                    window.clearInterval(this.timer);
                }
			} else {
                if( this.hideTimer ) {
                    window.clearInterval(this.hideTimer);
                }
			}
		},
		
		/**
		 * Create new timer for checking the input fields value.
		 * @access		Private
		 * @param       String event name: focus/blur.
		 */
		createTimer : function(event) {
			if (!event) {
				event = 'focus';
			}
			
			var this_ = this;
            //clear old timer
            this.clearTimer(event);
			if( event == 'focus' ) {
                this.timer = window.setInterval(function() {
                	this_.checkTyping();
                }, 500);
			} else {
                this.hideTimer = window.setInterval(function() {
                	this_.clearResults();
            	}, 250);
			}
		},
		
		/**
		 * Parse results with given data
		 * @access		Private
		 * @param       JSON Object data
		 */
		parseResults : function(data) {
			var this_ = this;
			
            //cear results
            this.clearResults();
			if( data && data.length ) {
				//loop results
				var content = '<' + this.options.resultsParentNode + ' class="' + this.options.classPrefix + 'results">';
				
				for (var i = 0; i < data.length; i++) {
					content += '<' + this.options.resultsChildNode + ' class="' + this.options.classPrefix + 'result">';
					content += data[i];
					content += '</' + this.options.resultsChildNode + '>';
				}
				
				content += '</' + this.options.resultsParentNode + '>';
				
				this.options.resultsTarget.show();
				switch(this.options.domInsertType) {
					case "append":
						this.options.resultsTarget.append(content);
						break;
					case "prepend":
						this.options.resultsTarget.prepend(content);
						break;
					case "after":
						this.options.resultsTarget.after(content);
						break;
					case "before":
						this.options.resultsTarget.before(content);
						break;
					case "replace":
						this.options.resultsTarget.html(content);
						break;					
				}
								
				//loop needed bindings
				$(this.options.resultsChildNode + '.' + this.options.classPrefix + 'result').each(function() {
					$(this).bind('click', function() {
						this_.selectItem($(this).html());
					});
					$(this).bind('mouseover', function() {
						$('.' + this_.options.classPrefix + 'highlight').removeClass(this_.options.classPrefix + 'highlight');
						$(this).addClass(this_.options.classPrefix + 'highlight');
					});
					$(this).bind('mouseout', function() {
						$(this).removeClass(this_.options.classPrefix + 'highlight');
					});
				});
				
				
				this_.resultCount = data.length;
				this_.resultCurrent = null;
			}
		},
		
		/**
		 * Select clicked item
		 * @access		Private
		 * @param       String value
		 */
		selectItem : function(value) {
			//clear input field value
			this.element.val('');
						
			//select item
			this.clearResults();
			
			//return result item
			this.options.selectItem(value);			
		},
		
		/**
		 * Select current result row (with keyboard)
		 * @access      Private
		 */
		selectCurrentResult : function() {
			if( this.resultCount && this.resultCurrent !== null ) {
				var value = $(this.options.resultsChildNode + '.' + this.options.classPrefix + 'result').eq(this.resultCurrent).html();
				this.selectItem(value);
			}
		},
		
		/**
		 * Select next result item
		 */
		selectNextResult : function() {
			if( this.resultCount ) { //results visible
				if ( this.resultCurrent === null ) {
					this.resultCurrent = 0;
				}
				else if( this.resultCurrent < this.resultCount-1 ) {
					//show next
					this.resultCurrent++;
				} else {
					//show first?
					//this.resultCurrent = 0;
				}
				this.updateResultClasses();
			}
		},
		
		/**
		 * Select previous result item
		 */
		selectPreviousResult : function() {
			if( this.resultCount ) { //results visible
				if( this.resultCurrent > 0 ) {
					//show prev
					this.resultCurrent--;
				} else {
					//show last?
					this.resultCurrent = null;
				}
				this.updateResultClasses();
			}
		},
		
		/**
		 * Submit new AJAX request to get content.
		 * @access		Private
		 * @param       String value to be sent as parameter
		 */
		submitRequest : function(value) {
			this.lastSentValue = value;
			var this_ = this;
			if (this.options.ajaxEnabled) {
				$.post(this.options.ajaxLocation, {'q' : value}, function(result){
					this_.parseResults(result);
				}, 'json');	
			} else { 
				this_.parseResults(this.options.getResults(value));
			}
		},
		
		/**
		 * Trigger needed events for results
		 * @access      Private
		 */
		updateResultClasses : function() {
			var this_ = this;
			$(this.options.resultsChildNode + '.' + this.options.classPrefix + 'result').each(function(i,e) {
				if( i == this_.resultCurrent ) {
					$(e).trigger('mouseover');
				} else {
					$(e).trigger('mouseout');
				}
			});
		},
		
		/**
		 * Show hidable elements
		 * @access      Private
		 */
		 showHidableElements : function () {
		 	if(this.options.elementsToHide != false) { 
		 		for(var i = 0; i < this.options.elementsToHide.length; i++) {
		 			if(this.resultCount >= this.options.elementsToHide[i][1]) {
		 				this.options.elementsToHide[i][0].show();
		 			}
		 		}
		 	}
		 }
	});
})(jQuery);