/**
 * @author hohercak
 */

/* ********************************** */
/*           SuggestInput             */
/* ********************************** */

  	function SuggestInput(inputId, group, config){
		
		/**
		 * Zavoláme konstruktor předka
		 */
		EventController.apply(this, arguments);

		/**
		 * Konfigurace.
		 */
		this.config = config;

		/**
		 * Skupina 
		 */	    
		this.group = group;
		
		/**
	     * reference na INPUT element
	     */
		this.input = null;
		if(inputId){
		   	this.input = this.associateInput(inputId);
		}
		
	}
		
	SuggestInput.prototype = new EventController;

  	/**
  	 * propojeni naseptavace na input element.
  	 * 
  	 * @param {Object} inputId
  	 */
  	SuggestInput.prototype.associateInput = function(inputId){
   		return document.getElementById(inputId);
  	};
  
  	/**
  	 * Validuje data, jestli jsou poplatna k datum v input
  	 * boxech.
  	 * 
  	 * @param {Object} data Vrati true kdyz data ze serveru 
  	 * odpovidaji datum v INPUTech skupiny (uzivatel nic nezmenil, 
  	 * jinak jsou data neplatna).
  	 * 
  	 */
  	SuggestInput.prototype.isDataExpired = function(data) {
  		dataValue = (data[this.getInputId()])?data[this.getInputId()]:"";
    	return !data || (dataValue != this.getInputValue());
  	};

  	SuggestInput.prototype.getInputId = function() {
		return this.input.id;
  	};

  	SuggestInput.prototype.getInputValue = function() {
		return this.input.value;
  	};

/* ********************************** */
/*           SuggestConfig            */
/* ********************************** */
  	
	/**
	 * Implicitni konfigurace naseptavace
	 * 
	 */
	function SuggestConfig(){
		
		// parent containers to incerase z-index
		//this.inputContainers = null;
		
		//minimalni delka vstupu pro odesilani requestu
		this.minInputLength = 1;
		
		// CSS styly
		this.suggestBoxCssId = null;
		this.suggestBoxCssClass = "suggest-box";
		this.suggestLinesBoxCssId = null;
		this.suggestLinesBoxCssClass = "suggest-lines-box";
		this.suggestLineCssClass = null;
		this.suggestLineHighlightCssClass = "suggest-line-highlight";
	}
  
