/**
 * class CSuggestProvider
 * Az autocomplete mezőben megjelenő találatokat szolgáltató objektum.
 * A szerver felé küldi el a kéréseket, és a válaszokat átadja az autocomplete mezőnek.
 * @todo Minden CmooAutoComplete példánynak szüksége van egy CSuggestProvider objektumra.
 */
var CSuggestProvider = new Class ({
	/**
	 * Az aktuális kérés adatait tartalmazó objektum.
	 */
	rObject : null,
	/**
	 * A kéréshez hozzáfűzendő plusz paraméterek.
	 */
	plusParams : null,
	/**
	 * Konstruktor.
	 * @param sUrl Az ajánlatokat szolgáltató szerver oldali szkript URL-je.
	 * @param method A kérés fajtája: post | get
	 * @param oManager A CmooRequestManager objektum referenciája ami a szerverrel történő kommunikációt vezérli.
	 * @param queryString A kéréshez hozzáfűzendő plusz paraméterek lekérdező karakterlánca.
	 */
	initialize : function (sUrl, method, oManager) {
		this.oManager = oManager;
		this.method = method == 'get' ? 'get' : 'post';
		this.sUrl = sUrl;
		// a plusz paraméterek feldolgozása
		if (arguments.length > 3 && $type(arguments[3]) == 'string') {
			this.plusParams = '&' + arguments[3];
		}
		this.rObject = {
			type: 'json',
			url:this.sUrl,
			method: this.method,
			evalScripts:false,
			onFailure: function(xhr) {
				//window.alert("hiba");
			}
		};
	},
	/**
	 * Lekéri az ajánlati listát.
	 * @param oAComplete Az autocomplete objektum, amelynek el akarjuk juttatni a válaszokat.
	 * A válaszok megérkezése esetén az autocmplete objektum autoComplete() metódusát hívja meg.
	 */
	requestList : function (oAComplete) { 
		//this.cancel();
		this.rObject.data = oAComplete.getQueryString() + ($chk(this.plusParams) ? this.plusParams : ''),
		this.rObject.onComplete = oAComplete.autoComplete.bind(oAComplete); 
		this.oManager.submit(this.rObject);
	},
	/**
	 * Törli az aktuális folyamatban lévő kérést.
	 */
	cancel : function () {
		if ($chk(this.rObject)) {
			this.oManager.cancel(this.rObject);
		}
	}
});
/**
 * class CmooAutoComplete
 * Az autoComoplete mező. Csak a megjelenítésért, és a felhasználói interakciókért felelős.
 * A szerverrel történő kommunikációt egy CSuggestProvider objektum közreműködésével végzi.
 * @todo
 * A szerver oldalról egy JSON objektumnak kell visszajönnie, amelynek kötelezően tartalmazina kell egy list nevű attributumot.
 * Ez egy tömb, vagy null. A tömb elemei szintén objektumok amelyek a következő attributumokkal kell, hogy rendelkezzenek: html, plain, value
 * A html a legördülő listában megjelenő elem, ami lehet egyszerű szöveg. A plain az a szöveg, amelynek az input mezőbe kell kerülnie kiválasztás esetén.
 * A value az érték, amely egy hidden mezőbe kerül amely az input mező.name + '_' megnevezéssel rendelkezik;
 * A lista elem objektumhoz, vagy  még adható plusz név-érték pár. Ha a konstruktor aPFields paraméterében a név meg volt adva, akkor megkeresi az adott id-jű input elemet , és ezt az értéket adja hozzá kiválasztáskor.
 * Ugyanez érvényes visszafelé, a javaslatkéréskor legyűjti ezen mezők aktuális értékét, és elküldi a szerver felé. Tehát ezeket a mezőket használhatjuk oda-vissza egyaránt. 
 * <code>
 * var oRManager = new CmooRequestManager();
 * var oAComplete = new CmooAutoComplete ($('from'),new CSuggestProvider('./ajax/autoairport.php','post',oManager,'auto_location'),['f_type']);
 * </code>
 */
