/**
 * Smart Search jQuery Widget
 * by Giancarlo Bellido
 *
 * Options:
 *
 * 	url:        Datasource URL.
 *	data:       Format: { data: [ { value: <value>, label: <label> }, { .. } ] }
 *	force:      Forces Textbox to select value from select box.
 *	class_name: Applies class to select box.
 *	name:	    Select Box Name. Defaults to Element name.
 *	onNotFound: Not Found event.
 */

jQuery.widget("ui.smartsearch", {

	select_box: null,
	container_tag: "select",
	// Removed Elements.
	removed: null,

	_initEvents: function()
	{
		this.element.focus(this._onFocus)
		            .keydown(this._onKeyDown)
		            .keyup(this._onKeyPress)
			    .blur(this._onBlur)
			    .resize(this._onResize)
		;		
	},

	_initSelectBox: function()
	{
		var self =this;
		var position = this.element.position();
		
		if (!this.options.name)
		{
			this.options.name = this.element.attr("name");
			this.element.removeAttr("name");
		}

		this.select_box = $(document.createElement(this.container_tag));
		this.select_box.hide()
			       .focus(function() { self._onSelectFocus(); })
			       .click(function() { self._onSelectClick(); })
			       .blur(function() { self._onSelectBlur(); })
			       .css("position", "absolute")
			       .css("left", position.left)
		               .attr("size", 6)
			       .attr("name", this.options.name)
			       .addClass("ph-smartsearch")
			       .addClass(this.options.class_name)
		;
	},

	_init: function()
	{
		var self = this;

		this.removed = $(document.createElement(this.container_tag));

		this._initEvents();
		this._initSelectBox();

		if (this.options.url)
			$.get(this.options.url, null, function(data) { self._onData(data.data); }, "json");
		else if (this.options.data)
			this._onData(this.options.data);

		this.element.after(this.select_box).attr("autocomplete", "off");
		this.element[0].select_box = this.select_box;
		this.element[0].instance   = this;

		this._onResize();
	},
	
	_onData: function(data)
	{
		var value = this.element.val();

		for (var key in data)
		{
			this.select_box.append('<option value="' + data[key].value + '"' + 
				(value == data[key].value ? " selected=\"selected\"" : '') + 
				'>' + data[key].label + '</option>');
		}
	},

	_onResize: function()
	{
		var w = this.element.outerWidth();
		this.select_box.width(w > this.options.min_width ? w : this.options.min_width);
	},

	_onBlur: function()
	{
		this.instance.blur();
	},

	selectOptions: function()
	{
		return this.select_box.children();
	},

	selectSelected: function()
	{
		var s = this.selected();
		if (s.length)
			this.element.val(s.text());

	},

	blur: function()
	{
		var self = this;
		setTimeout(function() 
		{ 
			if (!self.select_box.attr("focused"))
			{
				if (self.options.force)
					self.selectSelected();
				
				if (self.options.onNotFound && (self.element.val() != self.selectedText()))
					self.options.onNotFound();

				self.select_box.hide();
			}
		}, 200);
	},

	_onSelectBlur: function()
	{
		if (this.options.force && this.select_box.attr("focused"))
		{
			this.element.val(this.selectedText());
		}
		this.select_box.hide().removeAttr("focused");
	},

	_onSelectFocus: function()
	{
		this.select_box.attr("focused", "yes");
	},

	_onSelectClick: function()
	{
		this.element.val(this.selectedText());
		this.select_box.hide().removeAttr("focused");
	},

	_onFocus: function()
	{
		this.instance.showSelectBox().css('visibility', 'visible');		
		$(this).keyup(); 
	},

	/** Show Select Box and Make sure it is in the right position. */
	showSelectBox: function()
	{
		var position = this.element.position();
		this.select_box.show().css("top", position.top + this.element.outerHeight())
		return this.select_box;
	},

	selected: function()
	{
		return this.select_box.children(':selected');
	},

	selectedText: function()
	{
		return this.selected().text();
	},

	previous: function()
	{
		return this.selected().prev(); //All(':visible').eq(0);
	},

	next: function()
	{
		return this.selected().next(); //All(':visible').eq(0);
	},

	select: function(item)
	{
		this.deselect();
		return item ? $(item).attr("selected", "selected") : null;
	},

	_onKeyDown: function(evt)
	{
		var E = this.instance;

		switch (evt.keyCode) {
		case 38: // UP
			if (E.select(E.previous()).length == 0)
				E.select(E.select_box.children(':last'));

			break;
		case 40: // DOWN
			if (E.select(E.next()).length == 0)
				E.select(E.select_box.children(':first'));

			if (this.select_box.is(':hidden'))
				this.showSelectBox();
			break;
		case 9:
			E.select_box.css('visibility', 'hidden');
			break;
		default:
		}
	},

	deselect: function(item)
	{
		if (!item) item = this.selected();
		item.removeAttr("selected");
	},

	_filterOut: function(option)
	{
		this.removed.append(option);
	},

	_filterIn: function(option)
	{
		this.select_box.append(option);
	},

	// Checks if option matches text box . if it does then selects it and moves it up.
	_match: function(option)
	{
		if (option.innerHTML == this.element.val())
			this.select(option).prependTo(this.select_box);
	},

	destroy: function()
	{
		if (this.select_box)
		{
			this.element.attr("name", this.select_box.attr("name"));
			this.select_box.remove();
			this.select_box = null;
		}

		if (this.removed)
			this.removed = null;

		$.widget.prototype.destroy.apply(this, arguments);

		return this;
	},

	filter: function()
	{
		var self = this;
		var regex = new RegExp(this.element.val(), "i");
		
		this.select_box.children().each(function()
		{
			if (regex.test(this.innerHTML))
				self._match(this);
			else
				self._filterOut(this);
		});
		//iterate through removed Items
		this.removed.children().each(function()
		{
			if (regex.test(this.innerHTML))
			{
				self._filterIn(this);
				self._match(this);
			}
		});

		return this.select_box.children();
	},

	_onKeyPress: function(evt)
	{
		switch (evt.keyCode) {
		case 13: 
			if (this.select_box.is(':visible'))
			{
				this.instance.selectSelected();				
				this.instance.blur();				
			}
			break;
		case 38: case 40:
			break;
		default:

			this.instance.showSelectBox();
			var found = this.instance.filter();

			if (found.length)
			{
				if (this.instance.options.force)
					this.instance.select(found.eq(0));

				if (this.instance.options.onFound)
					this.instance.options.onFound();

			} else if (this.instance.options.onNotFound)
				this.instance.options.onNotFound();
				
		}
	}

});

$.extend($.ui.smartsearch, {
	defaults: {
		min_width: 100
	},
	getter : [ "selectOptions" ]
});
