/**
 * @author werner
 * 
 * requires:
 *
 *
 */

jQuery.fn.addSuggest = function(hash) {
	$.addSuggest(this, hash);
	return this;
};

jQuery.addSuggest = function( container, hash )
{
	if ( $(container).length == 0 )
		return;

	var self = $(container).eq(0);
	self._hash					= hash;
	self._fadeout_timer			= null;
	self._wait_img				= null;
	self._cache					= {'':''};
	self._current_search_term	= '';
	self._container				= null;
	
	self._startFadeTimer = function()
	{
		window.clearTimeout(self._fadeout_timer);
		
		self._fadeout_timer = window.setTimeout(function(){
			
			if ( self._container.find('.' + self._hash['activeEntryClass']).length ) {
				self._startFadeTimer();
				return;
			}
			
			self._container.fadeOut('slow');
			
		}, 10000);
	}
	
	self._showSuggestions = function()
	{
		var search_term = self.val().toLowerCase();
		
		var output = self._cache[search_term];
		
		if ( self._cache[search_term]===null
			|| self._cache[search_term]===false
			|| typeof self._cache[search_term]=='undefined' )
			return;
		
		if ( !output ) {
			self._hideSuggestions();
			return;
		}
		
		self._container.html( output );
		
		self._container.find('li:has(a)').each(function() {
			
			$(this).hover(
				function(){
					self._selectEntry($(this));
				},
				function(){
					$(this).removeClass('active');
					self._startFadeTimer();
			});
			
			$(this).click(function(){
				var click = $(this).find('a').prop('onclick');
				if( click &&  click != 'undefined' )
					click();
				else
					location.href = $(this).find('a').attr('href');
			});
		});		
		
		if ( self._wait_img )
			self._wait_img.hide();
		
		if ( !self._hash['container'] )
		{
			self._container.css({left:self.offset().left,
							top:self.offset().top + $(container).outerHeight() + 2});
		}
		
		self._container.show();
		
		self._selectEntryAtIndex(-1);
		self._startFadeTimer();
	}
	
	self._updateSuggestions = function()
	{
		self._startFadeTimer();
		
		var search_term = self.val().toLowerCase();
		
		// Wartet auf AJAX-Antwort
		if ( self._cache[search_term]===false )
			return;
		
		if ( search_term == self._current_search_term )
			return;
		
		self._current_search_term = search_term;
		
		if ( self._cache[search_term]!=null )
		{
			self._showSuggestions();
			return;
		}
		
		if ( self._wait_img ) {
		
			if ( self._hash.loadImageOffset )
				self._wait_img.css({position:"absolute",
					left:self.offset().left + self._hash.loadImageOffset[0],
					top:self.offset().top + self._hash.loadImageOffset[1]
				});
			else
			{
				self._wait_img.css({position:"absolute",
					left:self.offset().left + self.outerWidth() - 16,
					top:self.offset().top + (self.outerHeight() >> 1) - 5
				});
			}
			
			self._wait_img.show();
		}
		
		self._cache[search_term] = false;
		
		// Normal AJAX request
		if ( self._hash['ajaxMethod'] ) {

			var ajaxParams = [search_term];
			for ( key in self._hash['additionalAjaxVars'] )
				ajaxParams.push( self._hash['additionalAjaxVars'][key] );

			ajax.call(self._hash['ajaxMethod'], ajaxParams, function(result) {
				self._cache[ajax.response.call.params[0]] = result;
				self._showSuggestions();
			});
		}

		// MVC request
		else  {

			var ajaxParams = self._hash['additionalAjaxVars'];
			ajaxParams[self._hash['ajaxMVCVar']] = search_term;
			
			MVC.get(self._hash['ajaxMVCMethod'], ajaxParams, function(result) {
				self._cache[ajax.response.call.params[0][self._hash['ajaxMVCVar']]] = result;
				self._showSuggestions();
			});
		}
	}
	
	self._hideSuggestions = function()
	{
		if ( self._wait_img )
			self._wait_img.hide();
		
		self._container.hide();
		self._selectEntryAtIndex(-1);

	}
	
	self._selectEntryAtIndex	= function(index)
	{
		if ( index == -1 && self._hash['autoMarkFirst'] )
			index = 0;
		
		self._selectEntry( index == -1 ? 'null' : self._container.find('li:has(a)').eq(index) );
	}
	
	self._selectEntry = function( entry )
	{
		self._container.find('li:has(a)').removeClass('active');

		if ( entry && entry.addClass ) {
			entry.addClass('active');
			self._startFadeTimer();
		}
	}
	
	_cancelEvent = function( event )
	{
		event.cancelBubble = true;
		if (event.stopPropagation)
			event.stopPropagation();
	}

	// validate parameter hash
	var params = {
		ajaxMethod:			{type:'string',	def:''},
		additionalAjaxVars:	{type:'object',	def:{}},
		
		ajaxMVCMethod:		{type:'string',	def:''},
		ajaxMVCVar:			{type:'string', def:'search'},
		
		inputFieldDefault:	{type:'string', def:'Suchen'},
		autoMarkFirst:		{type:'boolean',def:false},
		loadImageSrc:		{type:'string',	def:'/gfx/search_field_wait.gif'},
		loadImageOffset:	{type:'object',	def:null},
		container:			{type:'object',	def:null},
		activeEntryClass:	{type:'string',	def:'active'}
	}

	if ( typeof self._hash['ajaxMethod'] == 'undefined' && typeof self._hash['ajaxMVCMethod'] == 'undefined' )
			throw("Required parameter 'ajaxMethod' or 'ajaxMVCMethod' is missing");
	
	for ( param in params )
	{
		var details = params[param];

		if ( typeof details['req'] != 'undefined' && details['req'] && typeof self._hash[param] == 'undefined' )
			throw("Required parameter '"+param+"' is missing");

		if ( typeof details['def'] != 'undefined' && typeof self._hash[param] == 'undefined' )
			self._hash[param] = details['def'];

		if ( typeof details['type'] != 'undefined' && typeof self._hash[param] != details['type'] )
			throw("Parameter '"+param+"' must be of type '"+details['type']+"', but is '" + typeof self._hash[param] + "'");
	}
	
	// init suggestions container
	
	if ( self._hash['container'] ) {
		self._container = self._hash['container'];
	}
	else {
		self._container = $('<div style="position:absolute"></div>');
		self._container.css({background:"#f8f8f8",
						textAlign:"left",
						boxShadow:"0 3px 5px rgba(0,0,0,.2)",
						padding:0,
						zIndex:200,
						borderColor:"#eee",
						borderStyle:"solid",
						borderWidth:1
					   });
		$('body').append(self._container);
	}
	self._container.hide();

	// init input field
	
	var enter_field = function(){
		$(self).css('color', 'black');
		if ($(self).val()==self._hash['inputFieldDefault'])
			$(self).val('');
		else {
			self._showSuggestions();
		}
	};
	
	var leave_field = function(){
		window.setTimeout( function(){self._hideSuggestions()}, 200 );
		if (!$(self).val())
			$(self).css('color','gray').val(self._hash['inputFieldDefault']);
	}
	
	self.attr("autocomplete", "off").css('color', 'gray');
	self.click(enter_field).focus(enter_field).blur(leave_field);
	if ( !self.val() )
		self.val(self._hash['inputFieldDefault']);
	
	// init wait image
	
	if ( self._hash['loadImageSrc'] ) {
		self._wait_img = $('<img src="' + self._hash['loadImageSrc'] + '">')
			.hide();
		
		$('body').append(self._wait_img);
	}
	
	// add event handler functions
	
	self.keydown(function(event){

		self.attr("autocomplete", "off");
		
		switch ( event.keyCode ) {
			case 27: /* ESC */
				self._hideSuggestions();
				return false;
		}
		
		var shows_suggestions = self._container.css('display')!='none'
			&& self._container.find('a').length > 0;
		
		if ( shows_suggestions ) {
			
			var active_entry = $( self._container.find('.' + self._hash['activeEntryClass']) );
			var entries = self._container.find('li:has(a)');
			
			var index_of_active_entry = -1;
			entries.each(function(index) {
				if ( this==active_entry.get(0) )
					index_of_active_entry = index;
			});
			
			switch ( event.keyCode ) {
				case 13: /* RETURN */
				case 17: /* ENTER */
					if ( active_entry.length ) {
						self.blur();
						active_entry.click();
						return false;
					}
					break;

				case 38: /* UP */
					if ( index_of_active_entry > 0 )
						self._selectEntryAtIndex(index_of_active_entry-1);
					_cancelEvent( event );
					return false;

				case 40: /* DOWN */
					if ( index_of_active_entry < entries.length - 1 )
						self._selectEntryAtIndex(index_of_active_entry+1);
					_cancelEvent( event );
					return false;
			}
		}
		
		self._updateSuggestions();
		
		return true;
	});
	
	self.keyup(function(event){
		
		switch ( event.keyCode ) {
			case 13: /* RETURN */
			case 17: /* ENTER */
			case 38: /* UP */
			case 40: /* DOWN */
			case 27: /* ESC */
				_cancelEvent( event );
				return false;
		}
		
		self._updateSuggestions();
		
		return true;
	});
}

