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

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

jQuery.addSuggest = function( container, hash )
{
	if ( $(container).length == 0 )
		return;
	
	_input 					= $(container).eq(0);
	_fadeout_timer			= null;
	_wait_img				= null;
	_cache					= {'':''};
	_current_search_term	= '';
	_container				= null;
	
	_startFadeTimer = function()
	{
		window.clearTimeout(_fadeout_timer);
		
		_fadeout_timer = window.setTimeout(function(){
			
			if ( _container.find('.' + hash['activeEntryClass']).length ) {
				_startFadeTimer();
				return;
			}
			
			_container.fadeOut('slow');
			
		}, 10000);
	}
	
	_showSuggestions = function()
	{
		var search_term = _input.val().toLowerCase();
		
		var output = _cache[search_term];
		
		if ( _cache[search_term]===null
			|| _cache[search_term]===false
			|| typeof _cache[search_term]=='undefined' )
			return;
		
		if ( !output ) {
			_hideSuggestions();
			return;
		}
		
		_container.html( output );
		
		_container.find('li:has(a)').each(function() {
			
			$(this).hover(
				function(){
					_selectEntry($(this));
				},
				function(){
					$(this).removeClass('active');
					_startFadeTimer();
			});
			
			$(this).click(function(){
				location.href = $(this).find('a').attr('href');
			});
		});		
		
		if ( _wait_img )
			_wait_img.hide();
		
		if ( !hash['container'] )
			_container.css({left:_input.offset().left,
							top:_input.offset().top + $(container).height() + 4});
		
		_container.show();
		
		_selectEntryAtIndex(-1);
		_startFadeTimer();
	}
	
	_updateSuggestions = function()
	{
		_startFadeTimer();
		
		var search_term = _input.val().toLowerCase();
		
		// Wartet auf AJAX-Antwort
		if ( _cache[search_term]===false )
			return;
		
		if ( search_term == _current_search_term )
			return;
		
		_current_search_term = search_term;
		
		if ( _cache[search_term]!=null ) {
			_showSuggestions();
			return;
		}
		
		if ( _wait_img ) {
		
			if ( hash.loadImageOffset )
				_wait_img.css({	position:"absolute",
					left:_input.offset().left + hash.loadImageOffset[0],
					top:_input.offset().top + hash.loadImageOffset[0]
				});
			else
				_wait_img.css({	position:"absolute",
					left:_input.offset().left + _input.width() - 12,
					top:_input.offset().top + (_input.height() >> 1) - 5
				});
			
			_wait_img.show();
		}
		
		_cache[search_term] = false;
		
		ajax.call(hash['ajaxMethod'], [search_term], function(result) {
			_cache[ajax.response.call.params[0]] = result;
			_showSuggestions();
		});
	}
	
	_hideSuggestions = function()
	{
		if ( _wait_img )
			_wait_img.hide();
		
		_container.hide();
		_selectEntryAtIndex(-1);

	}
	
	_selectEntryAtIndex	= function(index)
	{
		_selectEntry( _container.find('li:has(a)').eq(index) );
	}
	
	_selectEntry = function( entry )
	{
		_container.find('li:has(a)').removeClass('active');
		entry.addClass('active');
		_startFadeTimer();
	}
	
	_cancelEvent = function( event )
	{
		event.cancelBubble = true;
		if (event.stopPropagation)
			event.stopPropagation();
	}
	
	_validateParameterHash = function()
	{
		var params = {
			ajaxMethod:			{ type:'string',	req:true },
			inputFieldDefault:	{ type:'string', 	def:'Suchen'},
			loadImageSrc:		{ type:'string',	def:'/gfx/search_field_wait.gif'},
			loadImageOffset:	{ type:'object',	def:null},
			container:			{ type:'object',	def:null},
			activeEntryClass:	{ type:'string',	def:'active'}
		}
		
		for ( param in params )
		{
			var details = params[param];
	
			if ( typeof details['req'] != 'undefined' && details['req'] && typeof hash[param] == 'undefined' )
				throw("Required arameter '"+param+"' is missing");
			
			if ( typeof details['def'] != 'undefined' && typeof hash[param] == 'undefined' )
				hash[param] = details['def'];
			
			if ( typeof details['type'] != 'undefined' && typeof hash[param] != details['type'] )
				throw("Parameter '"+param+"' must be of type '"+details['type']+"', but is '" + typeof hash[param] + "'");
		}
	}
	
	_validateParameterHash();
	
	// init suggestions container
	
	if ( hash['container'] ) {
		_container = hash['container'];
	}
	else {
		_container = $('<div style="position:absolute;"></div>');
		_container.css({background:"#f8f8f8",
						textAlign:"left",
						WebkitBoxShadow:"0 3px 5px rgba(0,0,0,.2)",
						MozBoxShadow:"0 3px 5px rgba(0,0,0,.2)",
						padding:0,
						borderColor:"#eee #aaa #aaa #eee",
						borderStyle:"solid",
						borderWidth:1
					   });
		$('body').append(_container);
	}
	_container.hide();

	// init input field
	
	var enter_field = function(){
		$(this).css('color', 'black');
		if ($(this).val()==hash['inputFieldDefault'])
			$(this).val('');
		else {
			_showSuggestions();
		}
	};
	
	var leave_field = function(){
		window.setTimeout( function(){_hideSuggestions()}, 200 );
		if (!$(this).val())
			$(this).css('color','gray').val(hash['inputFieldDefault']);
	}
	
	_input.attr("autocomplete", "off").css('color', 'gray');
	_input.click(enter_field).focus(enter_field).blur(leave_field);
	if ( !_input.val() )
		_input.val(hash['inputFieldDefault']);
	
	// init wait image
	
	if ( hash.loadImageSrc ) {
		_wait_img = $('<img src="' + hash.loadImageSrc + '">')
			.hide();
		
		$('body').append(_wait_img);
	}
	
	// add event handler functions
	
	_input.keydown(function(event){
		
		switch ( event.keyCode ) {
			case 27: /* ESC */
				_hideSuggestions();
				return false;
		}
		
		var shows_suggestions = _container.css('display')!='none'
			&& _container.find('a').length > 0;
		
		if ( shows_suggestions ) {
			
			var active_entry = $( _container.find('.' + hash['activeEntryClass']) );
			var entries = _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 ) {
						_input.blur();
						active_entry.click();
						return false;
					}
					break;

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

				case 40: /* DOWN */
					if ( index_of_active_entry < entries.length - 1 )
						_selectEntryAtIndex(index_of_active_entry+1);
					_cancelEvent( event );
					return false;
			}
		}
		
		_updateSuggestions();
		
		return true;
	});
	
	_input.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;
		}
		
		_updateSuggestions();
		
		return true;
	});
}
