(function( $ ) {
    $.widget( "custom.combobox", {
	options: {
	    autoAdd: false
	},

	_create: function() {
	    this.wrapper = $( "<span>" )
		.addClass( "custom-combobox" )
		.insertAfter( this.element );

	    this.element.hide();
	    this._createAutocomplete();
	    this._createShowAllButton();
	},

	_createAutocomplete: function() {
	    var selected = this.element.children( ":selected" ),
		value = selected.val() ? selected.text() : "";

	    this.input = $( "<input>" )
		.appendTo( this.wrapper )
		.val( value )
		.attr( "title", "" )
		.addClass( "custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left" )
		.autocomplete({
		    delay: 0,
		    minLength: 0,
		    source: $.proxy( this, "_source" )
		})
		.tooltip({
		    tooltipClass: "ui-state-highlight"
		});

	    this._on( this.input, {
		autocompleteselect: function( event, ui ) {
		    ui.item.option.selected = true;
		    this._trigger( "select", event, {
			item: ui.item.option
		    });
		},

		autocompletechange: "_removeIfInvalid"
	    });
	},

	_createShowAllButton: function() {
	    var input = this.input,
		wasOpen = false;

	    $( "<a>" )
		.attr( "tabIndex", -1 )
		.attr( "title", "Show All Items" )
		.tooltip()
		.appendTo( this.wrapper )
		.button({
		    icons: {
			primary: "ui-icon-triangle-1-s"
		    },
		    text: false
		})
		.removeClass( "ui-corner-all" )
		.addClass( "custom-combobox-toggle ui-corner-right" )
		.mousedown(function() {
		    wasOpen = input.autocomplete( "widget" ).is( ":visible" );
		})
		.click(function() {
		    input.focus();

		    // Close if already visible
		    if ( wasOpen ) {
			return;
		    }

		    // Pass empty string as value to search for, displaying all results
		    input.autocomplete( "search", "" );
		});
	},

	_source: function( request, response ) {
	    var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
	    response( this.element.children( "option" ).map(function() {
		var text = $( this ).text();
		if ( this.value && ( !request.term || matcher.test(text) ) )
		    return {
			label: text,
			value: text,
			option: this
		    };
	    }) );
	},

	_removeIfInvalid: function( event, ui ) {

	    // Selected an item, nothing to do
	    if ( ui.item ) {
		return;
	    }

	    // Search for a match (case-insensitive)
	    var value = this.input.val(),
		valueLowerCase = value.toLowerCase(),
		valid = false;
	    this.element.children( "option" ).each(function() {
		if ( $( this ).text().toLowerCase() === valueLowerCase ) {
		    this.selected = valid = true;
		    return false;
		}
	    });

	    // Found a match, nothing to do
	    if ( valid ) {
		this.element.val(value);
		this.element.trigger('change',event,ui)
		return;
	    }

	    if( this.options.autoAdd ) {
		// auto add this thing to the list?
		var r = true;
		if( $.isFunction(this.options.autoAdd ) ) {
		    r = this.options.autoAdd(val);
		}
		var v = $('<option/>').val(value).html(value);
		this.element.append(v);
		this.element.val(value);
		this._trigger('afteradd',event,ui);
	    }
	    else {
		// Remove invalid value
		this.input
		    .val( "" )
		    .attr( "title", value + " didn't match any item" )
		    .tooltip( "open" );
		this.element.val( "" );
		this._delay(function() {
		    this.input.tooltip( "close" ).attr( "title", "" );
		}, 2500 );
		this.input.data( "ui-autocomplete" ).term = "";
	    }
	},

	_destroy: function() {
	    this.wrapper.remove();
	    this.element.show();
	},

	removeItems: function() {
	    this.element.children('option').remove();
	},

	addItem: function(o) {
            var v = $('<option/>').val(o.val).html(o.text);
	    this.element.append(v);
	    this.element.val(o.val);
	    this.input.val(o.text);
	},

	select: function(v) {
	    /* find the option, get its text */
	    var self = this;
	    this.element.children('option').each(function(){
		if( $(this).val() == v ) {
		    this.selected = true;
		    self.input.val(this.html())
		}
	    })
	}
    });
})( jQuery );
