/**
 * AJAXPagination 
 * @author Valentin 
 * v0.3	15/07/09 | Agregada animacion de loading (con su respectivo CSS e Imagen)
 * v0.2 14/07/09 | Agregado parametro de opciones
 * 
 * Genera un paginado dinamico y gestiona las peticiones
 * AJAX.
 * Genera un evento eParent:indexChanged al concretar una 
 * peticion. Este evento lleva como memo el numero de la 
 * pagina gestionada.
 * 
 * La informacion recibida por ajax se asigna a un cache 
 * (this.cachedData), con lo cual solo se genera la peticion
 * la primera vez que se solicita la pagina deseada.
 * 
 * Variables:
 * 
 * @var string targetAction : Modulo server side al que apuntan las peticiones AJAX.
 * @var string template : Plantilla HTML que tienen los indices (ver: http://www.prototypejs.org/api/template/).
 * @var string eParent : ID del elemento y de la instancia de AJAXPagination.
 * @var int maxPages : Numero de paginas totales.
 * @var int actualPage : Pagina inicial.
 * @var int offset : Cantidad de elementos que se mostraran a cada lado de la pagina
 * 									actualmente seleccionada.
 * @var bool allowCache : Cache permitido | no permitido. Caso de que no, agrega a la
 * 									peticion un argumento aleatorio como variable GET, y se regenerara
 * 									los datos AJAX recibidos con cada peticion.
 * 
 * Los indices a mostrar son:
 * 
 * [PRIMERA][OFFSET][ACTUAL_PAGE][OFFSET][ULTIMA]
 * 
 * Con un offset = 3 y 15 elementos, estando en el 13:
 * 
 * [PRIMERA]
 * [10][11][12]
 * [13]
 * [14][15][16]
 * [ULTIMA]
 * 
 * La clase gestiona los indices en caso de que los offsets pasen los limites (PRIMERA / ULTIMA)
 * 
 * TODO: 
 * 	 	* Generar un mejor sistema de argumentos, ver cuales son opciones y cuales no (copiar el sistema Prototype).
 * 		* Mandar los labels como opciones y opciones por defecto
 * 		* Generar errores customizados
 * 
 * NOTAS:
 * 		No se utiliza javascript no intrusivo por el simple hecho de que es mucho mas 
 * 		sencillo que asociar y desasociar observers en cada regeneracion del indice.
 */