var CmooAutoComplete = new Class ({
	/**
	 * maximum ennyi elem lehet a legördülő listában
	 */
	MAX_LIST_LENGTH : 10,
	/**
	 * megadja, hogy hány kaionrakter leütés után reagáljon
	 */ 
	MIN_CHAR_COUNT : 2,
	/**
	 * A gyorsgépelés késleltetése 
	 */
	PRESS_DELAY : 280,
	/** 
	 * az aktuálisan kiválasztott lem indexe
	 */		
	curr : -1,
	/**
	 *	Az input mező
	 */
	inputField : null,
	/**
	 * A mező neve
	 */
	fieldName : null,
	/**
	 *	itt tároljuk a válasz listát
	 */
	resultList : null,
	/**
	 * plusz figyelendo mezo ha van
	 */
	plusFields : new Array,
	/**
	 * az ajánlat szolgáltató
	 */
	suggestProvider : null,
	/**
	 * az ajánlatokat tartalmazó konténer.
	 */	
	resultCnt : null,
	/**
	 * a billenytű  lenyomások késleltető objektuma
	 */
	timeoutId : null,
	/**
	 * Az onComplete utan hivodik meg
	 */
	fnCallback : null,
	/**
	 * Az aktuális adatokat tárolja. 
	 */
	temp : null,
	/**
	 * Konstruktor.
	 * @param oIField Az input mező. Lehet Element, vagy létező id. Ha mégsincs ilyen elem, akkor a body-hoz hozzáfűz egy újat.
	 * @param oSProvider A kiegészítést ajánló szolgáltató objektum.
	 * @param aPFields Azon plusz elem, vagy azok tömbje, amely(ek) adatait el akarjuk küldeni, illetve az ajánlatból fel akarjuk tölteni.
	 */
	initialize : function (oIField, oSProvider, aPFields, fOnComplete) {
		if (!$chk($(oIField))) {
			new Element ('input',{'type':'text','id':oIField,'name':oIField}).inject($(document.body));
		}
		this.inputField = $(oIField);
		this.inputField.set('autocomplete','off');
		this.fieldName = this.inputField.get('name');
		this.suggestProvider = oSProvider;
		//this.plusFields = aPFields;
		// rejtett mező létrehozása
		if(!$chk($(this.fieldName +'_'))) {
			this.hiddenField = new Element ('input',{'type':'hidden','name':this.fieldName + '_','id':this.fieldName + '_','value':''}).inject(this.inputField,'after');
		}
		else {
			this.hiddenField = $(this.fieldName +'_');
		}
		// onComplete callback 
		if ($type(fOnComplete) == 'function'){
			this.fnCallback = fOnComplete;
		}
		// a további figyelt mezők
		var oPFiled = null;
		if ($type(aPFields) == 'array') {
			for (var i = 0; i< aPFields.length; ++ i) {
				if ($chk(oPField = $(aPFields[i]))) {
					this.plusFields.push(oPField);
				}
				else {
					oPField = new Element('input',{'type':'hidden','name':aPFields[i],'id':aPFields[i],'value':''});
					oPField.inject(this.hiddenField,'after');
					this.plusFields.push(oPField);
				}
			}
		}
		else if ($chk(aPFields)) {
			if ($chk(oPField = $(aPFields))) {
				this.plusFields.push(oPField);
			}
			else {
				oPField = new Element('input',{'type':'hidden','name':aPFields,'id':aPFields,'value':''});
				oPField.inject(this.hiddenField,'after');
				this.plusFields.push(oPField);
			}
		}
		// a legördülő abalak létrehozása
		this.createDropDown();
		//
		this.inputField.addEvent('keyup', function(oEvent){this.onKeyUp(oEvent);}.bind(this));
		this.inputField.addEvent('keydown', function(oEvent){this.onKeyDown(oEvent);}.bind(this));
		this.inputField.addEvent('blur',function() {this.suggestProvider.cancel();(function (){this.hideDropDown();}).delay(800,this);}.bind(this));
		//document.addEvent('click',function() {this.suggestProvider.cancel();this.hideDropDown()}.bind(this));
		document.addEvent('scroll',function () {this.setPosition(this.resultCnt)}.bind(this));
		window.addEvent('resize',function () {this.setPosition(this.resultCnt)}.bind(this));
	},
	/**
	 * Visszatölti a mentett állapotot.
	 */
	_backup : function () {
		if($chk(this.temp)) {
			this.inputField.set('value',this.temp.iText);
			this.hiddenField.set('value',this.temp.hText);
			if($chk(this.temp.pFields)) {
				for (var i= 0; i< this.plusFields.length; i++) {
					if ($chk(this.temp.pFields[this.plusFields[i].get('name')])) {
						this.plusFields[i].set('value',this.temp.pFields[this.plusFields[i].get('name')]);
					}
				}
			}	
		}
	},
	_empty : function () {
		this.resultList = null;
		this.listCnt.empty();
	},
	/**
	 * Elmenti a autocomplet mező és a kiegészítő mezők aktuális állapotát.
	 */
	_save : function () {
		this.temp = {
			'iText' : this.inputField.get('value'),
			'hText' : this.hiddenField.get('value'),
			'pFields' : []
		};
		if ($chk(this.plusFields)) {
			for (var i = 0; i< this.plusFields.length; i++) {
				this.temp.pFields[this.plusFields[i].get('name')] = this.plusFields[i].get('value');
			}
		}	
	},
	/**
	 * Beállítja az input mező és a hidden mező értékét, valamint a plusz mezőkét, ha vannak ilyenek.
	 * @param oValues A html,plain,value, és plusz mezők adatait tartalmazó objektum.
	 */
	_setValue : function (oValues) {
		if ($type(oValues) == 'object') {
			this.inputField.set('value',oValues.plain);
			this.hiddenField.set('value',oValues.value);
			for (var i = 0; i < this.plusFields.length; i++) {
				if ($chk(oValues[this.plusFields[i].get('name')])) {
					this.plusFields[i].set('value',oValues[this.plusFields[i].get('name')]);
				}
			}
		}
	},
	/**
	 * Magát az autokiegészítést végzi el a providertől jött találatok alapján.
	 * @param oJSON A találatokat tartalmazó JSON objektum.
	 */
	autoComplete : function (oJSON) {
		if (!$chk(oJSON)) {
			return;
		}
		this._save();
		this._empty();
		this.hideDropDown();
		// a legördülő lista feltöltése
		if ($type(oJSON.list) == 'array' && oJSON.list.length > 0) {
			this.resultList = oJSON.list;
			this.curr = -1;
			var oEl = null;
			//this._setValue(this.resultList[0]);
			for (var i = 0; i < this.resultList.length; i++ ) {
				oEl = new Element('div',{'html':this.resultList[i].html});
				oEl.addEvent('click',this.onClick.bind(this));
				oEl.addEvent('mouseover',this.onMouseOver);
				oEl.addEvent('mouseout',this.onMouseOut);
				oEl.inject(this.listCnt);
			}
			//this.stepSuggestion(1);
			this.showDropDown();
		}
		// az egyéb mezők feltöltése
		for (var i = 0; i < this.plusFields.length; i++) {
			if ($chk(oJSON[this.plusFields[i].get('name')])) {
				this.plusFields[i].set('value',oJSON[this.plusFields[i].get('value')]);
			}
		}
	},
	/**
	 * Előállítja a legördülő listát.
	 * Csak konstruktor időben egyszer fut le.
	 */
	createDropDown : function () {
		this.resultCnt = new Element('div',{'class':'input_auto_div select-free'});
		this.resultCnt.setStyles({'overflow':'auto','position':'absolute'});
		//this.resultCnt.setStyle('position','absolute');
		this.resultCnt.set('html','<div class="auto_top"></div><div class="auto_cont"></div><div class="auto_bot"></div><!--[if lte IE 6.5]><iframe></iframe><![endif]-->');
		this.resultCnt.inject(this.inputField,'after');
		this.hideDropDown();
		this.listCnt = this.resultCnt.getElement('div[class=auto_cont]');
		//this.resultCnt.getElement('div[class=auto_bot]').setStyles({'background-color':'red','z-index':20,'height':'10px','margin-top':'-3px'});
		/*
		// alja
		new Element('div', {'class':'auto_bot'}).inject(this.resultCnt,'top');
		// kozepe: ebbe jonnek majd az eredmenyek
		this.listCnt = new Element('div', {'class':'auto_cont'}).inject(this.resultCnt,'top');
		// teteje
		new Element('div', {'class':'auto_top'}).inject(this.resultCnt,'top');
		//this.resultCnt.appendText('<!--[if lte IE 6.5]><iframe></iframe><![endif]-->');
		// <!--[if lte IE 6.5]><iframe></iframe><![endif]--> 
		new Element ('!--',{'html':'[if lte IE 6.5]><iframe></iframe><![endif]'}).inject(this.resultCnt);
		//this.autoComplete(null);
		*/
	},
	/**
	 * Lekérdezi az input mező, és ha vannak plusz mezők akkor azok tartalmával
	 * bővített lekérdezési karakterláncot.
	 * @return Az név, érték párokat reprezentáló lekérdezési karakterlánc.
	 */
	getQueryString : function () {
		var qs = this.inputField.get('name') + '=' + this.inputField.get('value');
		for (var i = 0; i < this.plusFields.length; i++) {
			if ($type(this.plusFields[i]) == 'element') {
				qs += '&' + this.plusFields[i].get('name') + '=' + this.plusFields[i].get('value');
			}
		}
		return qs;
	},
	/**
	 * Elrejti a legördülő listát.
	 */
	hideDropDown : function () {
		this.resultCnt.setStyle('display','none');
	},
	/**
	 * A legördülő lista elemeinek click eseménykezelője.
	 * @param oEvent Az esemény objektum.
	 */
	onClick : function (oEvent) {
		oEvent.stop();
		var oDiv = oEvent.target;
		var i = 0;
		var el = this.listCnt.getElements('div');
		while (el[i] != oDiv) {
			++i;
		}
		if (i < this.resultList.length) {
			this.curr = i;
			this._setValue(this.resultList[i]);
		}

		this.hideDropDown();
	},
	/**
	 * Az input mező keydown eseménykezelője.
	 * @param oEvent Az esemény objektum.
	 */
	onKeyDown : function (oEvent) {
		switch (oEvent.code) {
			case 38 :
				this.stepSuggestion(-1);
				break;
			case 40 :
				this.stepSuggestion(1);
				break;
			case 27 : // escape
				this._backup();
				oEvent.stop();
				this._empty();
				this.hideDropDown();
				// this.fnCallback meghivasa
				if ($chk(this.fnCallback)) this.fnCallback();
				break;
			case 9 : // tab
			case 13 : // enter
				if (oEvent.code == 13) {
					oEvent.stop();
				}
				// ha nem választott ki semmit akkor az elsőt tesszük be
				if ((this.curr<0 || !$chk(this.hiddenField.get('value')) || this.hiddenField.get('value') == '') && $chk(this.resultList.length)) {
					this.curr = 0;
					this._setValue(this.resultList[this.curr]);
				}
				this._empty();
				this.hideDropDown();
				// this.fnCallback meghivasa
				if ($chk(this.fnCallback)) this.fnCallback();
				break;
		}
	},
	/**
	 * Az input mező keyup eseménykezelője.
	 * @param oEvent Az esemény objektum.
	 */
	onKeyUp : function (oEvent) {
		var keyCode = oEvent.code;
		// leállítjuk a várakozó kérést
		$clear(this.timeoutId);
		// a backspace és a delete
		if ((keyCode == 8 || keyCode == 46) && this.inputField.get('value').length >= this.MIN_CHAR_COUNT) {
			this.timeoutId = this.suggestProvider.requestList.delay(this.PRESS_DELAY,this.suggestProvider,this);
		}
		// amiket nem kezelünk
		else if ((keyCode != 16 && keyCode < 32) || (keyCode >= 33 && keyCode < 46) || (keyCode >=112 && keyCode <= 123)) {
			return;
		}
		else if (this.inputField.get('value').length >= this.MIN_CHAR_COUNT) {
			this.timeoutId = this.suggestProvider.requestList.delay(this.PRESS_DELAY,this.suggestProvider,this);
		}
		else {
			this.listCnt.empty();
			this.hideDropDown();
		}
	},
	/**
	 * A legördülő lista elemeinek mouseout eseménykezelője.
	 * @param oEvent Az esemény objektum.
	 */
	onMouseOut : function (oEvent) {
		this.removeClass ('auto_over');
	},
	/**
	 * A legördülő lista elemeinek mouseover eseménykezelője.
	 * @param oEvent Az esemény objektum.
	 */
	onMouseOver : function (oEvent) {
		this.addClass ('auto_over');
	},
	/**
	 * Az adott elemhez szkrollozza a legördülő listát.
	 * @param oItem Az a listaelem amelyikhez szkrollozunk.
	 */
	scrollToItem : function(oItem) {
		var y = $(oItem).getPosition(this.resultCnt).y;
			this.resultCnt.scrollTo(0,y);
	},
	/**
	 * A paraméterül kapott div elemet pozicionálja az input mező bal alsó széléhez.
	 * @param A pozicionálandó elem.
	 */
	setPosition : function (oElement) {
		if ($chk(this.inputField) && $chk(oElement)) {
			var ifCoor = this.inputField.getCoordinates();
			// pozicionálás
			oElement.setStyle('left',ifCoor.left + 'px');
			oElement.setStyle('top',ifCoor.bottom + 'px');
			// méretezés
			oElement.setStyle('width',ifCoor.width);
		}
	},
	/**
	 * Megjeleníti a legördülő listát.
	 */
	showDropDown : function () {
		this.setPosition(this.resultCnt);
		if (this.resultList.length >= this.MAX_LIST_LENGTH) {
			this.resultCnt.setStyle('height',this.inputField.getSize().y * this.MAX_LIST_LENGTH + 'px');
		}
		else {
			this.resultCnt.setStyle('height','auto');
		}
		this.resultCnt.setStyle('display','block');
	},
	/**
	 * A legördülő listában léptet az előző, vagy következő elemre.
	 * @param iStep A léptetés iránya. Ha negatív, akkor fel, ha pozitív, akkor le.
	 */
	stepSuggestion : function (iStep) {
		if ($chk(this.resultList) && $chk(el = this.listCnt.getElements('div'))) {
			this.showDropDown();
			//var temp = this.curr;
			if (iStep > 0) {
				if (this.curr < this.resultList.length-1) {
					++ this.curr;
				}
			}
			else {
				if (this.curr > 0) {
					-- this.curr;
				}
			}
			for (var i = 0; i < el.length; i++) {
				el[i].removeClass('auto_highlight');
			}
			this._setValue(this.resultList[this.curr]);
			if($chk(oDiv = el[this.curr])) {;
				oDiv.addClass('auto_highlight');
				this.scrollToItem(oDiv);
			}	
		}	
	}
});
