function AutoSuggest ( field_id )
{
	this.field = document.getElementById ( field_id );

	this.name = field_id;

	var self = this;

	this._old_val = "";

	this.min_chars = 1;
	this.multi_values = true;  // NEW: if FALSE the field will handle one value only

	this._selected = 0;	// Selected element (from 0)
	this._list = null;	// Current list of elements

	this.cbacks = {
		"request" : AutoSuggest.fake_request,
		"row-render" : AutoSuggest._row_render,
		"row-get-val" : AutoSuggest._row_get_val
	};

	this._cache = {};

	this.use_cache = true;

	this._evt_keypress = function ( evt )
	{
		var key = window.event ? window.event.keyCode : evt.keyCode;

		var bubble = 1;

		switch ( key )
		{
			case 9: // TAB
			case 13: // RETURN
				if ( ! self.pop.is_hidden () )
				{
					self._set_value ();
					bubble = 0;
				}
				self._clear_suggestions ();
				break;

			case 27: // ESC
				self._clear_suggestions ();
				break;
		}

		return bubble;
	};

	this.hide = function ()
	{
		self._clear_suggestions ();
	};

	this._evt_keyup = function ( evt )
	{

		var key = window.event ? window.event.keyCode : evt.keyCode;
		
		// set responses to keydown events in the field
		// this allows the user to use the arrow keys to scroll through the results
		// ESCAPE clears the list
		// TAB sets the current highlighted value
		//

		var bubble = 1;

		switch ( key )
		{
			case 38:  // ARROW UP
				if ( self.pop.is_hidden () ) return bubble;
				self._selected -= 1;
				if ( self._selected < 0 ) self._selected = 0;
				self._render ();
				bubble = 0;
				break;


			case 40:  // ARROW DOWN
				if ( self.pop.is_hidden () ) return bubble;
				self._selected += 1;
				if ( self._selected >= self._list.length ) self._selected = self._list.length -1;
				self._render ();
				bubble = 0;
				break;

			case 13:
				break;
			
			default:
				self._get_suggestions ( self.field.value );
		}

		return bubble;
	};

	this._evt_blur = function ()
	{
		setTimeout ( function () { 
			self._clear_suggestions (); 
			if ( self.field._onblur ) 
			{
				self.field._onblur ();
			}
		}, 300 );
	};
	

	// Disable browser autocomplete feature
	this.field.setAttribute ( "autocomplete", "off" );

	this.field._onblur = this.field.onblur;

	this.field.onkeypress 	= this._evt_keypress;
	this.field.onkeyup 	= this._evt_keyup;
	this.field.onblur	= this._evt_blur;

	this.pop = new WWL.popbox ( this.field.id + "-pop" );
	this.pop.set_parent ( this.field, "D" );
	this.pop.show ();
	this.pop.hide ();

	AutoSuggest._instances [ this.name ] = this;
}

AutoSuggest._instances = {};

AutoSuggest.get = function ( name )
{
	return AutoSuggest._instances [ name ];
};

AutoSuggest.set = function ( field_id )
{
	return new AutoSuggest ( field_id );
};

AutoSuggest.evt = function ( div, event_name, instance_name, pos )
{
	var ac = AutoSuggest.get ( instance_name );
	var s;

	switch ( event_name )
	{
		case "over":
			s = ac.cbacks [ 'row-render' ] ( ac._list [ ac._selected ], false );
			$( ac.name + ":" + ac._selected, s );
			$( ac.name + ":" + ac._selected ).className = "row";

			ac._selected = pos;
			s = ac.cbacks [ 'row-render' ] ( ac._list [ pos ], true );
			div.innerHTML = s;
			div.className = 'row_hover';
			break;

		case "click":
			ac._set_value ();
			break;
	}
};

AutoSuggest._row_render = function ( row, is_selected )
{
	
	var s = row [ 'label' ] + ( is_selected ? " - SELECTED" : "" );

	return s;
};

AutoSuggest._row_get_val = function ( row )
{
	return row [ 'value' ];
};

AutoSuggest.prototype._get_suggestions = function ( val )
{
	var self = this;

	var my_val = val.toLowerCase ();

	// if input stays the same, do nothing
	if ( my_val == this._old_val ) return;

	this._old_val = my_val;

	// input length is less than the min required to trigger a request
	// do nothing
	if ( val.length < this.min_chars ) 
	{
		this._clear_suggestions ();
		return;
	}

	if ( this.use_cache && typeof this._cache [ my_val ] != "undefined" )
	{
		console.debug ( "CACHE HIT FOR: %s", my_val );

		self._set_suggestions ( this._cache [ my_val ], val );
	} else {
		// new request
		this.cbacks [ 'request' ] ( val, function ( lst ) { self._set_suggestions ( lst, val ); } );
	}
};

AutoSuggest.prototype._clear_suggestions = function ()
{
	this.pop.get ().innerHTML = '';
	this.pop.hide ();
};

AutoSuggest.prototype._set_value = function ()
{
	var val = this.cbacks [ 'row-get-val' ] ( this._list [ this._selected ] );
	var orig = this.field.value;
	var pos;

	pos = orig.lastIndexOf ( " " );
	orig = orig.substr ( 0, pos ) + " " + val;

	this.field.value = orig.replace ( /^ */, "" );
	if ( this.multi_values ) this.field.value += ", ";

	this._clear_suggestions ();

	var self = this;
	setTimeout ( function () { self.field.focus (); }, 50 );
};


AutoSuggest.prototype._set_suggestions = function ( lst, orig_txt )
{
	if ( this.use_cache )
	{
		var my_val = orig_txt.toLowerCase ();
		this._cache [ my_val ] = lst;
	}

	// if field input no longer matches what was passed to the request
	// don't show the suggestions
	//
	if ( orig_txt != this.field.value )
		return false;

	this._selected = 0;
	this._list = lst;

	this._render ();

	this.pop.show ();
};

AutoSuggest.prototype._render = function ()
{
	if ( ! this._list || ! this._list.length ) 
	{
		this._clear_suggestions ();
		return;
	}

	var lst = this._list;
	var t, l = lst.length;
	var s = '<div class="suggest-box">';
	var args;
	var class_name;

	for ( t = 0; t < l; t ++ )
	{
		class_name = ( t == this._selected ? "row_hover" : "row" );
		args = "'" + this.name + "'," + t;
		s += '<div class="' + class_name + '" ';
		s += ' id="' + this.name + ':' + t + '" ';
		s += ' onclick="AutoSuggest.evt(this,\'click\',\'' + this.name + '\',' + t + ')" ';
		s += ' onmouseover="AutoSuggest.evt(this,\'over\',\'' + this.name + '\',' + t + ')" ';
		s += '>';
		s += this.cbacks [ 'row-render' ] ( lst [ t ], ( t == this._selected ? true : false ) );
		s += '</div>';

	}

	s += '</div>';

	this.pop.get ().innerHTML = s;
};

AutoSuggest.fake_request = function ( txt, cback )
{
	var t, l = [];

	for ( t = 0; t < 10; t ++ )
		l.push ( { label: txt + "-" + t, value: "v-" + txt + "-" + t } );

	if ( cback ) return cback ( l );
};


