/**
 * Funktionen zum zeigen der Vorschläge.
 */
var BranchSuggest = Class.create();

/**
 * Das Element für das die Vorschläge geholt werden.
 * 
 * @var HTML InputElement
 */
BranchSuggest.prototype._element = undefined;

/**
 * Ids der gefundenen Branchen, die genau dem Suchbegriff entsprechen.
 * 
 * @var {Array}
 */
BranchSuggest.prototype._exact = new Array;

/**
 * Funktion die bei einem Ereignis aufgerufen wird.
 * 
 * @var Function
 */
BranchSuggest.prototype._observeFunction = undefined;

/**
 * Das aktuelle AJAX Objekt.
 * 
 * @var	XMLHttpRequest
 */
BranchSuggest.prototype._request = undefined;

/**
 * Speichert ob ein Vorschlag ausgewählt wurde und seitdem keine Änderung im
 * Eingabefeld vorgenommen wurde. (onChange wird teilweise auch ausgelöst, wenn
 * ein Vorschlag ausgewählt wurde.)
 * 
 * @var	boolean
 */
BranchSuggest.prototype._selected = true;

/**
 * Objekt mit den aktuellen Vorschlägen.
 * 
 * @var	Object
 */
BranchSuggest.prototype._suggestions = {};

/**
 * Anzahl der Vorschläge, die in der Auswahlliste hinzugefügt wurden.
 * 
 * @var	Integer
 */
BranchSuggest.prototype._suggestionsCount = 0;

/**
 * Timeout der Ajax Abfrage.
 * 
 * @var	Timeout
 */
BranchSuggest.prototype._timeout = undefined;

/**
 * Url an die die Abfragen gesendet werden.
 * 
 * @type {String}
 */
BranchSuggest.prototype._url = 'branchs.php';

/**
 * Hinweistext, wenn nicht alle Branchen angezeigt werden.
 * 
 * @var {String}
 */
BranchSuggest.prototype.textMore = '...';



/**
 * Fügt die Vorschlags Funktionen bei einem Element hinzu.
 * 
 * @param	mixed	element	Das Element oder die Id des Elements für das die
 * 							Vorschläge geholt werden.
 */
BranchSuggest.prototype.initialize = function(element) {
	if (typeof element == 'string') {
		element = $(element);
	}
	element.setAttribute('autocomplete','off');
	this._element = element;

	this._observeFunction = this.action.bind(this);
	this.observeEvents(true);
}

/**
 * Startet oder stopt die Ereignisüberwachung.
 * 
 * @param	boolean	observe	Ob die Ereignisse Überwacht werden oder nicht.
 */
BranchSuggest.prototype.observeEvents = function(observe) {
	var action;
	if (observe) {
		action = 'observe';
	} else {
		action = 'stopObserving';
	}
	Event[action](this._element, 'change', this._observeFunction);
	Event[action](this._element, 'keyup', this._observeFunction);
	Event[action](this._element, 'keydown', this._observeFunction);
	Event[action](this._element, 'blur', this._observeFunction);
}

/**
 * Leitet die Ereignisse an die entsprechende Suggest Methode weiter.
 * 
 * @param	Event	action	Event Objekt, des Ereignisses, das ausgelöst wurde.
 */
BranchSuggest.prototype.action = function(action) {
	switch (action.type) {
		case 'keyup': case 'keydown':
			this._key(action);
			break;

		case 'blur':
			this._hide();
			break;

		case 'change':
			this._change();
			break;
	}
}

/**
 * Ermittelt ob eine Branche eingegeben wurde und wählt diese aus.
 */
BranchSuggest.prototype._change = function() {
	if (!this._selected) {
		if (this._element.value.length <= 1) {
			this._remove();
		} else {
			if (this._request != undefined) {
				this._request.transport.abort();
			}
			var params = $H(
				{
					search: this._element.value,
					action: 'getSuggestion'
				}
			);
			this._request = new Ajax.Request(
				this._url,
				{
					method: 'get',
					parameters: params.toQueryString(),
					onComplete: this.changeSelect.bind(this)
				}
			);
		}
	}
}

/**
 * Löscht die aktuelle Branche.
 */
BranchSuggest.prototype._remove = function() {
	var number = this._element.id.replace(/\D+/, '');
	branchs.remove(number);
};

/**
 * Ermittelt ob eine Branche eingegeben wurde und wählt diese aus.
 * 
 * @param	string	request		Die Ausgabe der Abfrage.
 * @param	Hash	suggestions	Assoziazives Array mit den gefundenen
 * 								Vorschlägen.
 */
BranchSuggest.prototype.changeSelect = function(request, suggestions) {
	var suggestionsArray = $H(suggestions).toArray();
	if (
		suggestionsArray.length == 1 && (
			suggestionsArray.first().value.sub == undefined ||
			$H(suggestionsArray.first().value.sub).toArray().length <= 1
		)
	) {
		this.show(request, suggestions);
		if (
			suggestionsArray.first().value.sub != undefined &&
			$H(suggestionsArray.first().value.sub).toArray().length == 1
		) {
			this._highlight('nextSibling');
		}
		this.select();
	} else {
		this._remove();
	}
};

