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

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.log4j.Appender;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;

import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.logs.base.Q70ConfiguracionLog;
import es.ejie.frmk.infraestructura.logs.base.Q70ConstantesLogs;
import es.ejie.frmk.infraestructura.logs.base.Q70LogInterno;
import es.ejie.frmk.infraestructura.logs.base.Q70LogRemotoAppender;
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.utilidades.concurrencia.Q70Lock;

/**
 * Implementación específica de Q70LogFactory para log4j.
 * Se encarga de crear y configurar logs.
 * @author Carlos Rodriguez
 * @version 1.0  fecha: 10-dic-03
 * 
 */
public class Q70FactoriaLogImpl implements Q70LogFactory
{
	private final int MAX_BACKUP_INDEX = 5;
	private final int KBYTE = 1024;

	/**
	 * Este metodo debe eliminar del log4j los logs y/o trazas creados por Geremua 
	 * para un servidor o para una aplicacion.
	 * No se puede hacer 'LogManager.getLoggerRepository().shutdown();', ya que log4j para Geremua 
	 * esta a nivel de classpath de servidor y ejecutar este codigo implica que se elimina cualquier
	 * log existente en el repositorio, aunque no sea de una aplicacion de Geremua.
	 * 
	 * @see es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogFactory#flushAll()
	 */
	public void flushAll()
	{
	}

	/**
	 * Devuelve un log configurado de acuerdo a los parámetros
	 * @param name Nombre del log.
	 * @param config datos sobre la configuración (fichero, consola, ...)
	 * @param commonDataManager.
	 * @return El log configurado.  
	 */
	public Q70Log getLog(String name, Q70ConfiguracionLog config, Q70GestorDatosComunes commonDataManager, Q70Lock lock) throws Q70FrameworkException
	{
		try {
			//configurar el log4j
			BasicConfigurator.configure();
			LogManager.getRootLogger().removeAllAppenders();
			//obtener un logger con el nombre del log
			Logger logger = Logger.getLogger(name);
			//borrar los appenders si los hubiera
			logger.removeAllAppenders();
			//no heredar loggers del root.
			logger.setAdditivity(false);

			//desactivar el logger si la propiedad de configuración activo es distinta de "SI"
			if (config.getProperty(Q70ConstantesLogs.ACTIVO).toUpperCase().equals(Q70ConstantesLogs.SI))
				logger.setLevel(Level.DEBUG);
			else
				logger.setLevel(Level.OFF);

			//Obtener los campos a recoger del MDC.
			String camposGlobales = config.getProperty(Q70ConstantesLogs.ATRIBUTOS_GENERICOS);
			StringTokenizer st = new StringTokenizer(camposGlobales, Q70ConstantesLogs.DELIMITER);
			int numberOfAttributes = st.countTokens();
			String[] globalData = new String[numberOfAttributes];
			for (int i = 0; i < numberOfAttributes; i++)
			{
				globalData[i] = st.nextToken();
				LogLog.debug(globalData[i]);
			}
			//delegar en una función específica según el destino.
			//en futuras versiónes e podría hacer dinámico a través de una factoría y un interfaz con un método
			// configureLog.
			if (config.getProperty(Q70ConstantesLogs.DESTINO).equals(Q70ConstantesLogs.TARGET_DATABASE)) {
				configureBD(logger, config, commonDataManager, globalData);
			} else if (config.getProperty(Q70ConstantesLogs.DESTINO).equals(Q70ConstantesLogs.TARGET_DATE_BACKUP_FILE)) {
				configureDateBackupFile(logger, config, commonDataManager);
			} else if (config.getProperty(Q70ConstantesLogs.DESTINO).equals(Q70ConstantesLogs.TARGET_SIZE_BACKUP_FILE)){
				configureSizeBackupFile(logger, config, commonDataManager);
			} else {//por defecto a la consola 
				configureConsole(logger, config, commonDataManager);
			}
			
			//cargamos el Appender en el logger si está activo
			if  ((config.getProperty(Q70ConstantesLogs.LOG_REMOTO_ACTIVO) != null) && (config.getProperty(Q70ConstantesLogs.LOG_REMOTO_ACTIVO).toUpperCase().equals(Q70ConstantesLogs.SI))){
				/**
				 * TODO GEREMUA FASE 2. Deshabilitación por código del Log Remoto. Descomentar para habilitar el funcionamiento
				 * del Appender
				 */
				/**
				 * FIXME GEREMUA FASE 2. Deshabilitación por código del Log Remoto. Descomentar para habilitar el funcionamiento
				 * del Appender
				 */
				//configureLogRemoto(logger, config, commonDataManager);
			}
			
			Q70Log log = new Q70LogImpl(logger, lock);
			return log;
		} catch (Exception e) {
			throw new Q70FrameworkException(Q70ConstantesLogs.ID_COMPONENTE,this.getClass().toString(),"getLog","Se ha producido un error en el método getLog.",e);
		}

	}
	