/* ********************************** */
/*           Suggest                  */
/* ********************************** */

  	function Suggest(inputId, group, config){
		
		SuggestInput.apply(this, arguments);

		/**
		 * napoveda zapnuta/vypnuta
		 */		
		this.isOn = true;

		/**
		 * 
		 */	
	   	this.requestDataTimer = null;
		
		/**
		 * 
		 */
	   	this.hideTimer = null;
		
		/**
		 * 
		 */
		this.isVisible = false;

		/**
		 * 
		 */
		this.inputHasFocus = false;
		
		/**
		 * 
		 */
		this.suggestBoxHasFocus = false;
		
	   	/**
	   	 * vizualni cast naseptavace
	   	 */
		this.suggestBox = null;
		if(this.input){
		   	this.suggestBox = this.getSuggestBox(config, this);
		}
		
		if(this.input && this.suggestBox){
			this.insertIntoDocument();
		   	this.assignInputEvents();
		   	this.turnOn(this.isOn);
		}

  	}
  	
	Suggest.prototype = new SuggestInput;
	
	//Suggest.Z_INDEX_DELTA = 100;

  	Suggest.prototype.setInputValue = function(value) {
		this.input.value = value;
  	};
  	
	Suggest.prototype.getSuggestBox = function(config, eventController){
		return new SuggestBox(config, this);
	};
	
  	Suggest.prototype.hasFocus = function(){
   		return this.inputHasFocus || this.suggestBoxHasFocus;
  	};
  
  	/**
  	 * 
  	 */
	Suggest.prototype.insertIntoDocument = function(){
		document.body.appendChild(this.suggestBox.element);
	};
	
	Suggest.prototype.unselectLine = function(){
		this.suggestBox.unselectLine();
	};
	
	Suggest.prototype.selectLine = function(lineElement){
		this.suggestBox.selectLine(lineElement);
	};

	Suggest.prototype.selectNextLine = function(){
		this.suggestBox.selectNextLine();
	};

	Suggest.prototype.selectPreviousLine = function(){
		this.suggestBox.selectPreviousLine();
	};

	Suggest.prototype.putLineValue = function(lineElement){
		this.input.value = lineElement.firstChild.data;
		this.input.focus();
		this.input.select();
		this.hide();
	};

	Suggest.prototype.putSelectedLineValue = function(){
		var lineElement = this.suggestBox.getSelectedLine();
		if(lineElement){
			this.putLineValue(lineElement);
			return true;
		}
		return false;
	};

	/**
	 * naplanuje skryti se zadanym zpozdenim
	 */
  	Suggest.prototype.scheduleHide = function(delay){
		window.clearTimeout(this.hideTimer);
      	this.hideTimer = null;
		var suggest = this;
		this.hideTimer = window.setTimeout(function() {
			suggest.hide();	
		}, delay);
	};
	
	/**
	 * zrusi naplanovane skryti
	 */
  	Suggest.prototype.cancelHide = function(delay){
		window.clearTimeout(this.hideTimer);
      	this.hideTimer = null;
	};
	
  	/**
  	 * Skryje naseptavac.
  	 * 
  	 */
  	Suggest.prototype.hide = function(){
	    this.unselectLine();
		//if(this.isVisible && this.config && this.config.inputContainers) {
		//	for(var i = 0; i < this.config.inputContainers.length; i+=1) {
		//		this.config.inputContainers[i].style.zIndex -= Suggest.Z_INDEX_DELTA;
		//	}
		//}
		this.suggestBox.hide();
		this.isVisible = false;
  	};

  	/**
  	 * Zobrazi naseptavac.
  	 * 
  	 */
  	Suggest.prototype.show = function(){
		//if(!this.isVisible && this.config && this.config.inputContainers) {
		//	for(var i = 0; i < this.config.inputContainers.length; i+=1) {
		//		this.config.inputContainers[i].style.zIndex += Suggest.Z_INDEX_DELTA;
		//	}
		//}
		
		this.suggestBox.show();
		this.isVisible = true;
  	};

  	/**
  	 * nastavi funkce, ktere maji zpracovavat
  	 * jednotlive udalosti.
  	 *  
  	 */
  	Suggest.prototype.assignInputEvents = function() {
    
		var suggest = this;
		
    	// ******* FOKUS *****
    	this.registerEvent(this.input, EventController.BLUR, 
			function(e){
				suggest.inputHasFocus = false;
				if(suggest.isOn){
					suggest.processFocus();
				}
			}, false);
			  
    	this.registerEvent(this.input, EventController.FOCUS, 
			function(e){
				suggest.inputHasFocus = true;
				if(suggest.isOn){
					suggest.processFocus();
				}
			}, false);
			  
    	// ******* KLAVESNICE ********
    	this.registerEvent(this.input, EventController.KEYDOWN,
			function(e){
				if(!suggest.isOn){
					return true;
				}
				if(!e){ //IE
					e = window.event;
				}
				return suggest.inputKeydown(e);
			} , false);  
		
    	this.registerEvent(this.input, EventController.KEYUP,
			function(e){
				if(!suggest.isOn){
					return true;
				}
				if(!e){ //IE
					e = window.event;
				}
				suggest.inputKeyup(e);
			} , false);  
  	};

  	Suggest.prototype.processFocus = function(e) {
  		if(this.hasFocus()){
  			this.cancelHide();
  		} else {
			this.scheduleHide(300);
  		}
  	};
  	
  	Suggest.prototype.inputKeydown = function(e) {
    	var keyId = Keyboard.getKey(e);
		
		//FIXMR zabranit uvozovky
		
		// NAHORU
    	if (keyId == Keyboard.UP_ARROW){
      		return this.inputUpArrowPressed();
    	}
     
     	// DOLU
    	if (keyId == Keyboard.DOWN_ARROW){
      		return this.inputDownArrowPressed();
    	}
		
		// ENTER 
		if (keyId == Keyboard.ENTER) {
	  		return this.inputEnterPressed(e);
	  	} 
		
		//DOPRAVA
      	if (keyId == Keyboard.RIGHT_ARROW) { 
			return this.inputRightArrowPressed(e);
        }
    
		//DOLEVA
      	if (keyId == Keyboard.LEFT_ARROW) { 
			return this.inputLeftArrowPressed(e);
        }
    
		// ESCAPE
	  	if (keyId == Keyboard.ESCAPE) { 
			return this.inputEscapePressed(e);
		} 
	};

  	Suggest.prototype.inputKeyup = function(e) {
    	var keyId = Keyboard.getKey(e);
		
		if(keyId == Keyboard.BACKSPACE || keyId == Keyboard.DELETE){
		  	this.scheduleDataRequest(600);
		} else if( keyId != Keyboard.TAB
				&& keyId != Keyboard.ENTER
				&& keyId != Keyboard.SHIFT
				&& keyId != Keyboard.CTRL
				&& keyId != Keyboard.ALT
				&& keyId != Keyboard.PAUSE
				&& keyId != Keyboard.CAPS_LOCK
				&& keyId != Keyboard.ESCAPE
				&& keyId != Keyboard.PAGE_UP
				&& keyId != Keyboard.PAGE_DOWN
				&& keyId != Keyboard.END
				&& keyId != Keyboard.HOME
				&& keyId != Keyboard.LEFT_ARROW
				&& keyId != Keyboard.UP_ARROW
				&& keyId != Keyboard.RIGHT_ARROW
				&& keyId != Keyboard.DOWN_ARROW
				&& keyId != Keyboard.INSERT
				&& keyId != Keyboard.LEFT_WINDOW_KEY
				&& keyId != Keyboard.RIGHT_WINDOW_KEY
				&& keyId != Keyboard.SELECT_KEY
				&& keyId != Keyboard.F1
				&& keyId != Keyboard.F2
				&& keyId != Keyboard.F3
				&& keyId != Keyboard.F4
				&& keyId != Keyboard.F5
				&& keyId != Keyboard.F6
				&& keyId != Keyboard.F7
				&& keyId != Keyboard.F8
				&& keyId != Keyboard.F9
				&& keyId != Keyboard.F10
				&& keyId != Keyboard.F11
				&& keyId != Keyboard.F12
				&& keyId != Keyboard.NUM_LOCK
				&& keyId != Keyboard.SCROLL_LOCK){
		  	this.scheduleDataRequest(800);
		}
	};

  	Suggest.prototype.inputUpArrowPressed = function(e) {
		if(this.isVisible){
			this.selectPreviousLine();
			return false;
		}
		return true;
	};
	
  	Suggest.prototype.inputDownArrowPressed = function(e) {
		if(this.isVisible){
			this.selectNextLine();
			return false;
		}
		return true;
	};
	
  	/**
  	 * 
  	 * @param {Object} e
  	 */
  	Suggest.prototype.inputRightArrowPressed = function(e) {
        if(this.isVisible && this.putSelectedLineValue()){ 
          return false;
        }
		return true;
	};

  	/**
  	 * 
  	 * @param {Object} e
  	 */
  	Suggest.prototype.inputLeftArrowPressed = function(e) {
        if(this.isVisible && this.suggestBox.getSelectedLine()){
			this.unselectLine(); 
          	return false;
        }
		return true;
	};
		
  	/**
  	 * 
  	 * @param {Object} e
  	 */
  	Suggest.prototype.inputEnterPressed = function(e) {
        if(this.isVisible){
	        this.putSelectedLineValue(); 
			return false;
        }
		return true;
	};
		
  	/**
  	 * 
  	 * @param {Object} e
  	 */
  	Suggest.prototype.inputEscapePressed = function(e) {
		if(this.isVisible){
			this.hide();
			return false;
		}
		return true;
	};

	/**
	 * 
	 * @param {Object} data
	 */
  	Suggest.prototype.isDataValid = function(data) {
		if(!this.group){
			return !this.isDataExpired(data);
		}
		for(var suggest in this.group){
      		if(this.group[suggest].isDataExpired(data)){
        		return false;
    	  	}
    	}
		return true;
	};

	/**
	 * 
	 * @param {Object} data
	 */
  	Suggest.prototype.isDataEmpty = function(data) {
		return !data || !data.list || (data.list.length == 0); 
	};

	/**
	 * 
	 */
  	Suggest.prototype.scheduleDataRequest = function(delay, overWrite){
  		if(overWrite === false && this.requestDataTimer !== null) {
  			return;
  		}
		window.clearTimeout(this.requestDataTimer);
      	this.requestDataTimer = null;
	    this.unselectLine();
		var suggest = this;
		this.requestDataTimer = window.setTimeout(function() {
			if(suggest.realyDoRequest()){
				if(suggest.requestData){
					suggest.requestData();
				}
			} else {
				suggest.hide();
			}
			
			suggest.requestDataTimer = null;
		}, delay);
	};
	

  	Suggest.prototype.realyDoRequest = function(){
		if(this.config){
			if (this.getInputValue().length < this.config.minInputLength){
				return false;	
			}
		}
  		return true;
  	};

	// NUTNO implementovat
 	// Suggest.prototype.requestData = function(){}

  	/**
  	 * 
  	 * @param {Object} data
  	 */
	Suggest.prototype.setData = function(data) {
		if(this.isDataValid(data)) {
			this.suggestBox.setData(data);
			if (this.isDataEmpty(data)){
				this.hide();
			}
			if(!this.isDataEmpty(data) && this.hasFocus()){
				this.show();
			}
		}
  	};
  	
	/**
	 * 
	 */
  	Suggest.prototype.clear = function() {
  		this.suggestBox.clear();
	};

	/**
	 * 
	 */
  	Suggest.prototype.turnOn = function(on) {
  		if(on){
	  		this.isOn = true; 
			this.input.setAttribute("autocomplete", "off");
  		} else {
	  		this.isOn = false; 
  			this.hide();
			this.input.removeAttribute("autocomplete");
  		}
	};

	
	Suggest.prototype.scrollToLine = function(element) {
		var parent = element.offsetParent;
		var delta = element.offsetTop - parent.scrollTop;
		if( delta < 0){
			parent.scrollTop += delta;
		}
		if( delta > (parent.clientHeight  - element.offsetHeight)){
			parent.scrollTop += delta - (parent.clientHeight  - element.offsetHeight);
		}
	};
	
	
	Suggest.prototype.getSuggestPosition = function() {
		var x = 0;
		var y = 0;
		var obj = this.input;
		
		if(obj.offsetParent) {
			while (obj) {
				x += obj.offsetLeft;
				y += obj.offsetTop;
				obj = obj.offsetParent;
			}
		} else {
			x = obj.x;
			y = obj.y;
		}
		
		y += 1 + this.input.offsetHeight;
		
		return [x, y];
	};
