/**
 * PersonalizacaoUtil
 *
 * Esta classe contem metodos utilitarios para gerir o controle de drag-and-drop de modulos
 * juntamente com as funcionalidades de personalizacao, isto eh, armazenamento das
 * preferencias do usuario em banco de dados.
 *
 * Para utilizar, basta executar o metodo <code>PersonalizacaoUtil.init()</code> durante
 * o onLoad da pagina.
 */

	// usado para debug do DWR
	function handler( msg ) {
		//throw msg;
	}

	var PersonalizacaoUtil = new Object();

	var handle = null;

	PersonalizacaoUtil.config = {
		updateIntervalMillis : 1000 * 10, // 10s
		flushOnUnload : true,
		cookiePrefix : "userPreferences" ,
		loadAvailableModulesCallback : null,
		siteId : null
	};

	PersonalizacaoUtil.modules = new Array();

	PersonalizacaoUtil.getModules = function() {
		return this.modules;
	}

	PersonalizacaoUtil.getModuleById = function( id ) {
		for ( var i = 0; i < this.modules.length; i++ ) {
			if ( this.modules[i].id == id ) return this.modules[i];
		}
		return null;
	}

	/**
	 * Dispara a inicializacao de todos os mecanismos javascript da personalizacao.
	 * Especificamente, este metodo dispara o init do drag-and-drop em cada modulo,
	 * adiciona os handlers para o flush das preferencias (no timer, e no onUnload),
	 * e carrega as preferencias do usuario a partir do banco de dados.
	 */
	PersonalizacaoUtil.init = function() {

		//Debug de erros do DWR
		DWREngine.setErrorHandler( handler );
		DWREngine.setWarningHandler( handler );
		DWREngine.setTimeout( 1000 * 11 );

	     // Dispara o init do drag-n-drop
	    initModuleDrags();

		// Pluga a callback que grava as preferencias quando o drop acontece, remo??o e adi??o de m?dulos
		_onDragEnd = _onAddModule = _onRemoveModule = function ( x, y, moduleInfo ) {
			PersonalizacaoUtil.saveState( _moduleColumns );
		}

	  	// Pluga o flush no onUnload da p?gina, se desejado
		if ( this.config.flushOnUnload ) {
	  		addUnload( function() { PersonalizacaoUtil.flushState() } );
		}

	  	// Dispara o flush em intervalos periodicos, se desejado
		if ( this.config.updateIntervalMillis != null ) {
			handle = setInterval( function() { PersonalizacaoUtil.flushState() }, this.config.updateIntervalMillis );
		}

		// Carrega a lista de modulos possiveis do servidor e guarda em cache
		PersonalizacaoController.getAvailableModules( function( modules ) {
			PersonalizacaoUtil.modules = modules;

			if ( PersonalizacaoUtil.config.loadAvailableModulesCallback ) {
				PersonalizacaoUtil.config.loadAvailableModulesCallback( modules );
			}
		});
		// Carrega o estado do servidor
		PersonalizacaoUtil.loadState();
		// Carrega menu de personalizacao
		PersonalizacaoUtil.loadMenu();
	}

	/**
	 * Descobre os m?dulos do usu?rio logado (ou os m?dulos do usu?rio
	 * an?nimo) e os coloca nas posi??es apropriadas.
	 */
	PersonalizacaoUtil.loadState = function() {
        var prefix = this.config.cookiePrefix;

		PersonalizacaoController.loadModulePositions( function( prefs ) {

			var lastUpdate = readCookie( prefix + "LastUpdate" );
			var lastFlush = readCookie( prefix + "LastFlush" );

			var useCookie = false;
			if ( ( lastUpdate != null ) &&
				 ( lastFlush == null ) || ( parseInt( lastUpdate ) > parseInt( lastFlush ) ) ) {
			 	useCookie = true;
			}

			if ( useCookie && logado()) {
				var currentModulePositions = readCookie( prefix + "ModuleState" );
				if ((lastFlush != null) &&
				    (currentModulePositions != null) &&
				    (currentModulePositions != "")) {
	    			var currentArray = PersonalizacaoUtil.buildStateFromString( currentModulePositions );
		    		for ( var i = 0; i < currentArray.length; i++ ) {
			    		for ( var j = 0; j < currentArray[i].length; j++ ) {
				    		// Chamada para a API do dnd
				    		if (currentArray[i][j]!=null && currentArray[i][j]!="") {
						    	var moduleDef = PersonalizacaoUtil.getModuleById( currentArray[i][j] );
						    	if (moduleDef != null) {
	    							addModule( i, j, moduleDef );
	    						}
    						}
	    				}
		    		}
				} else {
				    for ( var i = 0; i < prefs.length; i++ ) {
					    for ( var j = 0; j < prefs[i].length; j++ ) {
						    // Chamada para a API do dnd
						    addModule( i, j, prefs[i][j] );
					    }
				    }

				}
				PersonalizacaoUtil.saveState( _moduleColumns );

			} else {

				for ( var i = 0; i < prefs.length; i++ ) {
					for ( var j = 0; j < prefs[i].length; j++ ) {

						// Chamada para a API do dnd
						addModule( i, j, prefs[i][j] );
					}
				}
			}
		});
	}

	/**
	 * Grava o estado dos m?dulos que o usu?rio escolheu. Esta fun??o
	 * grava as prefer?ncias em um cookie; para enviar as prefer?ncias
	 * para o servidor, veja a fun??o flushState().
	 *
	 * @param moduleColumns Array de objetos ModuleColumn representando
	 * o estado a ser persistido.
	 */
	PersonalizacaoUtil.saveState = function( moduleColumns ) {
	  // Serializa o estado em uma string, e coloca essa string em um cookie.
	  var serialized = PersonalizacaoUtil.serializeState( moduleColumns );
	  createCookie( this.config.cookiePrefix + "ModuleState", serialized );

		//$("log2").innerHTML = "state: " + serialized;

	  // Guarda a data atual em um cookie, para saber quando foi feita a ?ltima
	  // atualiza??o pelo usu?rio.
	  var lastUpdateTime = new Date().getTime();
	  createCookie( this.config.cookiePrefix + "LastUpdate", lastUpdateTime );
	}

	/**
	 * Carrega os modulos default
	 */
	PersonalizacaoUtil.loadDefaultModules = function() {
		PersonalizacaoController.loadDefaultModulesPosition( function( prefs ) {
			for ( var i = 0; i < prefs.length; i++ ) {
				for ( var j = 0; j < prefs[i].length; j++ ) {
					// Chamada para a API do dnd
					addModule( i, j, prefs[i][j] );
				}
			}
		});
	}

	/**
	 * Serializa um estado para grava??o em cookie. O estado ? uma
	 * lista de listas de IDs de m?dulos. As listas externas s?o
	 * separadas com ";" e as internas com "," . Exemplo:
	 *
	 * <pre>
	 *  1, 2, 3 ; 4 ; 5, 6
	 *  corresponde a:
	 *  [
	 *   [ 1, 2, 3 ] ,
	 *   [ 4 ] ,
	 *   [ 5, 6 ]
	 *  ]
	 * </pre>
	 *
	 * @param moduleColumns array de ModuleColumn representando o estado.
	 */
	PersonalizacaoUtil.serializeState = function( moduleColumns ) {
	  var result = "";
	  for ( var i = 0; i < moduleColumns.length; i++ ) {
	    var oneColumn = moduleColumns[i];
	    for ( var j = 0; j < oneColumn.modules.length; j++ ) {
	      var oneModule = oneColumn.modules[j];

	      result += oneModule.moduleId;

	      if ( j < oneColumn.modules.length - 1 ) {
	        result += ", ";
	      }
	    }

	    if ( i < moduleColumns.length - 1 ) {
	      result += "| ";
	    }
	  }

	  return result;
	}



	/**
	 * Deserializa um estado guardado em uma string. Veja o formato
	 * em <code>serializeState()</code>.
	 *
	 * @param strState String com o estado serializado.
	 * @return Array de arrays com os IDs dos m?dulos em cada coluna.
	 */
	PersonalizacaoUtil.buildStateFromString = function( strState ) {
	  var result = new Array();

	  // Divide a string nos "|" para obter as partes de cada coluna
	  var strCols = strState.split( "|" );

	  // Adiciona uma coluna para cada string.
	  for ( var i = 0; i < strCols.length; i++ ) {

	    // Quebra a string de cada coluna nos ","
	    var oneColumn = strCols[i].split( "," );
	    result.push( oneColumn );

	    // Faz um trim em cada string
	    for ( var j = 0; j < oneColumn.length; j++ ) oneColumn[j] = oneColumn[j].trim();

	  }

	  return result;
	}



	/**
	 * Dispara a grava??o do estado no servidor. Esta fun??o determina
	 * se isso ? necess?rio ou n?o dependendo das datas de ?ltima
	 * atualiza??o de estado / ?ltimo flush de estado. Essas informa??es,
	 * bem como o estado a ser enviado, s?o recuperadas de cookies.
	 *
	 * Esta fun??o foi projetada para ser chamada em um timeout ou interval.
	 */
	PersonalizacaoUtil.flushState = function( force, erase ) {
	  // Descobre quando foi o ?ltimo flush e compara com a ?ltima atualiza??o do usu?rio.
	  var lastUpdate = readCookie( this.config.cookiePrefix + "LastUpdate" );
	  var lastFlush = readCookie( this.config.cookiePrefix + "LastFlush" );

	  //alert( "update/flush " + lastUpdate + "/" + lastFlush );

		// Ignora a "inteligencia" se o force esta true.
		if ( ! force ) {
		  // Se n?o existe um lastUpdate, n?o faz nada
		  if ( lastUpdate == null ) return;

		  // Existe, mas tamb?m existe um lastFlush feito depois; tamb?m n?o faz nada
		  if ( ( lastFlush != null ) &&
		  	   ( parseInt( lastFlush ) >= parseInt( lastUpdate ) ) ) return;
		}

	  // Chega aqui se existe um update, e n?o existe um flush adequado; executa o flush
	  // e guarda essa informa??o no cookie.
	  var serializedState = readCookie( this.config.cookiePrefix + "ModuleState" );
	  var now = new Date().getTime();

	  // NOTA: S? cria o cookie se o retorno da fun??o for OK e n?o uma exce??o. (desta
	  // forma, se ocorrer um erro, o cookie n?o ser? setado e o pr?ximo timeout vai
	  // tentar novamente). Al?m disso, mesmo se executado mais tarde, a data gravada no
	  // cookie ? a data de submiss?o de dados (ent?o, se acontece uma altera??o durante
	  // a chamada, ela ter? uma data posterior ? data no cookie, e ser? necess?rio mais
	  // um flush para grav?-la).
		var prefix = this.config.cookiePrefix;
	  PersonalizacaoController.saveModulePositions( serializedState, function() {
	    createCookie( prefix + "LastFlush", now );
	    if (erase) {
            eraseCookie(prefix + "LastFlush");
	    }
	  });

	}

   /**
    * Logout da personalizacao
    */
	PersonalizacaoUtil.logout = function() {
		if (handle != null) {
			clearInterval(handle);
		}
	    PersonalizacaoUtil.flushState(true, true);
		var modulos = PersonalizacaoUtil.getModules();
	    for (i =0; i < modulos.length; i++) {
	    	removeModule(modulos[i].id);
	    }
	    PersonalizacaoUtil.loadDefaultModules();
	}

	/**
	 * Carrega o menu da pagina
	 */
	PersonalizacaoUtil.loadMenu = function() {
		// callback que monta a tab para adi??o de novos modulos
		PersonalizacaoUtil.config.loadAvailableModulesCallback = function( modules ) {
			//Obtem o lugar onde os links do modulos ser??o escrito
			var ulModules = $("availableModules");
			//Variaveis auxiliares
			var previous = null;
			var iSubMenu = 0;
			var ulCategory = null;
			var liCategory = null;
			// Itera por cada modulo, criando links para adicionar cada um
			for (var i = 0; i < modules.length; i++ ) {
				//Verifica se existe uma categoria
				if (modules[i].categoryLabel != null) {
					//Se ?? um nova categoria...
					if (previous == null || previous.categoryLabel != modules[i].categoryLabel) {
						//Salva a categoria anterior
						if (ulCategory!=null && liCategory != null) {
							liCategory.appendChild(ulCategory);
							//Atualiza na raiz
							ulModules.appendChild( liCategory );
						}
						liCategory = $new( "li" );
						//Link de subMenu
						liCategory.innerHTML = "<a id=\"link" + iSubMenu + "\" href=\"javascript:subMenuOnClick(\'" + (iSubMenu) + "\');\">" +
							modules[i].categoryLabel + "</a>";
						//Cria uma nova categoria
						ulCategory = $new ( "ul" );
						ulCategory.setAttribute("id", "submenu" + iSubMenu);
						ulCategory.setAttribute("class", "submenu");
						ulCategory.className = "submenu";
						ulCategory.style.display = "none";
						iSubMenu++;
					}
					//Adiciona os sub-itens
					if (ulCategory != null) {
						var liModule = $new( "li" );
						liModule.innerHTML = "<a href=\"javascript:exameAddModule(" + modules[i].id + ");\">" + modules[i].label + "</a>";
						ulCategory.appendChild( liModule );
					}
				//Se n??o for...
				} else {
					//Salva a categoria anterior
					var liModule = $new( "li" );
					if (ulCategory != null) {
						liCategory.appendChild(ulCategory);
						//Atualiza na raiz
						ulModules.appendChild( liCategory );
					}
					//Inclui um item sem categoria
					ulCategory = null;
					liModule.innerHTML = "<a href=\"javascript:exameAddModule(" + modules[i].id + ");\">" + modules[i].label + "</a>";
					ulModules.appendChild( liModule );
				}
				previous = modules[i];
			}
			//Ultima categoria
			if (ulCategory != null) {
				liCategory.appendChild(ulCategory);
				//Atualiza na raiz
				ulModules.appendChild( liCategory );
			}
		}
	}