/**
 * Ermittelt welche Taste gedrückt wurde und ruft die entsprechende Methode auf.
 * 
 * @param	Event	action	Event Objekt, des Ereignisses, das ausgelöst wurde.
 */
BranchSuggest.prototype._key = function(action) {
	switch (action.keyCode) {
		case Event.KEY_LEFT: case Event.KEY_RIGHT: case 16/*shift*/:
        case Event.KEY_HOME: case Event.KEY_END:
			break;

		case Event.KEY_DOWN:
			if (action.type == 'keydown') {
				this._highlight('nextSibling');
			}
			break;

		case Event.KEY_UP:
			if (action.type == 'keydown') {
				this._highlight('previousSibling');
			}
			break;

		case Event.KEY_RETURN:
			/* 
			 * In Opera funktioniert Event.stop nicht.
			 */
			Event.stop(action);
			/* 
			 * Hierdurch wird der Event im Opera eher weitergeleitet, so dass
			 * der Speichern Button noch deaktiviert ist.
			 */
			window.setTimeout(function() {}, 0);
			this.select();
			break;

		case Event.KEY_TAB:
			if (action.type == 'keydown') {
				this.select();
			}
			break;

		default:
			if (action.type == 'keyup') {
				if (this._timeout != undefined) {
					window.clearTimeout(this._timeout);
				}
				this._timeout = window.setTimeout(this._get.bind(this), 300);
			}
			this._selected = false;
			break;
	}
};

/**
 * Markiert einen Eintrag in der Liste.
 * 
 * @param	String	sibling	Ob der nächste oder der vorherige Eintrag markiert
 * 							wird.
 */
BranchSuggest.prototype._highlight = function(sibling) {
	var suggestions = document.getElementsByClassName('SuggestionActive');
	if (
		suggestions.length > 0 &&
		suggestions[0][sibling] != undefined &&
		suggestions[0][sibling].nodeType == 1
	) {
		suggestions[0].className = '';
		suggestions[0][sibling].className = 'SuggestionActive';
	}
}

/**
 * Markiert einen Eintrag mit der Maus in der Liste.
 * 
 * @param	Event	action	Das Ereignis, das ausgelöst wurde.
 */
BranchSuggest.prototype.highlight = function(action) {
	var suggestions = document.getElementsByClassName('SuggestionActive');
	if (suggestions.length > 0) {
		suggestions[0].className = '';
	}
	Event.element(action).className = 'SuggestionActive';
}

/**
 * Übernimt einen Vorschlag in das Auswahlfeld.
 */
BranchSuggest.prototype.select = function() {
	var suggestions = document.getElementsByClassName('SuggestionActive');
	if (suggestions.length > 0) {
		this.observeEvents(false);

		this._remove();

		var index = suggestions[0].id.substr(10);
		if (/Sub/.test(index)) {
			var indexSub = index.split('Sub')[1];
			index = index.split('Sub')[0];
		}

		this._hide();

		if (this._check(index)) {
			this._assignBranch(index, indexSub);
			this._selected = true;
		}
		this.observeEvents(true);
	}
}

/**
 * Überträgt eine ausgewählte Branche in das Eingabefeld.
 * 
 * @param	integer	branchId	Die Id der Branche.
 * @param	integer	subBranchId	Die Id der Unterbranche.
 */
BranchSuggest.prototype._assignBranch = function(branchId, subBranchId) {
	if (this._suggestions[branchId].alias != undefined) {
		this._element.value = this._suggestions[branchId].alias.name;
		$(this._element.id + 'id').value = this._suggestions[branchId].alias.id;
	} else {
		this._element.value = this._suggestions[branchId].name;
		$(this._element.id + 'id').value = branchId;
	}

	var number = this._element.id.replace(/\D+/, '');
	if (subBranchId != undefined) {
		branchs.subBranchs[branchId] = {};
		branchs.subBranchs[branchId][subBranchId] = this._suggestions[branchId].sub[subBranchId];
		branchs.addSub(number, subBranchId);
		if ($H(branchs.subBranchs[branchId]).toArray().length == 1) {
			branchs.subBranchs[branchId] = undefined;
		}
	}

	branchs.showAlias(number);
	branchs.showSubs();
}

/**
 * Überprüft ob die Branche ausgewählt werden kann.
 * 
 * @param	integer	branchId	Die Id der Branche.
 * 
 * @return	boolean	Ob die Branche ausgewählt werden kann.
 */
BranchSuggest.prototype._check = function(branchId) {
	if (branchs.checkDuplicate(branchId)) {
		/* 
		 * Wenn alert direkt aufgerufen wird, wird der Event nicht gestoppt
		 * und das Formular gesendet.
		 */
		window.setTimeout(
			function() {
				alert(branchs.msgDuplicate);
			},
			0
		);

		return false;
	} else {
		return true;
	}
}

/**
 * Holt die Vorschläge sobald genügend Zeichen eingegeben wurden.
 */
