/**
 * SoftAjax - Rozszerzenie, oparte na bibliotece Prototype, ta werja nie jest uniwersalna.
 *
 * Obiekt globalny sluzacy do wygodnego pobierania danych z widokow i uruchamiania akcji.
 * Zawiera dwie kluczowe metody:
 * - getView - sluzy do pobrania widoku
 * - setAction - wykonuje akcje
 * 
 * UWAGA!!!
 * Aby działał updateLockDivSize wymagane jest window.js z Prototype Window
 */

// Sprawdzamy, czy istnieje prototypeAjax (prototype przerobiony - z nazwa Ajax podmieniona na PrototypeAjax)
// Jezeli nie istnieje, to sprawdzamy, czy istnieje "oryginalny" Ajax z prototype
// i kopiujemy go jako PrototypeAjax na potrzeby tego modulu

if (typeof(PrototypeAjax)=='undefined') {
	try {
		if (typeof(Ajax.getTransport)!='undefined') {
			var prototypeAjax = Ajax;
		} else {
			throw new Error('PrototypeAjax missing!');
		}
	} catch (e) {
		if (e.message!='PrototypeAjax missing!')
			throw new Error('PrototypeAjax detection error!');
		else
			throw new Error('PrototypeAjax missing!');
	}
}

var softAjax = {

	connections: 		[], 	// Tablica z obiektami polaczen (request)
	onSessionExpired: 	null, 	// Funkcja do uruchomienia, jesli otrzymamy odpowiedz SESSION_EXPIRED

	/**
	 * Pobiera dane z widoku. 
	 * @param name 	Nazwa identyfikujaca request (unikalna). Jezeli nazwa sie powtorzy, 
	 *				poprzedni request o tej nazwie nie zostanie odebrany (zostanie zignorowany).
	 * @param url	URL z ktorego pobieramy dane
	 * @param p_options: Obiekt, ktory moze (nie musi) zawierac nastepujace pola:
	 *						query 		- obiekt lub referencja do formularza, ktore zostana wyslane POSTem
	 *						div 		- nazwa wezla do ktorego ma byc wstawiona odpowiedz
	 *						lock		- czy zablokowac cala aplikacje na czas ladowania (za pomoca lockDiv)
	 *						loadingDiv	- mozna tu podac nazwe wezla ktory ma byc pokazany podczas wczytywania danych
	 *						onSuccess- funkcja do wykonania w przypadku powodzenia, otrzymuje parametry: json, transport, request
	 *						onFailure- co nalezy wykonac w przypadku niepowodzenia, otrzymuje parametr jw.
	 * 
	 * Czynnosci, jakie sa wykonane w przypadku wywolania tej metody:
	 * - wyslanie rzadania do serwera (i ewentualne usuniecie obslugi poprzedniego rzadania o tej samej nazwie)
	 * - pokazanie loadingDiv lub zablokowanie aplikacji przez lockDiv
	 * i jezeli odpowiedz nadejdzie: 
	 * - wpisanie odebranej tresci do odpowiedniego DIVa
	 * - zevalowanie wszystkich skryptow w odpowiedzi znajdujacych sie w tagach <SCRIPT></SCRIPT>
	 * - zevalowanie instrukcji odpowiednio do wyniku (jezeli podano)
	 * - odblokowanie aplikacji/schowanie loadingDiv
	 *
	 * Co moze przysylac widok:
	 * - odpowiedz do wstawienia w DIV (UWAGA! Odpowiedz moze zawierac kod do wykonania w takach <SCRIPT></SCRIPT>
	 * - dodatkowo moze przesylac w naglowku X-JSON: obiekt JSON, obslugiwane sa nastpujace pola
	 *   --> div - zawierajace nazwe DIVa do ktrorego ma byc wstawiona odpowiedz. To pole jest ignorowane, jezeli przy wywolaniu podano inna nazwe DIVa
	 */
	getView: function(name, url, p_options) {
		// Obsluga parametrow
		var softOptions = {
			mode:			'view',		// Uzupelniane automatycznie
			name:			'',			// Uzupelniane automatycznie
			url:			'',			// Uzupelniane automatycznie
			query:			{},	// Tresc do wyslania POSTem
			div:			null,		// Gdzie maja byc wstawione odpowiedzi
			lock: 			false,		// Czy zablokowac cala aplikacje na czas ladowania
			loadingDiv:		null,		// Nazwa diva zawierajacego informacje o wczytywaniu
			onSuccess:		null,		// funkcja do wykonania w przypadku powodzenia, otrzymuje parametry: json (zawierajacy kod JSON), transport (cala odpowiedz) i request (referencja do requesta Ajaxowego - obiekt Prototype'a) 
			onFailure:		null		// funkcja do wykonania w przypadku niepowodzenia, otrzymuje parametry: json (zawierajacy kod JSON), transport (cala odpowiedz) i request (referencja do requesta Ajaxowego - obiekt Prototype'a)
		};
		Object.extend(softOptions, p_options || {});	
		softOptions.name = name;
		softOptions.url = url;
		try {
			if (softOptions.div!=null){
				$(softOptions.div).innerHTML = '';
				/**
				var tmpHeight = $(softOptions.div).getHeight();
				console.debug("Height : "+tmpHeight);
				$(softOptions.div).innerHTML = '';
				if(tmpHeight>0){
					$(softOptions.div).style.height = tmpHeight+'px';
				}
				console.debug("Actual : "+$(softOptions.div).style.height);
				**/
			}
		} catch (e) { console.warn(e);}
		this.connect(softOptions);
	},
	
	/**
	 * Wykonuje akcje i obsluguje odpowiedz.
	 * @param name 	Nazwa identyfikujaca request (unikalna). Jezeli nazwa sie powtorzy, 
	 *				poprzedni request o tej nazwie nie zostanie odebrany (zostanie zignorowany).
	 * @param url	URL z ktorego pobieramy dane
	 * @param p_options: Obiekt, ktory moze (nie musi) zawierac nastepujace pola:
	 *						query 		- obiekt lub referencja do formularza, ktore zostana wyslane POSTem
	 *						feedbackDiv - nazwa wezla do ktorego ma byc wstawiona odpowiedz. Jezeli nie podany, to moze byc tez podany w odpowiedzi z akcji
	 *						forms		- Tablica zawierajaca nazwy formularzy, z ktorych pola maja byc wygaszone przed podswietleniem blednych pol
	 *						lock		- czy zablokowac cala aplikacje na czas ladowania (za pomoca lockDiv)
	 *						loadingDiv	- mozna tu podac nazwe wezla ktory ma byc pokazany podczas wczytywania danych
	 *						onSuccess	- funkcja do wykonania w przypadku powodzenia, otrzymuje parametry: json, transport, request
	 *						onFailure	- funkcja do wykonania w przypadku niepowodzenia, otrzymuje parametry jw.
	 * 
	 * Czynnosci, jakie sa wykonane w przypadku wywolania tej metody:
	 * - wyslanie rzadania do serwera (i ewentualne usuniecie obslugi poprzedniego rzadania o tej samej nazwie)
	 * - pokazanie loadingDiv lub zablokowanie aplikacji przez lockDiv
	 * i jezeli odpowiedz nadejdzie: 
	 * - umieszczenie w feedbackDiv informacji zwrotnych
	 * - wygaszenie podswietlonych pol formularza
	 * - podswietlenie ewentualnych blednie wypelnionych pol
	 * - zevalowanie instrukcji przyslanych przez akcje (JSON)
	 * - zevalowanie instrukcji odpowiednio do wyniku (jezeli podano)
	 * - odblokowanie aplikacji/schowanie loadingDiv
	 *
	 * Co moze przysylac akcja:
	 * - odpowiedz w postaci JSON z wlasciwym naglowkiem (lub w naglowku X-JSON), obslugiwane sa nastpujace pola:
	 *   --> result (bool) - czy akcja sie powiodla
	 *   --> feedback (string) - odpowiedz z serwera
	 *   --> feedbackDiv (string) - DIV do umieszczenia odpowiedzi (ignorowane, jesli DIV podano przy wywolaniu)
	 *   --> badfields (string) - tablica z id pol do podswiedlenia. Podswietlenie polega na dodaniu klasy CSS. Nazwa klasy ustalona jest w module softPageControll.highlightClassName
	 *   --> eval (string) - kod JavaScript to wykonania 
	 */
	setAction: function(name, url, p_options) {
		// Obsluga parametrow
		var softOptions = {
			mode:			'action',	// Uzupelniane automatycznie
			name:			'',			// Uzupelniane automatycznie
			url:			'',			// Uzupelniane automatycznie
			query:			{},			// Tresc do wyslania POSTem
			feedbackDiv:	null,		// Gdzie maja byc wstawione komunikaty
			forms:			[],			// Tablica zawierajaca nazwy formularzy, z ktorych pola maja byc wygaszone przed podswietleniem blednych pol
			lock: 			false,		// Czy zablokowac cala aplikacje na czas ladowania
			loadingDiv:		null,		// Nazwa diva zawierajacego informacje o wczytywaniu
			onSuccess:		null,		// Funkcja do wykonania w przypadku powodzenia, otrzymuje jeden parametr zawierajacy kod JSON
			onFailure:		null		// Funkcja do wykonania w przypadku niepowodzenia, otrzymuje jeden parametr zawierajacy kod JSON
		};
		Object.extend(softOptions, p_options || {});	
		softOptions.name = name;
		softOptions.url = url;
		this.connect(softOptions);
	},
	
	/**
	 * Wysyla requesta - metoda wewnetrzna, uzywac getView i setAction
	 * @see getView
	 * @see setAction
	 * @access private
	 **/
	connect: function(softOptions) {
		
		// Sprawdzenie czy URL podany
		if (softOptions.url=='' || softOptions.url==null) {
			throw new Error ('Empty url in getView');
		}
		
		// Zend debugger handling
		//softOptions.url += '?start_debug=1&debug_port=10000&debug_fastfile=1& ;debug_host=bcg.dev2.softhis.com%2C127.0.0.1&send_sess_end=1& debug_no_cache=1140775690156&debug_stop=1&debug_url= 1&debug_new_session=1';
		//softOptions.url += '?start_debug=1&debug_port=10000&debug_fastfile=1&debug_host=bcg.dev2.softhis.com&send_sess_end=1&debug_no_cache=1140775690156&debug_stop=1&debug_url=1&debug_new_session=1';
		//softOptions.url += '?start_debug=1&debug_host=bcg.dev2.softhis.com&debug_port=10000;debug_stop=1';
		
		if (typeof(window.clientIp)!='undefined') {
			try {
				softOptions.url += '?babebebebu=fwefw';
				clientIp = window.clientIp;
				var debugString = 'start_debug=1&debug_host='+clientIp+'&debug_port=10000&debug_stop=1';
				if (window.zendProfile)
					debugString += '&start_profile=1';
				if (softOptions.url.indexOf('?')==-1)
					debugString = '?' + debugString;
				else
					debugString = '&' + debugString;
				softOptions.url += debugString;
			} catch (e) {
				console.warn('Ajax Zend debugging initialization failed!');
			}
		}
		
		// Dodajemy informacje, ze to AJAX
		Object.extend(softOptions.query, {ajax: true});
		
		if (this.connections[softOptions.name]!=null) {
			console.info('Connection already opened: ' + softOptions.name);
			//console.debug(this.connections[softOptions.name].transport);
			try {
				this.connections[softOptions.name].transport.abort();
				this.errorHandler(this.connections[softOptions.name]);
				//delete this.connections[softOptions.name];
			} catch (e) {
				throw new Error ('Failed to abort previous query: '+softOptions.name);
			}
		}
		
		this.handleLoading(softOptions, 'on');
		
		// Wyslanie requesta
		
		// alert('Wyslanie requesta');
		
		var req = new PrototypeAjax.Request(softOptions.url, {
			method:		'post', 
			onSuccess: 	function (transport, json) {
				try {
					if (json==null && req.getHeader('Content-type').indexOf('application/X-JSON')!=-1)
						eval('json = '+transport['responseText']);
				} catch(e) {
					console.warn('Couldnt eval JSON answer from response content (transport).')
				}
				if (req.softOptions.mode=='action')
					softAjax.actionReceiver(req, transport, json);
				else if (req.softOptions.mode=='view')
					softAjax.viewReceiver(req, transport, json);
				else throw new Error ('Unsupported request mode');
			},
			onFailure: 	function () {
				softAjax.errorHandler(req);
			},
			parameters: softOptions.query
		});
		
		req.softOptions = softOptions;
		
		// Dodanie do tablicy otwartych polaczen
		if (softOptions.name=='' || softOptions.name==null) {
			console.warn('Empty connection name in getView');
		} else {
			this.connections[softOptions.name] = req;
		}
		
	},
	
	/**
	 * Obsluga odpowiedzi na getView 
	 * @access private
	 * @param request Referencja do obiektu request
	 * @param transport Odpowiedz
	 * @param json Odpowiedz JSON
	 **/
	viewReceiver: function(request, transport, json) {
		
		try {
			if (json.sessionExpired && this.onSessionExpired!=null) {
				(this.onSessionExpired)(request, transport, json);
				console.info('Session expired');
				return false;
			}
		} catch (e) {} 
		
		
		
		if ($(request.softOptions.div)!=null) {
			// Jest wymuszony DIV do ktorego mamy wpisac tresc
			try {
				$(request.softOptions.div).innerHTML = transport.responseText;
			} catch (e) {
				console.warn(e);
			}
		} else {
			try {
				$(json.div).innerHTML = transport.responseText;
			} catch (e) {
				console.warn('No DIV to put anwer into for request '+request.softOptions.name);
			}
		}
		
		// Zevalowanie wszystkich tresci w tagach <SCRIPT></SCRIPT>
		this.evalAllScripts(transport.responseText);
		
		// Na koncu wykonanie onSuccess
		try {
			if (request.softOptions.onSuccess!=null) {
				//console.debug('toEval:'+request.softOptions.onSuccess);
				try {
					request.softOptions.onSuccess(json, transport, request);
				} catch (e) {
					$('universalLoadingDiv').hide();
					//todo
					console.warn('Couldnt run onSuccess:'+e);
				}
			}
		} catch(e) {
			console.warn('Couldnt run onSuccess:'+e);
		}
		
		// Schowanie loadinga
		try {
			this.handleLoading(request.softOptions, 'off');
		} catch(e) {
			console.warn('Couldnt run handleLoading');
		}
		
		// Posprzatanie po sobie
		delete this.connections[request.softOptions.name];
	},
	
	/**
	 * Obsluga odpowiedzi na setAction 
	 * @access private
	 * @param request Referencja do obiektu request
	 * @param transport Odpowiedz
	 * @param json Odpowiedz JSON
	 **/
	actionReceiver: function(request, transport, json) {
		
		try {
			if (json.sessionExpired && this.onSessionExpired!=null) {
				(this.onSessionExpired)(request, transport, json);
				console.info('Session expired');
				return false;
			}
		} catch (e) {} 
		
		//Wyczyszczenie feedbackow
		try {
			feedbacks = document.getElementsByClassName('feedbackDiv');
			feedbacks.each(function(item) {
				$(item).innerHTML = '';
			});
		} catch(e) {
			console.warn('Failed to clear feedbacks ' + e);
		}
		
		// Obsluga feedbacku
		try {
			if (json.feedback!=null) {
				// Jest feedback
				if (request.softOptions.feedbackDiv!=null) {
					// Jest wymuszony DIV do ktorego mamy wpisac tresc
					if (request.softOptions.feedbackDiv=='alert')
						alert(json.feedback);
					else
						$(request.softOptions.feedbackDiv).innerHTML = json.feedback;
				} else {
					if (json.feedbackDiv!=null) {
						// Jest DIV proponowany przez widok
						if (json.feedbackDiv=='alert') 
							alert(json.feedback);
						else {
							$(json.feedbackDiv).innerHTML = json.feedback;
						}
					} else {
						console.warn('No DIV to put feedback into for request '+request.softOptions.name);
					}
				}
			}
		} catch (e) {
			console.warn('Couldnt process action feedback for request '+request.softOptions.name);
			// console.warn(e);
		}
		
		// Wygaszenie pol formularza
		try {
			request.softOptions.forms.each(function(item, index) {
				//console.debug('putting down from form'+item)
				softPageControll.putDownFields(item);
			})
		} catch (e) {
			console.warn('Couldnt put down fields for request '+request.softOptions.name);
		}
		
		// Podswietlenie pol formularza
		try {
			if (json.badFields!=null) {
				json.badFields.each(function(item) {
					if (typeof(item)=='object') {
						softPageControll.highlightField(item.fieldId);
						try {
							$(item.fieldId+'Feedback').innerHTML = item.text;
						} catch (e) {}
					} else {
						softPageControll.highlightField(item);
					}
				})
			}
		} catch (e) {
			console.warn('Couldnt highlight fields for request '+request.softOptions.name);
			console.warn(e);
		}
		
		try {
			if (json.eval!=null) {
				eval(json.eval);
			}
		} catch (e) {
			console.warn('Couldnt eval JS returned from request '+request.softOptions.name);
		}
		
		// Na koncu wykonanie onSuccess lub onFailure
		try {
			if (json.result) {
				if (request.softOptions.onSuccess!=null) {
					try {
						request.softOptions.onSuccess(json, transport, request);
					} catch (e) {}
				}
			} else {
				if (request.softOptions.onFailure!=null) {
					try {
						request.softOptions.onFailure(json, transport, request);
					} catch (e) {}
				}
			}
		} catch (e) {
			console.warn('Couldnt run onSuccess/onFailure');
		}
		
		// Schowanie loadinga
		try {
			this.handleLoading(request.softOptions, 'off');
		} catch(e) {
			console.warn('Couldnt run handleLoading');
		}
		
		// Posprzatanie po sobie
		delete this.connections[request.softOptions.name];
	},
	
	/**
	 * Obsluga blednych odpowiedzi na getView 
	 * @access private
	 * @param request Referencja do obiektu request
	 **/
	errorHandler: function(request) {
		console.warn('Query '+request.softOptions.name+' failed');
		
		try {
			if (request.softOptions.onFailure!=null) {
				//console.debug('toEval:'+request.softOptions.onFailure);
				try {
					request.softOptions.onFailure(json, transport, request);
				} catch (e) {}
			}
		} catch(e) {
			console.warn('Couldnt run onFailure');
		}
		
		// Schowanie loadinga
		try {
			this.handleLoading(request.softOptions, 'off');
		} catch(e) {
			console.warn('Couldnt run handleLoading');
		}
		
		// Posprzatanie po sobie
		delete this.connections[request.softOptions.name];
	},
	
	/**
	 * Wykonuje wszystkie skrypty w tagach SCRIPT
	 * @access private
	 * @param text Tresc HTML w ktorym sa taki SCRIPT
	 **/
	evalAllScripts: function(text) {
		reg = new RegExp ('<script.*?>([\\S\\s]*?)</script>', 'gi');
		while (fnd = reg.exec(text)) {
			try {
				eval(fnd[1]);
			} catch (e) {
				console.warn('Failed to eval ' + fnd[1]);
				console.warn(e);
			}
			
		} 
	},
	
	/**
	 * Obsluguje wlaczanie/wylaczanie ladowania
	 * @access private
	 **/
	handleLoading: function(softOptions, mode) {
		if (mode!='off' && mode!='on')
			throw new Error('Unknown mode');
		if (softOptions.lock) {
			// Blokowanie calej aplikacji
			if (mode=='on') {
				softPageControll.lock();
			} else {
				if (!softOptions.dontUnlock)
					softPageControll.unlock();
			}
		} 
		
		if (softOptions.loadingDiv!=null) {
			// Tylko pokazywanie/chowanie loading'a
			try {
				if (mode=='on') {
					$(softOptions.loadingDiv).show();
				} else {
					$(softOptions.loadingDiv).hide();
				}
			} catch (e) {
				console.warn('Failed to show/hide loading '+softOptions.loadingDiv);
			}
		}
	}
	
}

