package com.fitbank.migrationdb.common;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.fitbank.common.logger.FitbankLogger;

/**
 * Clase orquestadora de los hilos de ejecución
 * 
 * @author SoftWarehouse S.A.
 */
public class MonitorThreads {

    /**
     * Contador de hilos activos.
     */
    private Long count = java.lang.Long.valueOf(0);

    /**
     * Monitos de hilo.
     */
    private boolean available = true;

    /**
     * Monitor de finalización de ejecución de hilos.
     */
    private boolean end = true;
    /**
     * Maximo numero de hilos a levantar en la ejecución de la migración.
     */
    private Integer maxthreads = 0;

    /**
     * Contador de cuentas procesadas.
     */
    private Integer total = 0;

    /**
     * Lista que almacena el nombre del hilo a procesar.
     */
    private List<Integer> lmaxthreads;

    /**
     * Logger de mensajes.
     */
    private static final Logger LOG = FitbankLogger.getLogger();

    /**
     * Crea una instancia de MonitorThreads
     * 
     * @throws java.lang.Exception
     *             Se presenta si no se puede construir la instancia
     */
    public MonitorThreads() throws Exception {
        maxthreads = 20;
        lmaxthreads = new ArrayList<Integer>();
        for (int i = 1; i <= maxthreads; i++) {
            lmaxthreads.add(i);
        }
    }

    // ********************** Manejo de HILOS

    /**
     * Método que Permite registrar la finalizacion de la ejecucion de un hilo.
     * 
     * @param pThreadnumber
     *            Número del Hilo Ejecutado
     * @throws Exception
     *             Se presenta en el caso que no se pueda reducir el numero de
     *             hilos pendientes de finalización
     */
    public synchronized void removeCounter() throws Exception {
        if (!available) {
            wait();
        }
        try {
            available = false;
            count--;
        } finally {
            available = true;
            if (count == 0) {
                end = true;
            }
            this.notify();
        }
    }

    /**
     * Método que Permite registrar el inicio de la ejecucion de un hilo.
     * 
     * @return Número del Hilo
     * @throws Exception
     *             Se presenta en el caso que no se pueda levantar un hilo de
     *             ejecución
     */
    public synchronized Integer addCounter() throws Exception {
        total++;
        LOG.error("CUENTAS PROCESADAS: " + total);
        if (!available || count > this.maxthreads) {
            wait();
        }
        Integer numthread = 0;
        try {
            available = false;
            int icount = this.count.intValue();
            icount++;
            this.count = Long.valueOf(icount);
            if (!lmaxthreads.isEmpty()) {
                numthread = lmaxthreads.get(0);
                lmaxthreads.remove(0);
            }
        } finally {
            available = true;
            end = false;
            this.notify();
        }
        return numthread;
    }

    /**
     * Método que controla la finalización de la ejecución.
     * 
     * @throws Exception
     *             Se presenta en el caso que la finalización no sea
     *             satisfactoria
     */
    public synchronized void finish() throws Exception {
        while (!end) {
            wait();
        }
        try {
            LOG.info("Fin Proceso de Migracion ");
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
    }

    /**
     * Entrega el valor de count
     * 
     * @return count
     */
    public Long getCount() {
        return count;
    }

}