BranchSuggest.prototype._get = function() {
	if (this._element.value.length > 2) {
		var params = $H(
			{
				search: this._element.value,
				action: 'getSuggestion'
			}
		);
		if (this._request != undefined) {
			this._request.transport.abort();
		}
		this._request = new Ajax.Request(
			this._url,
			{
				method: 'get',
				parameters: params.toQueryString(),
				onComplete: this.show.bind(this)
			}
		);
	} else {
		this.show(undefined, {});
	}
}

/**
 * Gibt die Vorschläge aus.
 * 
 * @param	string	request		Die Ausgabe der Abfrage.
 * @param	Hash	suggestions	Assoziazives Array mit den gefundenen
 * 								Vorschlägen.
 */
BranchSuggest.prototype.show = function(request, suggestions) {
	this._suggestions = suggestions;
	if ($H(suggestions).toArray().length == 0) {
		this._hide();
	} else {
		this._clear();
		this._suggestionsCount = 0;
		$H(suggestions).each(this.add.bind(this));
		$('Suggestions').firstChild.className = 'SuggestionActive';
		if (this._suggestionsCount > 20) {
			new Insertion.Bottom('Suggestions', this.textMore);
		}
	}
}

/**
 * Blendet die Vorschläge aus.
 */
BranchSuggest.prototype._hide = function() {
	if (this._timeout != undefined) {
		window.clearTimeout(this._timeout);
	}
	if ($('Suggestions') != undefined) {
		Element.remove('Suggestions');
	}
}

/**
 * Erstellt die Liste, wenn sie nicht vorhanden ist und entfernt die Vorschläge
 * aus ihr.
 */
BranchSuggest.prototype._clear = function() {
	
	if ($('Suggestions') == undefined) {			
		
		
		// Get width and height of current browser viewport (active part of the window) 
		var viewport = getViewport();		
		
		// Viewport height	
		var viewport_height = viewport.height;
		
		// Viewport width	
		var viewport_width = viewport.width;
		
		//var e = document.getElementById(this._element.id);
		var e = document.getElementById('branchSelect');
			
		var x = jQuery(e).offset().left;
	
		var y = jQuery(e).offset().top;
		
		var element_height = jQuery(e).height();
		
		var sugg = '<div id="Suggestions"></div>';		
		
		jQuery("body").append(sugg);			
		
		Event.observe(
			$('Suggestions'),
			'mouseover',
			function() {
				this.observeEvents(false);
			}.bind(this)
		);
		Event.observe(
			$('Suggestions'),
			'mouseout',
			function() {
				this.observeEvents(true);
			}.bind(this)
		);		
		
		var mask_height = 450;		

		y += 70;
		//x += 467;  			
		
		jQuery("#Suggestions").attr('style','position:absolute;left:' + x +'px;top:'+ y+'px;');
	}

	if ($('Suggestions').childNodes.length > 0) {
		$A($('Suggestions').childNodes).each(
			function(child) {
				$('Suggestions').removeChild(child);
			}
		);
	}
}

/**
 * Fügt einen Eintrag der Auswahlliste hinzu.
 * 
 * @param	Object	branch	Assoziatives Array des aktuellen Eintrags mit der Id
 * 							unter key und dem Wert unter value.
 */
BranchSuggest.prototype.add = function(branch) {
	this._suggestionsCount++;

	if (
        this._suggestionsCount <= 20 - this._exact.length ||
        this._exact.indexOf(branch.key) != -1 ||
        this._exact.indexOf(branch.key.replace(/^\d+Sub/, '')) != -1
    ) {
        if (this._exact.indexOf(branch.key) != -1 || this._exact.indexOf(branch.key.replace(/^\d+Sub/, '')) != -1) {
            this._suggestionsCount--;
        }

    	var value = branch.value.name;
		if (branch.value.alias != undefined) {
			value = branch.value.alias.name + ' -> ' + value;
		}

		new Insertion.Bottom('Suggestions', '<div id="Suggestion' + branch.key + '">' + value + '</div>');
		Event.observe(
			$('Suggestion' + branch.key),
			'click',
			this.select.bind(this)
		);
		Event.observe(
			$('Suggestion' + branch.key),
			'mouseover',
			this.highlight.bind(this)
		);

		if (branch.value.sub != undefined && $H(branch.value.sub).toArray().length > 0) {
			$H(branch.value.sub).each(
				function(sub) {
					var subBranch = {
						key: branch.key + 'Sub' + sub.key,
						value: {
							name: '-> ' + sub.value
						}
					};
					this.add(subBranch);
				}.bind(this)
			);
		}
	}
}

/* get viewport dimensions*/
function getViewport() {
	
        var htmlElement = document.getElementsByTagName('html')[0];
        var width = htmlElement.clientWidth;
        var height = htmlElement.clientHeight;

        if (window.opera) {
            height = window.innerHeight;
            width = window.innerWidth;
            
            // Substract scrollbars
            if (htmlElement.clientWidth > width) {
                height -= 16;
            }
            if (htmlElement.clientHeight > height) {
                width -= 16;
            }
        } 

        return { width:  width, height: height };
}