	/**
	 * NO BORRAR ESTE METODO, para cuando se active el log remoto.
	 * @param logger
	 * @param config
	 * @param commonDataManager
	 * @throws Q70FrameworkException
	 */
	private void configureLogRemoto (Logger logger, Q70ConfiguracionLog config, Q70GestorDatosComunes commonDataManager) throws Q70FrameworkException{
		Appender appenderRemoto;
		try {
			appenderRemoto = Q70LogRemotoAppender.getLogAppender();
			appenderRemoto.setLayout(new Q70MensajeColumnaLayout(commonDataManager,
					config.getProperty(Q70ConstantesLogs.ATRIBUTOS_GENERICOS),
					config.getProperty(Q70ConstantesLogs.ATRIBUTOS_MENSAJE)));
			logger.addAppender(appenderRemoto);
			System.out.println("Se ha cargado correctamente el Appender Remoto: "+config.toString());
		} catch (Throwable e) {
			if(Q70LogInterno.isEnabled()){
				Q70LogInterno.log("el Appender Remoto definido para el Gestor de Logs y Trazas no se ha podido inicializar. Mirar la salida de errores");
			}
			System.out.println("No se ha inicializado el Appender Remoto: "+config.toString());
			throw new Q70FrameworkException(Q70ConstantesLogs.ID_COMPONENTE,this.getClass().toString(),"configureLogRemoto","Se ha producido un error en el método configureLogRemoto.",e);
		}
		

	}

	private void configureBD(Logger logger, Q70ConfiguracionLog config, Q70GestorDatosComunes commonDataManager, String[] globalDataArray)
	{
		LogLog.debug("salida a bbdd");
		Q70DataSourceJDBCAppender app = new Q70DataSourceJDBCAppender(commonDataManager);
		app.setName("APP D " + logger.getName());
		app.setTable(config.getProperty(Q70ConstantesLogs.TABLA));
		app.setMDCAttributes(globalDataArray);
		app.setDataSourceName(config.getProperty(Q70ConstantesLogs.DATASOURCE));
		app.setBufferSize(Integer.parseInt(config.getProperty(Q70ConstantesLogs.BUFFERSIZE)));
		if(app==null)
			System.err.println(logger.getName() + ": Error al configurar el log en la base de datos.");
		else
			logger.addAppender(app);
	}

	private void configureDateBackupFile(Logger logger, Q70ConfiguracionLog config, Q70GestorDatosComunes commonDataManager)
	{
		LogLog.debug("salida a fichero backup por fecha");
		DailyRollingFileAppender appender = null;
		try
		{
			appender =
				new DailyRollingFileAppender(
					new Q70MensajeColumnaLayout(commonDataManager,
						config.getProperty(Q70ConstantesLogs.ATRIBUTOS_GENERICOS),
						config.getProperty(Q70ConstantesLogs.ATRIBUTOS_MENSAJE)),
					OptionConverter.substVars(config.getProperty(Q70ConstantesLogs.FICHERO), null),
					config.getProperty(Q70ConstantesLogs.PATRON_FRECUENCIA));
			appender.setName("APP F " + logger.getName());
			int bufferSize = Integer.parseInt(config.getProperty(Q70ConstantesLogs.BUFFERSIZE));
			if (bufferSize != 0)
			{
				appender.setBufferedIO(true);
				appender.setBufferSize(bufferSize * KBYTE);
				appender.setImmediateFlush(false);
			}

		}
		catch (IOException ioe)
		{
			System.err.println(logger.getName() + ": Error al crear el fichero de log " + OptionConverter.substVars(config.getProperty(Q70ConstantesLogs.FICHERO), null));
		}
		if(appender==null)
			System.err.println(logger.getName() + ": Error al configurar el log para salida a fichero con backup por fechas.");
		else
			logger.addAppender(appender);

	}

	private void configureSizeBackupFile(Logger logger, Q70ConfiguracionLog config, Q70GestorDatosComunes commonDataManager)
	{
		LogLog.debug("salida a fichero, backup por tamaño");
		RollingFileAppender appender = null;
		try
		{
			appender =
				new RollingFileAppender(
					new Q70MensajeColumnaLayout(commonDataManager,
						config.getProperty(Q70ConstantesLogs.ATRIBUTOS_GENERICOS),
						config.getProperty(Q70ConstantesLogs.ATRIBUTOS_MENSAJE)),
					OptionConverter.substVars(config.getProperty(Q70ConstantesLogs.FICHERO), null));
			appender.setMaxBackupIndex(MAX_BACKUP_INDEX);
			appender.setMaxFileSize(config.getProperty(Q70ConstantesLogs.TAMANO_MAXIMO));
			appender.setName("APP F " + logger.getName());
			int bufferSize = Integer.parseInt(config.getProperty(Q70ConstantesLogs.BUFFERSIZE));
			if (bufferSize != 0)
			{
				appender.setBufferedIO(true);
				appender.setBufferSize(bufferSize * KBYTE);
				appender.setImmediateFlush(false);
			}

		}
		catch (IOException ioe)
		{
			System.err.println(logger.getName() + ": Error al crear el fichero de log " + OptionConverter.substVars(config.getProperty(Q70ConstantesLogs.FICHERO), null));
		}
		if(appender==null)
				System.err.println(logger.getName() + ": Error al configurar el log con salida a fichero con backup por tamaño.");
		else
			logger.addAppender(appender);

	}

	private void configureConsole(Logger logger, Q70ConfiguracionLog config, Q70GestorDatosComunes commonDataManager)
	{
		LogLog.debug("salida a Consola");
		Q70MensajeColumnaLayout mensajeColumnaLayout =new Q70MensajeColumnaLayout(commonDataManager,
			config.getProperty(Q70ConstantesLogs.ATRIBUTOS_GENERICOS),
			config.getProperty(Q70ConstantesLogs.ATRIBUTOS_MENSAJE));
		mensajeColumnaLayout.addLoggerName(true);
		ConsoleAppender appender =new ConsoleAppender(mensajeColumnaLayout);
		appender.setName("APP C " + logger.getName());
		if(appender==null)
			System.err.println(logger.getName() + ": Error al configurar el log con salida a consola");
		else
			logger.addAppender(appender);
	}
}