/* ********************************** */
/*           SuggestBox               */
/* ********************************** */

	/**
	 * Konstruktor.
	 * 
	 */
	function SuggestBox(config, eventController){
		this.config = config;
		this.eventController = eventController;
		if(this.eventController){
	    	this.lines = this.getSuggestLinesBox(config, eventController);
    		this.element = this.createElement(this.lines.element);
		}
  	};
  	
	SuggestBox.prototype.createElement = function(linesElement){
		// IE fix aby suggest sel i pres prvek SELECT musime
		// "podlozit" prvkem IFRAME

		
		var useIframe = false;
		if(document.all && document.getElementById && !window.Opera && !window.opera){
			if(navigator.appVersion.substr(22,3)!="5.0"){
				useIframe = true;
			}
		}

    	var divElement = document.createElement("div");
		//divElement.style.display="none";
		divElement.style.visibility="hidden";
		divElement.style.zIndex="1";
		
    	if(this.config && this.config.suggestBoxCssId){
			divElement.id = this.config.suggestBoxCssId;
    	}
    	
    	if(this.config && this.config.suggestBoxCssClass){
			divElement.className = this.config.suggestBoxCssClass;
    	}
    	
		if(useIframe){
	   		var iframeElement = document.createElement("iframe");
			iframeElement.scrolling="no";
			iframeElement.frameBorder="0";
			iframeElement.style.zIndex="-1";
	   		
			if(document.location.protocol == "https:"){
				iframeElement.src="//0";
			} else if(window.opera) {
				iframeElement.src="#";
			} else {
				iframeElement.src="javascript:false";
			}

	    	EventController.registerEvent(divElement, EventController.RESIZE, function(e){
	    		iframeElement.style.width = divElement.offsetWidth + "px";
	    		iframeElement.style.height = divElement.offsetHeight + "px";
	    	}, false);
	   	
	    	divElement.appendChild(iframeElement);
	    }

    	divElement.appendChild(linesElement);

		return divElement;
  	};
  
  	/**
  	 * 
  	 */
  	SuggestBox.prototype.getSuggestLinesBox = function(config, eventController){
    	return new SuggestLinesBox(config, eventController);
  	};
	
  	/**
  	 * Funkce zobrazi SuggestBox element.
  	 * 
  	 */
  	SuggestBox.prototype.show = function(){
  		
  		var coord = this.eventController.getSuggestPosition();
  		this.element.style.top = coord[1] + "px"; //y
  		this.element.style.left = coord[0] + "px"; //x
    	this.element.style.visibility="visible";
    	this.element.style.width = (this.eventController.input.offsetWidth) + "px";
    	this.lines.scrollTop();
  	};
  
  	/**
  	 * Funkce skryje SuggestBox.
  	 * 
  	 */
  	SuggestBox.prototype.hide = function(){
    	//this.element.style.display = "none";
    	this.element.style.visibility="hidden";
  	};

	SuggestBox.prototype.unselectLine = function(){
		this.lines.unselect();
	};
	
	SuggestBox.prototype.selectLine = function(lineElement){
		this.lines.select(lineElement);
	};

	SuggestBox.prototype.selectNextLine = function(){
		this.lines.selectNext();
	};

	SuggestBox.prototype.selectPreviousLine = function(){
		this.lines.selectPrevious();
	};

	SuggestBox.prototype.getSelectedLine = function() {
		return this.lines.selected;
	};

	SuggestBox.prototype.setData = function(data) {
		this.lines.setData(data);
	};

  	SuggestBox.prototype.clear = function() {
  		this.lines.clear();
	};
	
