package es.ejie.frmk.infraestructura.utilidades.concurrencia;

import es.ejie.frmk.Q70FactoriaConstantes;
import es.ejie.frmk.control.arranque.Q70GestorArranqueConfiguracionServidor;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.logs.base.Q70LogInterno;
import es.ejie.frmk.infraestructura.utilidades.constantes.Q70UtilidadesConstantes;

/**
 * Clase encargada de abstraer la implementación para controlar el bloqueo de
 * diferentes threads dentro de un componente para su posible reconfiguración.
 * <p>
 * Esta clase actúa como dos candados.  
 * <ul>
 * <li>Un candado <code>Q70LockThreads</code> para suspender todos los threads
 * que vayan a entrar a una zona crítica.</li>
 * <li>Otro candado <code>Q70LockReconfigureThread</code> para esperar a los
 * posibles threads que estuvieran ejecutando la zona crítica.</li>
 * </ul>
 * </p>
 * @version 2.0 Fecha: 23-mar-2006
 */
public class Q70Lock {

	// Contador de threads en la zona crítica.
	private long contador = 0;
	// Indica si hay que bloquear los threads que van a entrar a la zona crítica.
	boolean bloqueo = false;
	/** Candado para suspender/activar todos los threads que vayan a entrar a
	 * una zona crítica. En el caso de Geremua, cuando un componente se va a
	 * reconfigurar, es necesario que no haya ningún thread ejecutando el
	 * componente.*/
	private Q70LockThreads lockThreads = null;
	/** Candado para suspender/activar el thread que necesita que los threads
	 * estén suspendidos. En el caso de Geremua, el thread que va a reconfigurar
	 * el componente deberá esperar a que hayan acabado todos los threads que
	 * estén ejecutando el componente.*/ 
	private Q70LockReconfigureThread lockReconfigureThread = null;
	// Indica si el componente a reconfigurar es el de Logs o el de Trazas
	private boolean componenteLogsTraza = false;

	private static boolean lockActivo = false;

	/**
	 * Constructor privado para que se use el otro constructor obligatoriamente.
	 */
	private Q70Lock() {
		if ("SI".equals(Q70GestorArranqueConfiguracionServidor.getGlobalProperty(Q70GestorArranqueConfiguracionServidor.LOCK_ACTIVO)))
			lockActivo=true;
		else
			lockActivo=false;
	}
	
	public Q70Lock(String tipoComponente) {
		if (isLockActivo()){
			lockThreads = new Q70LockThreads(this);
			lockReconfigureThread = new Q70LockReconfigureThread(this);
			// Indica si el componente a reconfigurar es el de Logs o el de Trazas
			// dependiendo del tipo de componente. 
			this.componenteLogsTraza = (Q70FactoriaConstantes.ID_GESTOR_LOGS.equalsIgnoreCase(tipoComponente) || Q70FactoriaConstantes.ID_GESTOR_TRAZA.equalsIgnoreCase(tipoComponente));
		}
	}
	
	/**
	 * Método que activa el bloqueo para la zona crítica y me hace esperar
	 * hasta que todos hayan salido de la zona crítica.
	 */
	public void suspender() throws Q70FrameworkException{
		try {
			if (isLockActivo())
				lockReconfigureThread.suspender();
		} catch (Exception e) {
			Q70LogInterno.log("\nERROR!!! Se ha producido un error en el método suspender de la clase Q70Lock.\n");
			throw new Q70FrameworkException(Q70UtilidadesConstantes.UTILIDADES_LOCK,this.getClass().toString(),"suspender","Se ha producido un error en el método suspender.",e);										
		}
	}
	
	/**
	 * Método que activa la entrada en la zona crítica. 
	 */
	public void activar() {
		if (isLockActivo())
			lockThreads.activar();
	}
	
	/**
	 * Método que actualiza el contador de los threads en la zona crítica y
	 * suspende los threads entrantes en caso de que esté activado el bloqueo.
	 */
	public void entradaZonaCritica() {
		if (isLockActivo())
			lockThreads.entradaZonaCritica();
	}
	
	/**
	 * Método que actualiza el contador de los threads en la zona crítica. Si
	 * el bloqueo está activado y han salido todos los threads activará al
	 * thread que ejecutó el método <code>suspender()</code>.
	 */
	public void salidaZonaCritica() {
		if (isLockActivo())
			lockReconfigureThread.salidaZonaCritica();
	}

	
	/**
	 * Retorna el número de threads ejecutando la zona crítica.
	 * @return long número de threads.
	 */
	protected long getContador() {
		return contador;
	}
	
	/**
	 * Indica si se ha activado el bloqueo para la zona crítica.
	 * @return boolean
	 */
	public boolean isBloqueo() {
		if (isLockActivo())
			return bloqueo;
		else
			return true;
	}

	/**
	 * Método que suma uno al contador de threads en la zona crítica.
	 */
	synchronized protected void mas() {
		this.contador++;
	}
	
	/**
	 * Método que resta uno al contador de threads en la zona crítica.
	 */
	synchronized protected void menos() {
		this.contador--;
	}

	/**
	 * Método que activa/desactiva el bloqueo para la zona crítica. 
	 * @param bloqueo true indica activar bloqueo.
	 */
	protected void setBloqueo(boolean bloqueo) {
		this.bloqueo = bloqueo;
	}

	/**
	 * Método que indica si el componente a reconfigurar es el de Logs o el de
	 * Trazas
	 * @return Returns the componenteLogsTraza.
	 */
	protected boolean isComponenteLogsTraza() {
		return componenteLogsTraza;
	}

	/**
	 * Método que indica si el lock está activo o inactivo
	 * @return boolean
	 */
	public static boolean isLockActivo() {
		return lockActivo;
	}
}