var AJAXPagination = Class.create({
	initialize : function(targetAction, template, eParent, maxPages, actualPage, options) {
		//console.log(eParent+' AJAXPagination created!');
		this.targetAction = targetAction;
		this.template 		= new Template(template);
		
		this.actualPage 		= 1;	
		this.lastPage				= null;	
		this.currentWorking = false;

		this.maxPages 		= parseInt(maxPages);
		this.params				= null;	
		this.cachedData		= [];

		this.jsonResponse = true;
		/*
		 * Opciones
		 */
		this.options = Object.extend({			
			allowCache: 		true,

			ajaxMethod:			'GET',
			params_GET:			{}, // Parametros GET por defecto
			params_IEF:			[],	// Parametros para agregar a la consulta, se agregan despues del ID (home/cargoAJAX/#{id}/param1/param2)		
			params_POST:		{}, // Parametros POST por defecto
			
			loading:				true,
			loadingObj:			null,			
			loadingImg:			'bigSnakeOrange',
			loadingImgExt:	'gif',
			
			labels:					{ first: 'PRIMERA', last: 'ULTIMA', numeric: []},
			activeClass: 		'activeItem',
			labeledClass: 	'text',
			
			offset:					3,
					
			onIndexChange : null
				
		}, options || {});
		
	
		if(maxPages > 2) {
			this.options.labels.numeric[1] 				= this.options.labels.first;
			this.options.labels.numeric[maxPages] = this.options.labels.last;
		}
			 
	 	this.maxItems			= (this.options.offset * 2) + 3;
	 	
		if(!$(eParent)) {
			this.triggerError('Error: Parent is null! ['+eParent+']');
		} else {
			this.parentE = $(eParent);
			this.parentN = eParent;
		}
				
		this.updatePagination(true);
	},
	/**
	 * Actualiza los indices y dispara un evento 
	 * _parentE_name :indexChanged que contiene
	 *  informacion adicional:
	 * 		{ 
	 * 			page: INDICE_ACTUAL
	 * 		}
	 */
	setPage : function(element) {

		if(element.id == undefined) {
			///console.log('ERROR: id no definido.\n'+element );
		}
		var id = element.id.split('__');	
		id = id[1];
		id = parseInt(id);

		if(this.actualPage == id || this.currentWorking) {
			return false;
		}	

		this.setCurrentState();	
		this.lastPage		= this.actualPage;
		this.actualPage = id;
	
		/*
		 * Si existe la informacion cacheada la utilizamos
		 */	
		if(this.options.allowCache && this.cachedData[id] && this.cachedData[id] != "undefined") {
			this.updatePagination();
			this.fireEv();
			this.setCurrentState();										
			return true;		
		}
		/*
		 * Si no existe, la buscamos mediante una peticion AJAX
		 */
		if(this.options.ajaxMethod == 'GET') {
			this.params = this.options.params_GET;
		} else {
			this.params = this.options.params_POST;			
		}
		
		if(!this.options.allowCache) {
			this.params.rand = Math.ceil(Math.random(4)*1000);
		}
		
		var url = this._setTarget(id);
		
		new Ajax.Request(url,
			{ 
				method: this.options.ajaxMethod,
				parameters: this.params,
				onSuccess: function(xhr) {
					var response = this.jsonResponse ? xhr.responseText.evalJSON() : xhr.responseText ;
					this.cachedData[this.actualPage] = response;			
					this.updatePagination();
					this.fireEv();
				}.bind(this),
				onComplete: function() {
					this.setCurrentState();	
				}.bind(this)
			}
		);		
			
	},
	_setTarget : function(id) {
		var _url = this.targetAction+id;
		if(this.options.params_IEF.lenght) {
			this.options.params_IEF.each( function(e) {
				_url += '/'+e;
			})
		}		
		return _url;
	},	
	setCurrentState : function() {
		if(!this.currentWorking) {
			this.currentWorking = true;		
			if(this.options.loading) {
				if(!$('AJAXPaginationLoading')) {
					var img = new Element(
						'img', 
						{ 						
							src: _base_url+'js/ajaxPagination/'+this.options.loadingImg+'.'+this.options.loadingImgExt, 
							alt: 'Cargando'
						}
					);
					var loading = new Element(
						'div', 
						{
							id: 'AJAXPaginationLoading', 
							style: 'display: block',
							'class': this.options.loadingImg
						}
					).update(img);					
					document.body.insert(loading);
					this._centerLoadingImg();
				} else {
					this._centerLoadingImg();
					$('AJAXPaginationLoading').show();
				}
			} else {
				//nada
			}
		} else {
			this.currentWorking = false;			
			if($('AJAXPaginationLoading')) {
				$('AJAXPaginationLoading').hide();
			}
		}
	},
	_centerLoadingImg : function() {
		var _dim = document.viewport.getDimensions();
		var _loadingDim = $('AJAXPaginationLoading').getDimensions();
		var _left 	= (_dim.width-_loadingDim.width)/2; 
		var _top 	= (_dim.height-_loadingDim.height)/2;
		$('AJAXPaginationLoading').setStyle({top: _top+'px', left: _left+'px'});		
	},	
	updatePagination : function(forceReDraw) {
	
		try {
			if(this.cachedData[this.actualPage] != undefined) {				
				var f;
				if(!this.options.onIndexChange.isFunction()) {
					f = eval(this.options.onIndexChange);
				} else {
					f = this.options.onIndexChange;
				}				
				f(this.actualPage, this.cachedData[this.actualPage], this.lastPage);
			}
		}	catch(e) {
			//throw('Callback is not defined!');				
		}		
					
	   if(forceReDraw == "undefined") {
	      forceReDraw = false;
	   }		
		/*
		 * Si la cantidad de indices totales es menor a maxItems no redibujamos 
		 */
		if(this.maxPages <= this.maxItems && !forceReDraw) {
			this.parentE.childElements().invoke('removeClassName', this.options.activeClass);	
			$(this.parentN+'__'+this.actualPage).addClassName(this.options.activeClass);							
			return true;
		}
		/**
		 * @var Array Lista de indices a dibujar
		 */
		var _items = [];		
		if(this.maxPages <= this.maxItems) {
			for(var i = 1; i < this.maxPages + 1; i++) {
				_items.push(i);
			}
		}	else {
			/*
			 * Definimos los limites de los offsets
			 */
			var topLimit 	= 0;
			var baseLimit = 0;		
	
			if(this.actualPage - this.options.offset <= 1) {
				topLimit += this.actualPage + this.options.offset + (this.options.offset - this.actualPage + 2);			
			} else {
				topLimit += (this.actualPage + this.options.offset) > this.maxPages - 1 ? this.maxPages - 1 : this.actualPage + this.options.offset;
			}
		
			if(this.actualPage + this.options.offset > this.maxPages - 1) {
				baseLimit += this.actualPage - (this.options.offset + ((this.actualPage + this.options.offset) - this.maxPages + 1));
			} else {
				baseLimit += (this.actualPage - this.options.offset) < 2 ? 2 : (this.actualPage - this.options.offset);
			}
				
			_items.push(1);
			_items.push(this.maxPages);
			
			if(this.actualPage != 1 && this.actualPage != this.maxPages) {
				_items.push(this.actualPage);
			}
			
			for(var i = this.actualPage - 1; i > baseLimit - 1; i--) {
				_items.push(i);
			}
			
			for(var i = this.actualPage + 1; i < topLimit + 1; i++) {
				_items.push(i);
			}
		}
				
		/*
		 * Updateamos los indices
		 */		 
		_items.sort(function(a,b){return a - b});
		html = '';
		
		_items.each(function(e) { 			
			html += this.parseTemplate(e);
		}.bind(this));

		this.parentE.update(html);

	},
	parseTemplate : function(_index) {
		var data = { klass: '', target: this.parentN+'.setPage(this)', idItem: '', page: ''};			
		if(_index == this.actualPage) {				
			data.klass = this.options.activeClass;
		} else {
			data.klass = '';				
		}
		data.idItem = this.parentN+'__'+_index;

		if(this.options.labels.numeric[_index] && this.options.labels.numeric[_index] != 'undefined') {
			data.page 	= this.options.labels.numeric[_index];
			data.klass 	+= ' '+this.options.labeledClass;				
		} else {
			data.page = _index;
		}		
		return this.template.evaluate(data);
	},
	fireEv : function() {
		var _target = $(this.parentN+'__'+this.actualPage);
		var evName = this.parentN+':indexChanged';
		//console.log(evName);
		_target.fire(evName, { page : this.actualPage });
	},
	triggerError : function(text) {
		throw new Error(text)	
	}
}); 


