package es.ejie.frmk;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.control.excepciones.Q70GestorDeExcepcionesServidor;
import es.ejie.frmk.infraestructura.logs.accesos.Q70LogAccesosInterface;
import es.ejie.frmk.infraestructura.logs.base.Q70ConstantesLogs;
import es.ejie.frmk.infraestructura.logs.base.Q70GestorLogsServidor;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70LogServidorInterface;
import es.ejie.frmk.infraestructura.logs.conectores.Q70LogConectoresInterface;
import es.ejie.frmk.infraestructura.logs.incidencias.Q70LogIncidenciasInterface;
import es.ejie.frmk.infraestructura.logs.navegacion.Q70LogNavegacionInterface;
import es.ejie.frmk.infraestructura.logs.transacciones.Q70LogTransaccionesInterface;
import es.ejie.frmk.infraestructura.mail.Q70GestorMailInterface;
import es.ejie.frmk.infraestructura.mail.Q70GestorMailServidor;
import es.ejie.frmk.infraestructura.poolCache.Q70GestorCacheInterface;
import es.ejie.frmk.infraestructura.sms.Q70GestorSMSInterface;
import es.ejie.frmk.infraestructura.sms.Q70GestorSMSServidor;
import es.ejie.frmk.infraestructura.tablas.Q70Constantes;
import es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosInterface;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;
import es.ejie.frmk.infraestructura.traza.Q70TrazaServidor;


/**
 * Clase base que gestiona el mapeo y la obtención de los componentes de aplicación y de servidor mediante una hashMap
 * El mapeo de los componentes de aplicación se realiza referenciado los componentes de aplicación con la referencia  
 * accesible a los componentes de aplicación en el EAR (Q70FactoryKey). 
 * Es decir, tendremos una HashMap en que la clave primaria sera la referencia Q70FactoryKey del EAR y como contenido
 * un hashMap con los componentes de la aplicación.  
 * En ese hashMap se guardará el id del compontente y la clase compontente   
 * La filosofia en la obtención de los componentes es recuperar el componente de aplicación, y en caso de que no exista
 * es obtenerla del servidor.
 * @version 2.0   Fecha: 8-abr-2006
 */
public class Q70Factoria {

	private static Map mapaDeClasesDeAplicacion = new HashMap(11);
	
	private static String CTE_ID_APLICACION = "codAplicacion";
	
	private static String CTE_ID_CONTADOR = "contadorWarDesplegados";

	private static Collection listaAplicaciones = new HashSet();
	
	private static String COMPONENTE = "FACTORIA";
	
	/**
	 * Este metodo devuelve un componente de <b>Tablas Codigo</b> de Aplicacion en la ejecucion actual.
	 * En caso de que no se encuentre la clase Q70FactoriaKey, es decir, que no se haya definido
	 * una configuracion particular de aplicacion entonces retornara una excepción 
	 * @return el componente de aplicación para usar Tablas Codigo 
	 */
	public static Q70GestorTablasCodigosInterface getGestorTablasCodigos(){
		Class claseKey = getClassKey();	
		String idComponente = Q70Constantes.ID_COMPONENTE;
		
		return (Q70GestorTablasCodigosInterface)getComponente(claseKey, idComponente);
	}
	
	/**
	 * Mapea el componente <b>Tablas Codigos</b> con la referencia de acceso a los componentes de aplicacion y su ID
	 * de componente.
	 * @param componente
	 */
	public static void setGestorTablasCodigos(Object componente){
		Class claseKey = getClassKey();
		String idComponente = Q70Constantes.ID_COMPONENTE;
		
		putComponente(componente, claseKey, idComponente);
	}

	/**
	 * Este metodo devuelve un componente de <b>Cache</b> de Aplicacion en la ejecucion actual.
	 * En caso de que no se encuentre la clase Q70FactoriaKey, es decir, que no se haya definido
	 * una configuracion particular de aplicacion entonces retornara una excepción 
	 * @return el componente de aplicación para usar el Cache 
	 */
	public static Q70GestorCacheInterface getGestorCache(){
		Class claseKey = getClassKey();	
		String idComponente = es.ejie.frmk.infraestructura.poolCache.Q70Constantes.ID_COMPONENTE;
		
		return (Q70GestorCacheInterface)getComponente(claseKey, idComponente);
	}

