/**
 * @author nbu
 * @created 18.03.2008
 *
 * benoetigt: prototype.js; effects.js
 *
 */
var ClavisAutoCompleter = {}
var autoCompleteRequest = null;
ClavisAutoCompleter.Base = Class.create( {
	baseInitialize : function(element, surround, update, options) {
		element = $(element)
		this.element = element;
		this.update = $(update);
		this.surround = $(surround);
		this.hasFocus = false;
		this.changed = false;
		this.active = false;
		this.index = 0;
		this.entryCount = 0;
		this.oldElementValue = this.element.value;
		if (this.setOptions)
			this.setOptions(options);
		else
			this.options = options || {};
		this.options.paramName = this.options.paramName || this.element.name;
		this.options.tokens = this.options.tokens || [];
		this.options.frequency = this.options.frequency || 0.4;
		this.options.minChars = this.options.minChars || 1;
		this.options.onShow = this.options.onShow || function(element, surround) {
			if (!(surround.style.position || surround.style.position == 'absolute')) {
				surround.style.position = 'absolute';
			}
			Position.clone(element, surround, {
				setHeight :false,
				offsetTop :element.offsetHeight
			});
			surround.show();
			/*
			 * Effect.Appear(surround, { duration :0.05 });
			 */
		};
		this.options.onHide = this.options.onHide || function(element, surround) {
			/*
			 * new Effect.Fade(surround, { duration :0.05 })
			 */
			surround.hide();
		};
		if (typeof (this.options.tokens) == 'string')
			this.options.tokens = new Array(this.options.tokens);
		if (!this.options.tokens.include('\n'))
			this.options.tokens.push('\n');
		this.observer = null;
		this.element.setAttribute('autocomplete', 'off');
		Element.hide(this.surround);
		Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
		Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
	},
	show : function() {
		if (Element.getStyle(this.surround, 'display') == 'none')
			this.options.onShow(this.element, this.surround);
		if (!this.iefix && (Prototype.Browser.IE) && (Element.getStyle(this.surround, 'position') == 'absolute')) {
			new Insertion.After(this.surround, '<iframe id="' + this.surround.id + '_iefix" ' + 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
			this.iefix = $(this.surround.id + '_iefix');
		}
		if (this.iefix)
			setTimeout(this.fixIEOverlapping.bind(this), 10);
	},
	fixIEOverlapping : function() {
		Position.clone(this.surround, this.iefix, {
			setTop :(!this.surround.style.height)
		});
		this.iefix.style.zIndex = 100;
		this.surround.style.zIndex = 200;
		Element.show(this.iefix);
	},
	hide : function() {
		this.stopIndicator();
		if (Element.getStyle(this.surround, 'display') != 'none')
			this.options.onHide(this.element, this.surround);
		if (this.iefix)
			Element.hide(this.iefix);
	},
	startIndicator : function() {
		if (this.options.indicator)
			Element.show(this.options.indicator);
	},
	stopIndicator : function() {
		if (this.options.indicator)
			Element.hide(this.options.indicator);
	},
	onKeyPress : function(event) {
		if (this.active)
			switch (event.keyCode) {
			case Event.KEY_TAB:
			case Event.KEY_RETURN:
				this.selectEntry();
				Event.stop(event);
			case Event.KEY_ESC:
				this.hide();
				this.active = false;
				Event.stop(event);
				return;
			case Event.KEY_LEFT:
			case Event.KEY_RIGHT:
				return;
			case Event.KEY_UP:
				this.markPrevious();
				this.render();
				Event.stop(event);
				return;
			case Event.KEY_DOWN:
				this.markNext();
				this.render();
				Event.stop(event);
				return;
			}
		else if (event.keyCode == Event.KEY_TAB || event.keyCode == Event.KEY_RETURN || (Prototype.Browser.WebKit > 0 && event.keyCode == 0))
			return;
		this.changed = true;
		this.hasFocus = true;
		if (this.observer)
			clearTimeout(this.observer);
		this.observer = setTimeout(this.onObserverEvent.bind(this), this.options.frequency * 1000);
	},
	activate : function() {
		this.changed = false;
		this.hasFocus = true;
		this.getUpdatedChoices();
	},
	onHover : function(event) {
		var element = Event.findElement(event, 'LI');
		if (this.index != element.autocompleteIndex) {
			this.index = element.autocompleteIndex;
			this.render();
		}
		Event.stop(event);
	},
	onClick : function(event) {
		var element = Event.findElement(event, 'LI');
		this.index = element.autocompleteIndex;
		this.selectEntry();
		this.hide();
	},
	onBlur : function(event) {
		/* needed to make click events working */
		setTimeout(this.hide.bind(this), 250);
		this.hasFocus = false;
		this.active = false;
	},
	render : function() {
		if (this.entryCount > 0) {
			for ( var i = 0; i < this.entryCount; i++)
				this.index == i ? Element.addClassName(this.getEntry(i), "selected") : Element.removeClassName(this.getEntry(i), "selected");
			if (this.hasFocus) {
				this.show();
				this.active = true;
			}
		} else {
			this.active = false;
			this.hide();
		}
	},
	markPrevious : function() {
		if (this.index > 0)
			this.index--
		else
			this.index = this.entryCount - 1;
		this.getEntry(this.index).scrollIntoView(true);
	},
	markNext : function() {
		if (this.index < this.entryCount - 1)
			this.index++
		else
			this.index = 0;
		this.getEntry(this.index).scrollIntoView(false);
	},
	getEntry : function(index) {
		return this.update.firstChild.childNodes[index];
	},
	getCurrentEntry : function() {
		if (this.index >= 0)
			return this.getEntry(this.index);
		else
			return null;
	},
	selectEntry : function() {
		this.active = false;
		var currEntry = this.getCurrentEntry();
		this.updateElement(currEntry);
	},
	updateElement : function(selectedElement) {
		if (this.options.updateElement && selectedElement != null) {
			this.options.updateElement(selectedElement);
			return;
		}
		if (this.index != -1) {
			var value = '';
			if (this.options.select) {
				var nodes = $(selectedElement).select('.' + this.options.select) || [];
				if (nodes.length > 0)
					value = Element.collectTextNodes(nodes[0], this.options.select);
			} else
				value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
			var bounds = this.getTokenBounds();
			if (bounds[0] != -1) {
				var newValue = this.element.value.substr(0, bounds[0]);
				var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
				if (whitespace)
					newValue += whitespace[0];
				this.element.value = newValue + value + this.element.value.substr(bounds[1]);
			} else {
				this.element.value = value;
			}
		}
		var elemVal = this.element.value;
		if (Object.isString(elemVal)) {
			this.element.value = elemVal.basicTrim();
		}
		this.oldElementValue = this.element.value;
		this.element.focus();
		if (this.options.afterUpdateElement)
			this.options.afterUpdateElement(this.element, selectedElement);
	},
	updateChoices : function(choices) {
		if (!this.changed && this.hasFocus) {
			this.update.innerHTML = choices;
			Element.cleanWhitespace(this.update);
			Element.cleanWhitespace(this.update.down());
			if (this.update.firstChild && this.update.down().childNodes) {
				this.entryCount = this.update.down().childNodes.length;
				for ( var i = 0; i < this.entryCount; i++) {
					var entry = this.getEntry(i);
					entry.autocompleteIndex = i;
					this.addObservers(entry);
				}
			} else {
				this.entryCount = 0;
			}
			this.stopIndicator();
			this.index = -1;
			if (this.entryCount == 1 && this.options.autoSelect) {
				this.selectEntry();
				this.hide();
			} else {
				this.render();
			}
		}
	},
	addObservers : function(element) {
		Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
		Event.observe(element, "click", this.onClick.bindAsEventListener(this));
	},
	onObserverEvent : function() {
		this.changed = false;
		this.tokenBounds = null;
		if (this.getToken().length >= this.options.minChars) {
			this.getUpdatedChoices();
		} else {
			this.active = false;
			this.hide();
		}
		this.oldElementValue = this.element.value;
	},
	getToken : function() {
		var bounds = this.getTokenBounds();
		return this.element.value.substring(bounds[0], bounds[1]).strip();
	},
	getTokenBounds : function() {
		if (null != this.tokenBounds)
			return this.tokenBounds;
		var value = this.element.value;
		if (value.strip().empty())
			return [ -1, 0 ];
		var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
		var offset = (diff == this.oldElementValue.length ? 1 : 0);
		var prevTokenPos = -1, nextTokenPos = value.length;
		var tp;
		for ( var index = 0, l = this.options.tokens.length; index < l; ++index) {
			tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
			if (tp > prevTokenPos)
				prevTokenPos = tp;
			tp = value.indexOf(this.options.tokens[index], diff + offset);
			if (-1 != tp && tp < nextTokenPos)
				nextTokenPos = tp;
		}
		return (this.tokenBounds = [ prevTokenPos + 1, nextTokenPos ]);
	}
});
ClavisAutoCompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
	var boundary = Math.min(newS.length, oldS.length);
	for ( var index = 0; index < boundary; ++index)
		if (newS[index] != oldS[index])
			return index;
	return boundary;
};
Ajax.ClavisAutoCompleter = Class.create(ClavisAutoCompleter.Base, {
	initialize : function(element, surround, update, url, options) {
		this.baseInitialize(element, surround, update, options);
		this.options.asynchronous = true;
		this.options.onComplete = this.onComplete.bind(this);
		this.options.defaultParams = this.options.parameters || null;
		this.url = url;
	},
	getUpdatedChoices : function() {
		this.startIndicator();
		var entry = encodeURIComponent(this.options.paramName) + '=' + encodeURIComponent(this.getToken());
		this.options.parameters = this.options.callback ? this.options.callback(this.element, entry) : entry;
		if (this.options.defaultParams)
			this.options.parameters += '&' + this.options.defaultParams;
		if (autoCompleteRequest != null) {
			autoCompleteRequest.transport.abort();
		}
		autoCompleteRequest = new Ajax.Request(this.url, this.options);
	},
	onComplete : function(request) {
		this.updateChoices(request.responseText);
		autoCompleteRequest = null;
	}
});
