(function($){
 
    $.widget("ui.airportpicker", {
	
	_init: function() {
	    new $.AirportPicker(this.element[0], this.options);
	},
	
	result: function(handler) {
	    return this.element.bind("result", handler);
	}
	
    });

    $.AirportPicker = function(input, options) {

	var KEY = {
	    UP: 38,
	    DOWN: 40,
	    DEL: 46,
	    TAB: 9,
	    RETURN: 13,
	    ESC: 27,
	    COMMA: 188,
	    PAGEUP: 33,
	    PAGEDOWN: 34,
	    BACKSPACE: 8
	};

	// Create $ object for input element
	var $input = $(input).attr("autocomplete", "off");

	var data;
	var timeout;
	var previousValue = "";
	var hasFocus = 0;
	var lastKeyPressCode;
	var config = {
	    mouseDownOnSelect: false
	};
	//var select = $.Autocompleter.Select(options, input, selectCurrent, config);
	
	var blockSubmit;
	
	// prevent form submit in opera when selecting with return key
	$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
	    if (blockSubmit) {
		blockSubmit = false;
		return false;
	    }
	});
	
	// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
	$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
	    // track last key pressed
	    lastKeyPressCode = event.keyCode;
	    switch(event.keyCode) {
		case KEY.TAB:
		case KEY.RETURN:
		    if( selectCurrent() ) {
			// stop default to prevent a form submit, Opera needs special handling
			event.preventDefault();
			blockSubmit = true;
			return false;
		    }
		    break;
				
		case KEY.ESC:
		    //select.hide();
		    break;
				
		default:
		    clearTimeout(timeout);
		    timeout = setTimeout(onChange, options.delay);
		    break;
	    }
	}).focus(function(){
	    // track whether the field has focus, we shouldn't process any
	    // results if the field no longer has focus
	    hasFocus++;
	}).blur(function() {
	    hasFocus = 0;
	    if (!config.mouseDownOnSelect) {
		hideResults();
	    }
	});
	
	function onChange() {
	    if( lastKeyPressCode == KEY.DEL ) {
		select.hide();
		return;
	    }
		
	    var currentValue = $input.val();
		
	    if ( currentValue.length >= options.minChars) {
		//alert(currentValue);
		displayResults(currentValue);
		//request(currentValue, receiveData, hideResultsNow);
	    }
	};
	
	function displayResults(criteria) {
	    var outterPanel = $("<div/>")
	    .hide()
	    .css("position", "absolute")
	    .css("background-color", "#CCCCCC")
	    .css("z-index", "99")
	    .css("padding", "3px")
	    .appendTo(document.body);
	    
	    var innerPanel = $("<div/>")
		.css("background-color", "#FFFFFF")
		.css("position", "relative")
		.appendTo(outterPanel);
	    var columns = options.airports.length / options.columnLength;
	    for(var i=0; i<columns;i++) {
		var column = $("<div>").css("display","inline").appendTo(innerPanel);
		var list = $("<ul>").appendTo(column);
		var columnLength = (((i*options.columnLength)+options.columnLength) > options.airports.length) ? options.airports.length : (i*options.columnLength)+options.columnLength;
		for(var j = (i*options.columnLength); j<columnLength; j++) {
		    list.append("<li>").css("list-style-image","none").css("list-style-type","none").append(options.airports[j]);
		}
	    }
	    
	    var offset = $(input).offset();
	    outterPanel.css({
		width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
		top: offset.top + input.offsetHeight,
		left: offset.left
	    }).show();
        }
	
	function filterAirports(airports, criteria) {
	    var reg = new RegExp(criteria, "i");
	    return $.grep(airports, function(item, index) {
		return (reg.exec(item) != null);
	    });
	}
	/*/
	function selectCurrent() {
		var selected = select.selected();
		if( !selected )
			return false;
		
		var v = selected.result;
		previousValue = v;
		
		if ( options.multiple ) {
			var words = trimWords($input.val());
			if ( words.length > 1 ) {
				v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
			}
			v += options.multipleSeparator;
		}
		
		$input.val(v);
		hideResultsNow();
		$input.trigger("result", [selected.data, selected.value]);
		return true;
	}
	
	
	
	function trimWords(value) {
		if ( !value ) {
			return [""];
		}
		var words = value.split( options.multipleSeparator );
		var result = [];
		$.each(words, function(i, value) {
			if ( $.trim(value) )
				result[i] = $.trim(value);
		});
		return result;
	}
	
	function lastWord(value) {
		if ( !options.multiple )
			return value;
		var words = trimWords(value);
		return words[words.length - 1];
	}
	
	// fills in the input box w/the first match (assumed to be the best match)
	// q: the term entered
	// sValue: the first matching result
	function autoFill(q, sValue){
		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
		// if the last user key pressed was backspace, don't autofill
		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
			// select the portion of the value not typed by the user (so the next character will erase)
			$.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
		}
	};

	function hideResults() {
		clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	};

	function hideResultsNow() {
		var wasVisible = select.visible();
		select.hide();
		clearTimeout(timeout);
		stopLoading();
		if (options.mustMatch) {
			// call search and run callback
			$input.autocomplete("search", function (result){
					// if no value found, clear the input box
					if( !result ) {
						if (options.multiple) {
							var words = trimWords($input.val()).slice(0, -1);
							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
						}
						else
							$input.val( "" );
					}
				}
			);
		}
		if (wasVisible)
			// position cursor at end of input field
			$.Autocompleter.Selection(input, input.value.length, input.value.length);
	};

	function receiveData(q, data) {
		if ( data && data.length && hasFocus ) {
			stopLoading();
			select.display(data, q);
			autoFill(q, data[0].value);
			select.show();
		} else {
			hideResultsNow();
		}
	};

	function request(term, success, failure) {
		if (!options.matchCase)
			term = term.toLowerCase();
		var data = cache.load(term);
		// recieve the cached data
		if (data && data.length) {
			success(term, data);
		// if an AJAX url has been supplied, try loading the data now
		
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			
			var extraParams = {
				timestamp: +new Date()
			};
			$.each(options.extraParams, function(key, param) {
				extraParams[key] = typeof param == "function" ? param() : param;
			});
			
			$.ajax({
				// try to leverage ajaxQueue plugin to abort previous requests
				mode: "abort",
				// limit abortion to this input
				port: "autocomplete" + input.name,
				dataType: options.dataType,
				url: options.url,
				data: $.extend({
					q: lastWord(term),
					limit: options.max
				}, extraParams),
				success: function(data) {
					var parsed = options.parse && options.parse(data) || parse(data);
					cache.add(term, parsed);
					success(term, parsed);
				}
			});
		}

		else if (options.source && typeof options.source == 'function') {
			var resultData = options.source(term);
			var parsed = (options.parse) ? options.parse(resultData) : resultData;

			cache.add(term, parsed);
			success(term, parsed);
		} else {
			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
			select.emptyList();
			failure(term);
		}
	};
	
	function parse(data) {
		var parsed = [];
		var rows = data.split("\n");
		for (var i=0; i < rows.length; i++) {
			var row = $.trim(rows[i]);
			if (row) {
				row = row.split("|");
				parsed[parsed.length] = {
					data: row,
					value: row[0],
					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
				};
			}
		}
		return parsed;
	};

	function stopLoading() {
		$input.removeClass(options.loadingClass);
	};
	 */
    };


    $.AirportPicker.defaults = {
	minChars: 1,
	delay: 400,
	columnLength: 25,
	matchCase: false,
	matchSubset: true,
	matchContains: false,
	cacheLength: 10,
	max: 100,
	mustMatch: false,
	extraParams: {},
	selectFirst: true,
	formatItem: function(row) { return row[0]; },
	formatMatch: null,
	autoFill: false,
	width: 0,
	scroll: true,
	scrollHeight: 180
    };

    $.extend($.ui.airportpicker, {
	defaults: $.AirportPicker.defaults
    });

})(jQuery);