/* ********************************** */
/*           SuggestLinesBox          */
/* ********************************** */

	/**
	 * 
	 * @param {Object} config
	 */
  	function SuggestLinesBox(config, eventController){
		this.config = config;
    	this.eventController = eventController;
		
		if(this.eventController){
    		this.limit = this.determineLimit();
	    	this.element = this.createElement();
		}
    	
		this.selected = null; 
  	}

  	/**
  	 * Funkce stanovi maximalni pocet
  	 * zobrazitelnych zaznamu
  	 * 
  	 * @param {Object} limit
  	 */
  	SuggestLinesBox.prototype.determineLimit = function(limit){
    	return (limit)? limit : 100;
  	};

  	/**
  	 * Zrusi oznaceni vybraneho elementu
  	 * 
  	 */
  	SuggestLinesBox.prototype.unselect = function(){
    	if(this.selected){
      		this.selected.className= null;
      		this.selected = null;
    	}
  	};

  	/**
  	 * Vybere element. 
  	 * 
  	 * @param {Object} element
  	 */
  	SuggestLinesBox.prototype.select = function(element){
    	this.unselect();
    	this.selected = element;
    	if(this.config && this.config.suggestLineHighlightCssClass){
    		// TODO nejen CSSkem odlisit
	    	this.selected.className = this.config.suggestLineHighlightCssClass;
	    	this.eventController.scrollToLine(this.selected);
    	}
  	};

  	/**
  	 * Vybere nasledujici element.
  	 * 
  	 */
  	SuggestLinesBox.prototype.selectNext = function(){

    	var newAdept = null;

    	if(this.selected){
      		newAdept = this.selected.nextSibling;
    	}

    	if(!newAdept) {
      		newAdept = this.element.firstChild;
    	}

    	if(newAdept){
      		this.select(newAdept);
      		return false;
    	}

	    return true;
  	};

  	/**
  	 * Vybere predchozi element
  	 * 
  	 */
  	SuggestLinesBox.prototype.selectPrevious = function(){

    	var newAdept = null;

    	if(this.selected){
      		newAdept = this.selected.previousSibling;
    	}

    	if(!newAdept) {
      		newAdept = this.element.lastChild;
    	}

    	if(newAdept){
      		this.select(newAdept);
      		return false;
    	}

    	return true;
  	};
  
  	/**
  	 * Vytvori radek
  	 * 
  	 * @param {Object} text
  	 */
  	SuggestLinesBox.prototype.createLineElement = function(text){
    	
		var line = document.createElement("p");
    	line.appendChild(document.createTextNode(text));
    
		var controller = this.eventController;
		
    	controller.registerEvent(line, EventController.CLICK, 
			function(e){
				if(!controller.isOn){
					return;
				}
				if(!e){ //IE
					e = window.event;
				}
				var target = null;
				if(e.target){ // FF
					target = e.currentTarget;
				} else if(e.srcElement){ //IE
					target = e.srcElement;
				}
				controller.putLineValue(target);
			}, false);
			
    	controller.registerEvent(line, EventController.MOUSEOVER, 
			function(e){
				if(!controller.isOn){
					return;
				}
				if(!e){ //IE
					e = window.event;
				}
				var target = null;
				if(e.target){ // FF
					target = e.currentTarget;
				} else if(e.srcElement){ //IE
					target = e.srcElement;
				}
				controller.selectLine(target);
			}, false);  
			
    	return line;
  	};
  
  	/**
  	 * Vytvori kontejner pro radky
  	 * 
  	 */
  	SuggestLinesBox.prototype.createElement = function(){
    
		var element = document.createElement("div");
		element.style.position = "relative";
		
    	if(this.config && this.config.suggestLinesBoxCssId){
			element.id = this.config.suggestLinesBoxCssId;
    	}
    	
    	if(this.config && this.config.suggestLinesBoxCssClass){
			element.className = this.config.suggestLinesBoxCssClass;
    	}

		var controller = this.eventController;

    	// ******* FOKUS *****
    	controller.registerEvent(element, EventController.BLUR, 
			function(e){
				// pro IE (FF necha focus na inputu)
				controller.suggestBoxHasFocus = false;
				if(controller.isOn){
					controller.processFocus();
				}
			}, false);
			  
    	controller.registerEvent(element, EventController.FOCUS, 
			function(e){
				// pro IE (FF necha focus na inputu)
				controller.suggestBoxHasFocus = true;
				if(controller.isOn){
					controller.processFocus();
					window.setTimeout(
						function(e){
							controller.input.focus();
						}, 100);
				}
			}, false);

    	return element;
  	};
 
  	/**
  	 * Zrusi vsechny radky
  	 */
  	SuggestLinesBox.prototype.clear = function() {
    	this.unselect();
    	this.scrollTop();
    	while(this.element.hasChildNodes()){
      		this.element.removeChild(this.element.firstChild);
    	}
  	};

  	/**
  	 * Promitne data do linesBoxElementu.
  	 * 
  	 * @param {Object} data
  	 */
  	
	SuggestLinesBox.prototype.setData = function(data) {
    	this.clear();
    	if(!data || !data.length || data.length == 0){
    		return;
    	}
    	var limit = Math.min(this.limit, data.length);
    	var height = 0;
    	for(var i = 0;i<limit; i+=1){
      		var line = this.createLineElement(data[i]);
      		this.element.appendChild(line);
      		height += line.offsetHeight;
    	}
    	if(height < 300){
    		this.element.style.height = (height + 2) + "px";
    		this.element.style.overflow = "hidden";
    	}else{
    		this.element.style.height = "300px";
    		this.element.style.overflow = "scroll";
    	}
  	};
	
	SuggestLinesBox.prototype.scrollTop = function() {
    	if(this.element.scrollTop != 0){
    		this.element.scrollTop = 0;
    	}
	};
	