/**
 * Metody do kontroli HTMLa
 **/

var softPageControll = {
	strict : true,									//jeżeli używamy strict DTD XHTML	
	loadingsCount: 	 	0,							// Uzupelniane automatycznie - liczba otwartych requestow, blokujacych aplikacje
	lockDiv: 			'universalLoadingDiv',		// Nazwa glownego DIVa blokujacego ekran
	lockDivCanvas: 		'universalLoadingDivCanvas',
	highlightClassName: 'highlight',				// Nazwa klasy CSS podswietlajacej
	
	/**
	 * Ustawia id diva blokujacego caly ekran.
	 * Z-index i wymiary diva beda dynamicznie dostosowywane.<br>
	 * @access public
	 * @access public
	 */
	setLockDiv: function(name) {
		if ($('name')!=null)
			this.lockDiv = name;
		else {
			console.warn('Div '+name+' not found');
			// throw new UserException('Div '+name+' not found');
		}
	},
	
	/**
	 * Dodaje element-dziecko do elementu o podanym id i tresci str
	 * @access public
	 * @param id - Id elementu rodzica
	 * @param str - HTML do wpisania
	 * @return Element - Referencja do utworzonego elementu
	 */
	appendElement: function (id, str) {
		var ptr = document.getElementById(id);
		span = document.createElement("span");
		ptr.appendChild(span);
		span.innerHTML = str;
		return span;
	},
	
	/**
	 * Usuwa dziecko rodzica
	 * @param p_id - id elementu dziecka
	 **/
	destroyElement: function(p_id) {
		$(p_id).style.display = 'none';
		var p_parent = $(p_id).parentNode;
		p_parent.removeChild($(p_id));
	},
	
	
	centerDiv: function (divId){
		
	    var windowScroll = WindowUtilities.getWindowScroll();    
	    var pageSize = WindowUtilities.getPageSize();    
	    
	    top = (pageSize.windowHeight)/2;
	    top += windowScroll.top;
	    $(divId).style.top = top+'px';
	    
	    
	    left = (pageSize.windowWidth)/2;
	    left += windowScroll.left;
	    
	    
	    $(divId).style.left = left+'px';
	    
	},
	
	/**
	 * Blokuje aplikacje 
	 * Klasa nie jest uniwersalna, dziła tylko na strict w projekcie znak
	 * @access public
	 */
	lock: function() {
		try {
			if (this.loadingsCount>0)	
				this.loadingsCount++;
			else {
				
			showLoadingDiv();
			
			}

		} catch (e) {
			alert('ex');
			console.warn('Failed to lock application');
		}
	},
	
	/**
	 * Aktualizuje szerokosc i wysokosc elementu blokujacego (lockDiv)
	 * @access public
	 **/
	updateLockDivSize: function() {
		if ($(this.lockDivCanvas)==null) {
			console.warn('LockDiv '+this.lockId+' missing');
		} else {
			try {
				// Requires window.js (Prototype Window!!!)
				
    			var pageSize = WindowUtilities.getPageSize();    
    			//console.debug(this.strict);
    			if(this.strict){
    				$(this.lockDivCanvas).style.width = pageSize.pageWidth+"px";
					$(this.lockDivCanvas).style.height = pageSize.pageHeight+"px";
    			}else{
    				$(this.lockDivCanvas).style.width = pageSize.pageWidth;
					$(this.lockDivCanvas).style.height = pageSize.pageHeight;
    			}
			} catch (e) {
				console.warn(e);
				console.warn('Failed do update size of LockDiv');
			}
		}
	},
	
	/**
	 * Odblokowuje aplikacje 
	 * @access public
	 **/
	unlock: function() {
		try {
			if (this.loadingsCount>1)
				this.loadingsCount--;
			else {
				this.loadingsCount=0;
				
				//allSelectsShowByVisibility();

				if ($(this.lockDiv)==null) {
					console.warn('LockDiv '+this.lockDiv+' missing');
				} else {
					
					$(this.lockDiv).hide();
					// document.getElementById(blockId).style.display = 'none';
					// document.getElementById(infoId).style.display = 'none';
				}
			}
		} catch (ex) {
			console.warn('Failed to unlock application');
		}
	},
	
	highlightField: function(id) {
		$(id).addClassName(softPageControll.highlightClassName);
	},
	
	putDownFields: function(formId) {
		if (typeof(formId) == 'undefined') {
			throw new Error ('Form ' + formId + ' not found');
		}
		elements = $(formId).getElements();
		elements.each(function(item) {
			try { 
				if (item.name!=null) 
					item.removeClassName(softPageControll.highlightClassName);
			} catch (e) { 
				console.warn('Failed to put down field'+item.name);
			}
		});
	},
	
	openBrowserWindow: function(params) {
		var defaultParams = {
			src:	'',
			name:	'newBrowserWindow',
			width:	300,
			height: 200
		}
		var allParams = Object.extend(defaultParams, params);
		window.open(allParams.src, allParams.name, 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=' + allParams.width + ',height=' + allParams.height);
	}
}