	/**
	 * Mapea el componente <b>Cache</b> con la referencia de acceso a los componentes de aplicacion y su ID
	 * de componente.
	 * @param componente
	 */
	public static void setGestorCache(Object componente){
		Class claseKey = getClassKey();
		String idComponente = es.ejie.frmk.infraestructura.poolCache.Q70Constantes.ID_COMPONENTE;
		
		putComponente(componente, claseKey, idComponente);
	}
	
	/**
	 * Este metodo devuelve un componente de <b>Gestor de Mail</b> de Aplicacion en la ejecucion actual.
	 * En caso de que no se encuentre la clase Q70FactoriaKey, es decir, que no se haya definido
	 * una configuracion particular de aplicacion entonces retornara la instancia de componente 
	 * de <b>Gestor de Mail </b> de servidor. 
	 * @return el componente para usar el Gestor de Mail 
	 */
	public static Q70GestorMailInterface getGestorMail()
	{
		Class claseKey = null;
		try {
			//componente aplicacion
			claseKey = doClassForNameKey();
		} catch (ClassNotFoundException e) {
			//componente servidor
			return Q70GestorMailServidor.getSingleton();
		}
		
		String idComponente = es.ejie.frmk.infraestructura.mail.Q70Constantes.ID_COMPONENTE;
		Object componente = getComponente(claseKey, idComponente);
		if (componente==null){
			return Q70GestorMailServidor.getSingleton();
		}
		return (Q70GestorMailInterface)componente;
	}

	/**
	 * Mapea el componente <b>Gestor Mail</b> con la referencia de acceso a los componentes de aplicacion y su ID
	 * de componente.
	 * @param componente
	 */
	public static void setGestorMail (Object componente)
	{
		Class claseKey=getClassKey();
		String idComponente=es.ejie.frmk.infraestructura.mail.Q70Constantes.ID_COMPONENTE;
		
		putComponente(componente,claseKey,idComponente);
	}
	
	/**
	 * Este metodo devuelve un componente de <b>Gestor de SMS</b> de Aplicacion en la ejecucion actual.
	 * En caso de que no se encuentre la clase Q70FactoriaKey, es decir, que no se haya definido
	 * una configuracion particular de aplicacion entonces retornara la instancia de componente 
	 * de <b>Gestor de SMS </b> de servidor. 
	 * @return el componente para usar el Gestor de SMS 
	 */	
	public static Q70GestorSMSInterface getGestorSMS()
	{
		Class claseKey = null;
		try {
			claseKey = doClassForNameKey();
		} catch (ClassNotFoundException e) {
			return Q70GestorSMSServidor.getSingleton();
		}
		
		String idComponente = es.ejie.frmk.infraestructura.sms.Q70Constantes.ID_COMPONENTE;
		Object componente = getComponente(claseKey, idComponente);
		if (componente==null){
			return Q70GestorSMSServidor.getSingleton();
		}
		return (Q70GestorSMSInterface)componente;
	}

	/**
	 * Mapea el componente <b>Gestor SMS</b> con la referencia de acceso a los componentes de aplicacion y su ID
	 * de componente.
	 * @param componente
	 */
	public static void setGestorSMS (Object componente)
	{
		Class claseKey=getClassKey();
		String idComponente=es.ejie.frmk.infraestructura.sms.Q70Constantes.ID_COMPONENTE;
		
		putComponente(componente,claseKey,idComponente);
	}
	
	/**
	 * Este metodo devuelve un componente de <b>Traza</b> de Aplicacion en la ejecucion actual.
	 * En caso de que no se encuentre la clase Q70FactoriaKey, es decir, que no se haya definido
	 * una configuracion particular de aplicacion entonces retornara la instancia de componente 
	 * de Traza de servidor. 
	 * @return el componente para usar la Traza 
	 */
	public static Q70TrazaInterface getTraza(){
		Class claseKey = null;
		try {
			claseKey = doClassForNameKey();
		} catch (ClassNotFoundException e) {
			return Q70TrazaServidor.getSingleton();
		}
		
		String idComponente = Q70ConstantesLogs.TRAZA;
		Object componente = getComponente(claseKey, idComponente);
		if (componente==null){
			return Q70TrazaServidor.getSingleton();
		}
		return (Q70TrazaInterface)componente;
	}
	
	/**
	 * Mapea el componente <b>Traza</b> con la referencia de acceso a los componentes de aplicacion y su ID
	 * de componente.
	 * @param componente
	 */
	public static void setTraza(Object componente){
		Class claseKey = getClassKey();
		String idComponente = Q70ConstantesLogs.TRAZA;
		
		putComponente(componente, claseKey, idComponente);
	}
	
