MooVents = {}

/**
 * Diese Klasse simuliert einen Blur Event für Elemente die keine Eingabefelder sind.
 *
 * @author Peter Hartwig
 */
MooVents.BlurEvent = new Class({

	/**
	 * Setzt alle Globalen Variablen zurück
	 */
	initialize: function() {
		this.notFocusFunc = function(){}
		this.parents = new Array();
		this.bodyFunc = null;
		this.body = Browser.ie ? $(document.body) : $(window);
	},

	/**
	 * Setzt die beiden globalen Variablen parents und notFocusFunc
	 *
	 * @param array parents Array mit Element-Objekten
	 * @param function notFocusFunc Diese Funktion wird aufgerufen, wenn der Blur ausserhalb des Elements geklickt wurde
	 */
	setGlobals: function(parents, notFocusFunc) {
		this.parents = parents;
		this.notFocusFunc = notFocusFunc;
	},

	/**
	 * Aktiviert den Blur Event
	 *
	 * @param bool withDeactivation Zeigt ob die Deaktivierungsfunktions bei einem Blur aufgerufen werden soll
	 */
	activate: function(withDeactivation) {
		this.bodyFunc = function(ev) {
			var elm = (ev.target) ? ev.target : ev.srcElement;
			var elmIsParent = this.checkIfElmIsInParent(elm);
			if(!elmIsParent) {
				this.notFocusFunc();
				if(withDeactivation) this.deactivate();
			}
		}.bind(this);
		this.body.addEvent('click', this.bodyFunc);
	},

	/**
	 * Prueft, ob das uebergebene Element elm, ein Kind oder das Element ist. Bei Erfolg wird ein true zurueckgegeben.
	 *
	 * @param Element elm Das zu pruefende Element
	 * @param Element parent Das Elternelement
	 *
	 * @return bool
	 */
	checkIfElmIsInParent: function(elm) {
		var i = 5000;
		while(elm != undefined) {
			for(var p = 0; p < this.parents.length; p++) {
				if(elm == this.parents[p]) return true;
			}
			elm = elm.parentNode;
			if(i == 0) return false;
			i--;
		}
		return false;
	},

	/**
	 * Deaktiviert den Blur Event
	 */
	deactivate: function() {
		if(this.bodyFunc !== null) {
			this.body.removeEvent('click', this.bodyFunc);
		}
	}
});

