package es.ejie.frmk.infraestructura.logs.base;

import java.util.HashMap;
import java.util.Iterator;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.logs.base.impl.Q70FactoriaLogImpl;
import es.ejie.frmk.infraestructura.logs.base.impl.Q70GestorDatosComunesImpl;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70GestorDatosComunes;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70Log;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogFactory;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogFlushable;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogServidorInterface;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;
import es.ejie.frmk.infraestructura.utilidades.concurrencia.Q70Lock;

/**
 * Esta clase es la encargada de crear y configurar los diferentes logs de Geremua.
 * <p>
 * La configuración se hace a través del <b>ConfiguradorUniversal</b>.
 * por ello esta clase debe implementar el interfaz configurable. 
 * A su vez debe implementar Arrancable para poder ser instanciada por el Gestor
 * de arranque y configuración. Éste una vez obtenida la instancia a través del método
 * <b>getInstance</b> llamará al método <b>afterCreate</b> en el que se realizará la configuración.<p> 
 * 
 * @author Carlos Rodriguez
 * @version 1.0  
 * fecha: 10-dic-03
 * 
 */
public abstract class Q70GestorLogsBase implements Q70LogServidorInterface
{
	private Q70LogFactory logFactory = new Q70FactoriaLogImpl();
	private Q70GestorDatosComunes commonDataManager = new Q70GestorDatosComunesImpl();

	private HashMap logs = new HashMap();
	private Q70ConfiguracionLogs configuracionLogs = null;
	
	private Q70Lock lock = null;

	/**
	 * @return Returns the lock.
	 */
	protected Q70Lock getLock() {
		return lock;
	}
	
	/**
	 * @param lock The lock to set.
	 */
	protected void setLock(Q70Lock lock) {
		this.lock = lock;
	}
	
	/**
	* Proporciona acceso a los datos comunes que comparten los logs (id_usuario, id_sesion...)
	* @return la instancia del Q70GestorDatosComunes
	*/
	public Q70GestorDatosComunes getCommonDataManager()
	{
		getLock().entradaZonaCritica();		
		try {
			return commonDataManager;
		} catch (RuntimeException e) {
			throw e;
		} finally {
			getLock().salidaZonaCritica();
		}
	}

	/**
	* Permite acceder a un log concreto a través de su nombre
	* @param name nombre del log
	* @return Instancia del Q70Log o nulo si aún no se ha creado 
	*/
	public Object getLog(String name)
	{
		getLock().entradaZonaCritica();		
		try {
			return logs.get(name);			
		} catch (RuntimeException e) {
			throw e;
		} finally {
			getLock().salidaZonaCritica();
		}
	}

	/**
	 * Se envían los logs pendientes al fichero de logs para cada tipo de log guardado en una HashMap
	 * Cada tipo de log (incidencias, accessos, transacciones, conectores) tiene implementada la interfaz 
	 * Q70LogFlushable
	 */
	protected void flushPendingMessages()
	{
		Iterator iterator = logs.values().iterator();
		while (iterator.hasNext()){
			Q70LogFlushable logFlush= (Q70LogFlushable)iterator.next();
			logFlush.flush();
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#inicializar()
	 */
	public void inicializar() throws Q70FrameworkException
	{
		if (!getLock().isBloqueo()) {
			throw new Q70FrameworkException(Q70ConstantesLogs.LOG,this.getClass().toString(),"inicializar","Se ha entrado por un método que necesita que el bloqueo del componente esté activo.",null);
		}
		if (Q70LogInterno.isEnabled())
			Q70LogInterno.log("inicializar called: " + "\n        configuracion= " + getConfiguracionLogs());

		Iterator iterator = getConfiguracionLogs().getLogs();
		while (iterator.hasNext()){
			try {
				String logNameConfiguracion = (String)iterator.next();
				Q70Log log = logFactory.getLog(getNombreLog(logNameConfiguracion, getConfiguracionLogs()), getConfiguracionLogs().getConfiguracion(logNameConfiguracion), commonDataManager, getLock());
				Object logAux = getTipoLog(logNameConfiguracion, log);
				logs.put(logNameConfiguracion, logAux);
			} catch (Exception e) {
				throw new Q70FrameworkException(Q70ConstantesLogs.TRAZA,this.getClass().toString(),"inicializar","Se ha producido un error en el método inicializar.",e);		
			}					
		}
	}

	/**
	 * @param logNameConfiguracion
	 * @param log
	 * @return devuelve la instancia del tipo de Log  (navegacion, incidencias, transaccion, conectores) que estamos
	 * preguntando
	 */
	protected abstract Object getTipoLog(String logNameConfiguracion, Q70Log log);

	/**
	 * @param logName nombre del log
	 * @param configuracionLogs configuración del log
	 * @return
	 */
	protected String getNombreLog(String logName, Q70ConfiguracionLogs configuracionLogs) {
		return logName + getConfiguracionLogs().getConfiguracion(logName).getProperty(Q70ConstantesLogs.CODIGO_APLICACION);
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#liberar()
	 */
	public void liberar() throws Q70FrameworkException
	{
		if (!getLock().isBloqueo()) {
			throw new Q70FrameworkException(Q70ConstantesLogs.LOG,this.getClass().toString(),"liberar","Se ha entrado por un método que necesita que el bloqueo del componente esté activo.",null);
		}

		//llamada al método de envío de mensajes de logs pendientes
		flushPendingMessages();
		
		if (Q70LogInterno.isEnabled())
			Q70LogInterno.log("liberar called");
	}
	
	protected abstract void doDestruir();

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#activar()
	 */
	public void activar() {
		getLock().activar();
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#suspender()
	 */
	public void suspender() throws Q70FrameworkException{
		try {
			getLock().suspender();
		} catch (Exception e) {
			Q70LogInterno.log("\nERROR!!! Se ha producido un error en el método suspender de la clase Q70GestorLogsBase.\n");
			throw new Q70FrameworkException(Q70ConstantesLogs.ID_COMPONENTE,this.getClass().toString(),"suspender","Se ha producido un error en el método suspender.",e);
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#destruir()
	 */
	public void destruir() throws Q70FrameworkException{
		try {
			liberar();
			doDestruir();
			activar();
		} catch (Throwable e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(Q70ConstantesLogs.LOG, Q70TraceLevel.ERROR, "Se ha producido un error en el método destruir de la clase Q70GestorLogsBase");
			}
			throw new Q70FrameworkException(Q70ConstantesLogs.LOG,this.getClass().toString(),"destruir","Se ha producido un error en el método destruir.",e);
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogServidorInterface#getConfiguracionLogs()
	 */
	public Q70ConfiguracionLogs getConfiguracionLogs(){
		return configuracionLogs;	
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogServidorInterface#setConfiguracionLogs(es.ejie.frmk.infraestructura.logs.base.Q70ConfiguracionLogs)
	 */
	public void setConfiguracionLogs(Q70ConfiguracionLogs confLog) throws Q70FrameworkException{
		if (!getLock().isBloqueo()) {
			throw new Q70FrameworkException(Q70ConstantesLogs.LOG,this.getClass().toString(),"liberar","Se ha entrado por un método que necesita que el bloqueo del componente esté activo.",null);
		}
		configuracionLogs = confLog;
	}
}