	/**
	 * Mapea el componente <b>Gestor de Logs</b> con la referencia de acceso a los componentes de aplicacion y su ID
	 * de componente.
	 * @param componente
	 */
	public static void setGestorLogs (Object componente){
		Class claseKey = getClassKey();
		String idComponente = Q70ConstantesLogs.LOG;
		
		putComponente (componente, claseKey, idComponente);
	}

	/**
	 * Este metodo devuelve un componente de <b>Gestor de Logs</b> de Aplicacion en la ejecucion actual.
	 * En caso de que no se encuentre la clase Q70FactoriaKey, es decir, que no se haya definido
	 * una configuracion particular de aplicacion entonces retornara la instancia de componente 
	 * de <b>Gestor de Logs </b> de servidor. 
	 * @return el componente para usar el Gestor de Logs 
	 */
	protected static Q70LogServidorInterface getGestorLogs(){
		Class claseKey = null;
		try {
			claseKey = doClassForNameKey();
		} catch (ClassNotFoundException e) {
			return Q70GestorLogsServidor.getSingleton();
		}
		
		String idComponente = Q70ConstantesLogs.LOG;
		Object componente = getComponente(claseKey, idComponente);
		if (componente==null){
			return Q70GestorLogsServidor.getSingleton();
		}
		return (Q70LogServidorInterface)componente;
	}

	/**
	 * Retorna la referencia del componente (aplicación/servidor) del <b>Gestor de Log de Accesso</b>
	 * @return el componente para usar el Gestor de Logs de Acceso
	 */
	public static Q70LogAccesosInterface getLogAccesos(){
		return (Q70LogAccesosInterface)getGestorLogs().getLog(Q70ConstantesLogs.LOG_ACCESOS);
	}

	/**
	 * Retorna la referencia del componente (aplicación/servidor) del <b>Gestor de Log de Incidencias</b>
	 * @return el componente para usar el Gestor de Logs de Incidencias
	 */
	public static Q70LogIncidenciasInterface getLogIncidencias(){
		return (Q70LogIncidenciasInterface)getGestorLogs().getLog(Q70ConstantesLogs.LOG_INCIDENCIAS);
	}

	/**
	 * Retorna la referencia del componente (aplicación/servidor) del <b>Gestor de Log de Navegacion</b>
	 * @return el componente para usar el Gestor de Logs de Navegacion
	 */
	public static Q70LogNavegacionInterface getLogNavegacion(){
		return (Q70LogNavegacionInterface)getGestorLogs().getLog(Q70ConstantesLogs.LOG_NAVEGACION);
	}

	/**
	 * Retorna la referencia del componente (aplicación/servidor) del <b>Gestor de Log de Transacciones</b>
	 * @return el componente para usar el Gestor de Logs de Transacciones
	 */
	public static Q70LogTransaccionesInterface getLogTransacciones(){
		return (Q70LogTransaccionesInterface)getGestorLogs().getLog(Q70ConstantesLogs.LOG_TRANSACCIONES);
	}

	/**
	 * Retorna la referencia del componente (aplicación/servidor) del <b>Gestor de Log de Conectores</b>
	 * @return el componente para usar el Gestor de Logs de Conectores
	 */
	public static Q70LogConectoresInterface getLogConectores(){
		return (Q70LogConectoresInterface)getGestorLogs().getLog(Q70ConstantesLogs.LOG_CONECTORES);
	}

	/**
	 * Retorna el componente aplicación a partir de la referencia de accesibilidad de componentes y el ID
	 * del componente
	 * @param claseKey referencia de accesibilidad de componentes - Q70FactoriaKey
	 * @param idComponente del componente
	 * @return el componente de aplicación
	 */
	private static Object getComponente(Class claseKey, String idComponente) {
		if (null==getMapaAplicaciones(claseKey))
			return null;
		return getMapaAplicaciones(claseKey).get(idComponente);
	}

	/**
	 * Retorna un Mapa con todos los componentes de aplicación accesibles desde el EAR.
	 * @param claseKey referencia de accesibilidad de componentes - Q70FactoriaKey
	 * @return Mapa de los componentes de aplicación 
	 */
	private static Map getMapaAplicaciones(Class claseKey) {
		return ((Map)mapaDeClasesDeAplicacion.get(claseKey));
	}
	