MooVents.Autocompleter = new Class({
	
	Implements: [Events, Options],
	
	initialize: function(options) {
		this.options = {
			blurs: new Hash(),
			inputFunc: function(){},
			jsonRequest: false,
			autocompleterOpened: false,
			selected: -1,
			width: 250
		}
		
		this.elements = {
			autocompleter: null,
			inputs: new Hash(),
			parent: $(options.parent),
			selects: new Hash()
		}
		delete options.parent;
		
		this.setOptions(options);
		this.generateAutocompleter();
	},
	
	addInputs: function(inputs) {
		inputs.each(function(input) {
			this.addInput(input);
		}.bind(this));
	},
	
	addInput: function(input) {
		var _self = this;
		this.elements.inputs.set(input.element.get('id'), new Hash(input));
		input.element.keyup = function (){return false;}
		input.element.addEvent('keyup', function(event) {
			if(event.key != 'left' && event.key != 'right' && !event.shift && !event.control && !event.meta && !event.alt) {
				if(event.key == 'enter') {
					if(_self.options.opened) {
						if(!_self.setResult(this.get('id'))) {
							_self.elements.parent.submit();
						}
					} else {
						_self.elements.parent.submit();
					}
				} else if(event.key == 'up' || event.key == 'down') {
					if(_self.options.opened) {
						var count = _self.elements.selects.getLength();
						var select = _self.options.selected;
						if(select > -1) {
							_self.elements.selects.get(select).removeClass('selected');
						}
						if(event.key == 'down') {
							select++;
							if(!_self.elements.selects.has(select)) {
								select = 0;
							}
						} else if(event.key == 'up') {
							select--;
							if(!_self.elements.selects.has(select)) {
								select = count-1;
							}
						}
						_self.elements.selects.get(select).addClass('selected');
						_self.options.selected = select;
						new Fx.Scroll(_self.elements.autocompleter).toElement(_self.elements.selects.get(select), 'y');
					}
				} else if(event.key == 'esc' || event.key == 'tab') {
					if(_self.options.opened) {
						_self.close();
					}
				} else {
					_self.loadRequest(this.get('id'));
				}
			}
			return false;
		});
		var blur = new MooVents.BlurEvent();
		blur.setGlobals([input.element], this.close.bind(this));
		this.options.blurs.set(input.element.get('id'), blur);
	},
	
	generateAutocompleter: function() {
		this.elements.autocompleter = new Element('div',{
			'class': 'autocompleterContainer',
			styles: {
				width : this.options.width
			}
		}).inject(this.elements.parent);		
	},
	
	loadRequest: function(id) {
		this.elements.autocompleter.setStyle('display', 'none');
		if(this.elements.inputs.get(id).get('element').get('value').length < 3) return;
		if(this.options.jsonRequest) this.options.jsonRequest.cancel();
		var value = this.elements.inputs.get(id).get('element').get('value');
		
		this.options.jsonRequest = new Request.JSON({
			url: this.elements.inputs.get(id).get('url'),
			onSuccess: function(data) {
				this.generateSelects(id, data);
			}.bind(this)
		});
		this.options.jsonRequest.post({value:value});
	},
	
	generateSelects: function(id, data) {
		this.elements.autocompleter.empty();
		this.options.jsonRequest = false;
		if(!data || data.length < 1) return;
		
		var _self = this;
		var number = -1;
		this.options.selected = -1;
		
		data.each(function(result) {
			if(result[this.elements.inputs.get(id).get('ident')] && result[this.elements.inputs.get(id).get('ident')].trim().length > 0) {
			    var reg = new RegExp('('+$(id).get('value')+')','gi');
				var select = new Element('div', {
					'class': 'aSelect',
					html: result[this.elements.inputs.get(id).get('ident')].trim().replace(reg,'<b>$1</b>')
				});
				number++;
				if(number%2 == 0) {
					select.addClass('lightGray');
				}
				select.addEvents({
					'click': function() {
						this.setResult(id);
					}.bind(this),
					'mouseenter': function(number) {
						if(_self.options.selected > -1) {
							_self.elements.selects.get(_self.options.selected).removeClass('selected');
						}
						this.addClass('selected');
						_self.options.selected = number;
					}.bind(select, number),
					'mouseleave': function() {
						this.removeClass('selected');
						_self.options.selected = -1;
					}
				});
				this.elements.autocompleter.adopt(select);
				this.elements.selects.set(number, select);
			}
		}.bind(this));
		
		this.show(id);
	},
	
	setResult: function(id) {
		if(this.options.selected < 0) return false;
		this.elements.inputs.get(id).get('element').set('value', this.elements.selects.get(this.options.selected).get('html').trim().replace(/\[.*\]/g,'').replace(/<\/?(?!\!)[^>]*>/gi, ''));
		this.elements.inputs.get(id).get('element').focus();
		this.close();
		return true;
	},
	
	close: function() {
		this.elements.autocompleter.setStyle('display', 'none');
		this.options.opened = false;
	},
	
	show: function(id) {
		if(this.elements.autocompleter.getChildren().length > 0) {
			this.elements.autocompleter.setStyles({
				display: 'block',
//				left: this.elements.inputs.get(id).get('left'),
//				top: this.elements.inputs.get(id).get('top'),
				width: this.elements.inputs.get(id).get('width')
			});
			this.elements.autocompleter.scrollTo(0, 0);
			this.options.opened = true;
			var blur = this.options.blurs.get(this.elements.inputs.get(id).get('element').get('id'));
			blur.activate(true);
		}
	}
	
});
