/**
 * autoSuggest
 * 
 * @version 0.4.3 2010-06-26
 * @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
 *
 * served events: "choose", "show", "hide"
 *
 * @todo wrong positioning of list in Chrome, Safari
 * @todo leaking in IE
 * @todo switch to make it case (in-)sensitive
 */
/*global document, window, vxJS */

if(!this.vxJS || !this.vxJS.xhr || !this.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, keyListened, 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/assets/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, serve) {
		elem.value = v.text;
		if(config.keyElem) {
			config.keyElem.value = v.key;
		}
		if(serve) {
			vxJS.event.serve(that, "choose");
		}
	};

	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() {
		var txt, p, s, r = this.response;

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

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

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

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

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

	var initRequest = function() {
		var p, s;

		if(!elem.value || (sentString && sentString === elem.value)) {
			return;
		}

		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);
		}

		sentString = elem.value;

		if(xhr) {
			xhr.use(null, { text: sentString }, { node: xhrImgSize ? xhrImg : null });
		}
		else {
			xhr = vxJS.xhr( xhrReq || {}, { limit: config.maxEntries || 10, text: sentString }, { node: xhrImgSize ? xhrImg : null });
			vxJS.event.addListener(xhr, "complete", handleXhrResponse);
			vxJS.event.addListener(xhr, "timeout", function() { window.alert("Response took to long!");});
		}
	};

	var handleKeyDown = function(e) {

		keyListened = true;

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

		enableTypeAhead	= true;
		window.clearTimeout(timeoutId);
		keyListened = true;
		timeoutId = window.setTimeout(initRequest, typeAheadTimeout);
	};

	var handleKeyUp = function(e) {
		var kc = e.keyCode;

		if (keyListened || kc < 32 && kc !== 8 && kc !== 0 || (kc >= 33 && kc < 46) || (kc >= 112 && kc <= 123)) {
			return;		// ignoring some keys
		}

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

		timeoutId = window.setTimeout(initRequest, typeAheadTimeout);
	};

	document.body.appendChild(layer);

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

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

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

	return that;
};