/**
 * autoSuggest
 * 
 * @version 0.3.0 2009-06-24
 * @author Gregor Kofler
 * 
 * @param {Object} elem input element
 * @param {Object} xhr request object containing command and URI
 * @param {Object} config additional parameters to configure dropdown
 *	maxEntries:	{Number} max entries in list
 *	keyElem:	{Object} optional input element for storing key values
 *	restrict:	{Boolean} restrict to provided entries when true
 *
 * "select" not implemented yet
 * served events: "select", "show", "hide"
 *
 * @todo wrong positioning of list in Chrome, Safari
 * @todo heavy leaking in IE
 * 
 */
/*global document, window, vxJS */

if(!vxJS || !vxJS.xhr || !vxJS.widget) { throw Error("widget.autoSuggest: vxJS core or vxJS.xhr or vxJS.widget missing."); }

vxJS.widget.autoSuggest = function(elem, xhrReq, config) {

	config = config || {};

	var	typeAheadTimeout = 250, timeoutId,
		xhr, enableTypeAhead, typedText, sentString, that = {}, hidden = true,
		initData = {
			text: elem.value,
			key: config.keyElem ? config.keyElem.value : null
		},
		list = vxJS.widget.shared.list({hoverSelect: true }),
		layer = function() {
			var l = "div".setProp("class", "vxJS_autoSuggest").create(list.element);
			l.style.display = "none";
			return l;
		}(),
		xhrImg = function() {
			var i;
			i = "img".setProp("src", "js/xhr_activity.gif").create();
			i.style.position = "absolute";
			vxJS.dom.setElementPosition(i,  { x: -100, y: -100 } );
			document.body.appendChild(i);
			return i;
		}(), xhrImgSize;
	
	var setValue = function(v) {
		elem.value = v.text;
		if(config.keyElem) {
			config.keyElem.value = v.key;
		}
	};

	var show = function() {
		if(hidden) {
			that.show();
			vxJS.event.serve(that, "show");
			hidden = false;
		}
	};

	var hide = function() {
		if(!hidden) {
			that.hide();
			vxJS.event.serve(that, "hide");
			hidden = true;
		}
	};
	
	var handleXhrResponse = function(r) {
		var s, p;

		if(sentString !== elem.value || !r || !r.entries || !r.entries.length) {
			hide();
			return;
		}

		s = r.entries[0].text;
		if(config.restrict) {
			while(elem.value.length) {
				if(new RegExp("^"+elem.value, "i").test(s)) {
					break;
				}
				elem.value = elem.value.slice(0, -1);			
			}
			typedText = elem.value;
		}

		if (enableTypeAhead) {
			setValue(r.entries[0]);
			vxJS.selection.set(elem, typedText.length);
		}

		p = vxJS.dom.getElementOffset(elem);
		p.y += vxJS.dom.getElementSize(elem).y;
		layer.style.width = elem.offsetWidth+"px";
		vxJS.dom.setElementPosition(layer, p);

		list.fill(r.entries);
		show();
	};

	var handleKeyDown = function(e) {
		if(!list || !list.getRows()) {
			return;
		}
		switch (e.keyCode) {
			case 38:
				list.up();
				break;
			case 40:
				list.down();
				break;
			case 27:
				setValue(initData);
				hide();
				break;
			case 13:
				setValue(list.getSelected()[0]);
				initData = { text: elem.value, key: config.keyElem ? config.keyElem.value : null };
				vxJS.selection.setCaretPosition(elem, "end");
				vxJS.event.preventDefault(e);
				hide();
		}
	};

	var handleKeyUp = function(e) {
		var kc = e.keyCode;
		if(kc === 27) { // needed for FF3
			setValue(initData);
			return;
		}

		if (kc < 32 && kc != 8 || (kc >= 33 && kc < 46) || (kc >= 112 && kc <= 123)) { return; }	// ignoring non alphanumeric Keys

		typedText = elem.value;

		window.clearTimeout(timeoutId);

		if(!typedText) {
			hide();
			return;
		}

		enableTypeAhead = !(kc == 8 || kc == 46);	// BS (8) and del (46) - Suggestions without Typeahead

		timeoutId = window.setTimeout(
			function() {
				var p, s;

//				hide();
				
				sentString = elem.value;

				if(!xhrImgSize && xhrImg.complete) {
					xhrImgSize = vxJS.dom.getElementSize(xhrImg);
				}
				if(xhrImgSize) {
					p = vxJS.dom.getElementOffset(elem);
					s = vxJS.dom.getElementSize(elem);
					p.x += s.x-xhrImgSize.x-4;
					p.y += (s.y-xhrImgSize.y)/2;
					vxJS.dom.setElementPosition(xhrImg, p);
				}

				if(xhr) {
					xhr.use(null, null, { text: sentString }, { node: xhrImgSize ? xhrImg : null });
					return;
				}

				xhr = vxJS.xhr(
					xhrReq || {},
					{ completed: handleXhrResponse },
					{ limit: config.maxEntries || 10, text: sentString },
					{ node: xhrImgSize ? xhrImg : null }
				);
			}, typeAheadTimeout);
	};

	typedText = elem.value;
	
	document.body.appendChild(layer);

	vxJS.event.addListener( elem, "keyup",	function (e) { handleKeyUp(e); });
	vxJS.event.addListener( elem, "keydown",function (e) { handleKeyDown(e); });
	vxJS.event.addListener( elem, "blur",	function () { hide(); });

	vxJS.event.addListener(list, "select", function() {
		setValue(list.getSelected()[0]);
		initData = { text: elem.value, key: config.keyElem ? config.keyElem.value : null };
		vxJS.selection.setCaretPosition(elem, "end");
		hide();
	});

	/**
	 * expose container element
	 */
	that.element = layer;

	/**
	 * expose hooks to attach fx
	 */
	that.show = function() { layer.style.display = ""; };
	that.hide = function() { layer.style.display = "none"; };
	
	return that;
};