	/**
	 * Mapea el componente con su Id de componente y la referencia de accesibilidad de componentes
	 * @param componente a mapear
	 * @param claseKey referencia de accesibilidad de componentes por aplicacion 
	 * @param idComponente del componente
	 */
	private static void putComponente(Object componente, Class claseKey, String idComponente) {
		Map mapaAplicacion = (Map)mapaDeClasesDeAplicacion.get(claseKey);
		if (mapaAplicacion==null){
			mapaAplicacion = new HashMap(5);
			mapaDeClasesDeAplicacion.put(claseKey, mapaAplicacion);
		}
		
		mapaAplicacion.put(idComponente,componente);
	}

	/**
	 * Obtiene la referencia de accesibilidad de componentes de aplicacion - Q70FactoryKey 
	 * @return referencia accesibilidad
	 */
	private static Class getClassKey() {
		Class claseKey = null;
		try {
			claseKey = doClassForNameKey();
		} catch (ClassNotFoundException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(COMPONENTE,Q70Factoria.class.getName(),"getClassKey","Se ha producido un error en el método getClassKey.",e));
		}
		return claseKey;
	}

	/**
	 * Se obtiene una instancia la clase Q70FactoriaKey que hacen accesible los componentes de aplicación definidos 
	 * en el EAR. Para cada aplicación de un mismo EAR devolverá una referencia distinta 
	 * @return instancia de accesibilidad de los componentes de aplicación
	 * @throws ClassNotFoundException
	 */
	private static Class doClassForNameKey() throws ClassNotFoundException {
		Class claseKey;
		claseKey = Class.forName("es.ejie.frmk.Q70FactoriaKey", false, Thread.currentThread().getContextClassLoader());
		return claseKey;
	}

	/**
	 * Guarda la lista de aplicaciones de Geremua
	 * @param aplicacion
	 */
	public static void setAplicacionCode(String aplicacion) {
		Class claseKey = getClassKey();
		String idAplicacion = CTE_ID_APLICACION;
		putComponente(aplicacion, claseKey, idAplicacion);
		
		int contadorWars=0;
		try {
			contadorWars = Integer.parseInt((String)getComponente(claseKey,CTE_ID_CONTADOR));
		} catch (Throwable e) {
			Q70TrazaInterface traza = getTraza();
			if (traza.isTraceActive()){
				traza.trace(COMPONENTE, Q70TraceLevel.DEBUG, "Pasa por primera vez por el método setAplicacionCode de la clase Q70Factoria para la aplicación " + aplicacion);
			}
		}finally{
			putComponente(String.valueOf(++contadorWars),claseKey,CTE_ID_CONTADOR);
			listaAplicaciones.add(aplicacion);
		}
	}
	
	/**
	 * Devuelve una lista de aplicaciones desplegadas en una instancia de Geremau
	 * @return listaAplicaciones lista de Aplicaciones 
	 */
	public static Collection getListaDeAplicaciones(){
		return listaAplicaciones;
	}
	
	/**
	 * Obtiene el codigo de la aplicación
	 * @return Codigo de la aplicación
	 */
	public static String getAplicacionCode(){
		Class claseKey = getClassKey();	
		String idAplicacion = CTE_ID_APLICACION;
		
		return (String)getComponente(claseKey, idAplicacion);
	}
	
	/**
	 * Borrar todos los componentes de una aplicación
	 */
	public static void removeAplicacion(){
		Class claseKey = getClassKey();
		int contadorWars = Integer.parseInt((String)getComponente(claseKey,CTE_ID_CONTADOR));
		putComponente(String.valueOf(--contadorWars),claseKey,CTE_ID_CONTADOR);
		if (0==Integer.parseInt((String)getComponente(getClassKey(),CTE_ID_CONTADOR))){
			listaAplicaciones.remove(getAplicacionCode());
			getMapaAplicaciones(claseKey).clear();
			mapaDeClasesDeAplicacion.remove(claseKey);
		}
	}

	/**
	 * Devuelve si el hilo de ejecucion que invoca a este metodo, pertenece a un classloader de 
	 * una aplicacion (EAR, WAR) o pertenece a un classloader mas alto, por ejemplo de servidor. 
	 * La implementacion se realiza intentando instanciar la clase <code>es.ejie.frmk.Q70FactoriaKey</code> 
	 * situada en EAR.
	 * 
	 * @return True, si es un hilo de ejecucion de aplicacion. False, en caso contrario.
	 */
	public static boolean isFactoriaKey() {
		try {
			doClassForNameKey();
			return true;
		} catch (ClassNotFoundException e) {
			return false;
		}
	}
}
