(function($) {
	$.fn.autocomplete = function(options) {
		var myOptions;
		var hiddenValues;
		var numHiddenValues;
		var hiddenFieldName = false;
		var hiddenField;
		
		var numItems;
		var curItem = -1;
		var originalValue = '';
		var menuShowing = false;
		var menuPicked = false;
		var loaded = false;
		var $this;
		var targetContainer = "autocomplete";
		
		myOptions = $.extend({
			top_padding: 0,
			left_padding: 0,
			extraValues: false,
			alwaysErase: false,
			addClass: false,
			removeClass: false,
			setWidth: true,
			columns: ['Hint']
		}, options);
		
		
		hiddenValues = [];
		numHiddenValues = 0;
		$this = $(this);
		if(myOptions.targetContainer){
			targetContainer = myOptions['targetContainer'];
		}
		if ($(this).parent().find("#"+targetContainer).length == 0)
			$(this).after('<div id="'+targetContainer+'"></div>');
		if (myOptions.setWidth) {
			$("#"+targetContainer).css({
				width: $(this).width() + 'px'
			}); 
		}
		
		if (options['hidden_name']) {
			hiddenFieldName = options['hidden_name'];
			hiddenField = $("#" + hiddenFieldName);
		}
		
		$(this).keydown(function(e) {
			if ( e.keyCode == 40 ) {	// down arrow
				$("#"+targetContainer+" tr").eq(curItem).removeClass("cur");
				if (curItem == -1 && !menuPicked) {
					$("#"+targetContainer).show();
					menuShowing = true;
					menuPicked = false;
				}
				if (menuShowing) {
					curItem++;
					if (curItem >= numItems - 1) curItem = numItems - 1;
					var selItem = $("#"+targetContainer+" tr").eq(curItem);
					selItem.addClass("cur");
					if (typeof($.fn.scrollTo) == 'function')
						$("#"+targetContainer).scrollTo(selItem);
					$(this).val(selItem.find("td").eq(0).text());
					if (hiddenFieldName)
						hiddenField.val(hiddenValues[selItem.find("td").eq(0).text()][hiddenFieldName]);
				}
				
			} else if (e.keyCode == 38) {	// up arrow
				if (menuShowing) {
					$("#"+targetContainer+" tr").eq(curItem).removeClass("cur");
					curItem--;
				}
				if (curItem < 0) {
					curItem = -1;
					$("#"+targetContainer).hide();
					$(this).val(originalValue);
					if (hiddenFieldName)
						hiddenField.val('');
				}
				if ($("#"+targetContainer+":visible").length) {
					var selItem = $("#"+targetContainer+" tr").eq(curItem);
					selItem.addClass("cur");
					if (typeof($.fn.scrollTo) == 'function')
						$("#"+targetContainer).scrollTo(selItem);
					$(this).val(selItem.find("td").eq(0).text());
					if (hiddenFieldName)
						hiddenField.val(hiddenValues[selItem.find("td").eq(0).text()][hiddenFieldName]);
				}
			}
			else if (e.keyCode == 37 || e.keyCode == 39) {
				
			}
			
			// pressed enter key & menu is showing
			else if (e.keyCode == 13 && $("#"+targetContainer+":visible").length) {
				$("#"+targetContainer).hide();
				menuShowing = false;
				menuPicked = true;
				return true;
			}
			
			else setTimeout(checkSearchInput, 50);
			return true;
		}).click(function() {
			if ((myOptions['addClass'] && !$(this).hasClass(myOptions['addClass'])) || myOptions['alwaysErase']) {
				$(this).val('');
				if (myOptions['removeClass'])
					$(this).removeClass(myOptions['removeClass']);
				if (myOptions['addClass']) {
					$(this).addClass(myOptions['addClass']);	
				}	
			}
			$(this).unbind('click');
		});
		
		if (navigator.userAgent.indexOf('iPhone')) {
			$(this).focus(function() {
				$(this).click();	
			});
		}
		
		$("#"+targetContainer+" tr").live("click", function() {
			var text = $(this).find("td").eq(0).text();
			$this.val(text);
			originalValue = text;
			if (hiddenFieldName)
				hiddenField.val(hiddenValues[text][hiddenFieldName]);
		});
		
		$(document).click(function() {
			$("#"+targetContainer).hide();
			menuShowing = false;
		});
		
		function checkSearchInput() {
			curItem = -1;
			var val = $this.val();	
			var params = {};
			if (myOptions.extraValues) {
				for (var i in myOptions.extraValues) {
					params[i] = myOptions.extraValues[i];	
				}
			}
			
			params['fullSearch'] = val;
			if (val.length > 2) {
				if (numHiddenValues) {
					var buffer = '<table>';
					var numItems = 0;
					for (var i in hiddenValues) {
						if (i.toLowerCase().search(val.toLowerCase()) != -1) {
							buffer += '<tr>';
							for (var column in myOptions.columns) {
								buffer += '<td>' + hiddenValues[i][myOptions['columns'][column]] + '</td>';
							}
							buffer += '</tr>';
							numItems++;
						}
					}
					buffer += '</table>';
					if (numItems) {
						$("#"+targetContainer).html(buffer).show();
						menuShowing = true;
						menuPicked = false;
					} else {
						$("#"+targetContainer).hide();
						menuShowing = false;		
					}
				}
				else if (!loaded) {
					$.get(myOptions['ajaxpath'], params, autoComplete, 'json');
				}
			}
			else {
				// check if there were at least 3 charaters, but now there are less
				// if so, delete the cached autocomplete list and get a new one when
				// there are 3 characters again
				loaded = false;
				if (numHiddenValues) {
					hiddenValues = [];
					numHiddenValues = 0;
				}
				if (val.length == 0) {
					$("#"+targetContainer).hide();	
					menuShowing = false;
				}
			}
		}
		
		// after the ajax call to get matches, this builds the hidden value list and the
		// list of items
		function autoComplete(result) {
			originalValue = $this.val();
			loaded = true;
			var buffer = '<table>';
			var colName;
			numItems = 0;
			hiddenValues = [];
			for (var i in result) {
				buffer += '<tr>';
				for (var column in myOptions.columns) {
					colName = myOptions['columns'][column];
					buffer += '<td>' + result[i][colName] + '</td>';
					
					// make search key the first column
					if (column == 0)
						hiddenValues[result[i][colName]] = result[i];
				}
				buffer += '</tr>';
				
				numItems++;
			}
			numHiddenValues = numItems;
			buffer += '</table>';
			if (numItems) {
				$("#"+targetContainer).html(buffer).show();
				menuShowing = true;
				menuPicked = false;	
			} else {
				$("#"+targetContainer).html("").hide();
				menuShowing = false;
			}
			return this;
		}
		
		return this;
	}
})(jQuery);