package com.fitbank.bpmserver;

import com.fitbank.common.logger.FitbankLogger;
import org.apache.commons.configuration.Configuration;
import org.apache.log4j.Logger;

/**
 * Interfaz que deben implementar los servicios de incializacion del server
 * <br/><br/>
 * Un servicio puede tener los siguientes comportamientos, segun la propiedad
 * especificada en <b>fitserver.properties</b>:<br/><br/>
 * 
 * nombre.de.la.clase.<b>executeOnStartup</b><br/>
 * Indica si el servicio se ejecuta al iniciar el servidor <b>(defautl: true)</b>
 * <br/><br/>
 * nombre.de.la.clase.<b>executeOnShutdown</b><br/>
 * Indica si el servicio se ejecuta al parar el servidor <b>(default: false)</b>
 * <br/><br/>
 * nombre.de.la.clase.<b>executeOnRuntime</b><br/>
 * Indica si el servicio se ejecuta mientras el servidor este en ejecucion <b>(default: false)</b>
 * <br/><br/>
 * nombre.de.la.clase.<b>executeByPeriods</b><br/>
 * Indica si el servicio se ejecuta en periodos Requiere la propiedad period <b>(default: false)</b>
 * <br/><br/>
 * nombre.de.la.clase.<b>period</b><br/>
 * Indica los periodos de ejecucion del servicio (minutos) <b>(default: 0)</b>.
 *
 * @author Soft WareHouse S.A.
 */
public abstract class ServiceBpm extends Thread {

    /**
     * Log por default.
     */
    public static final Logger LOGGER = FitbankLogger.getLogger();

    /**
     * Archivo de configuraciones para el servidor.
     */
    private static final Configuration CONFIG = FitBpmServerParam.getConfig();

    /**
     * Indica si esta corriendo o no el servicio.
     */
    private boolean running = false;

    /**
     * Logica de ejecucion del servicio.
     */
    public abstract void execute() throws Exception;

    /**
     * Procesos extras al interrumpir el servicio.
     */
    public abstract void shutdown();

    /**
     * Metodo de ejecucion hilo al iniciar el servidor.
     */
    @Override
    public void run() {
        if (this.isExecuteOnStartup()) {
            this.setRunning(true);
            this.runThread();
        }

        this.setRunning(false);
    }

    /**
     * Metodo de ejecucion del servicio.
     */
    private void runThread() {
        LOGGER.info("Ejecutando del servicio " + this.getName() + "...");
        while (this.isRunning()) {
            try {
                this.execute();

                if (this.isExecuteByPeriods() && !this.isInterrupted()) {
                    for (int i = 0; i < this.getPeriod(); i++) {
                        if (!this.isRunning()) {
                            break;
                        }
                        Thread.sleep(60 * 1000);
                    }
                } else if (!this.isExecuteOnRuntime()){
                    LOGGER.info("Servicio " + this.getName() + " ejecutado una sola vez");

                    if (this.isExecuteOnStartup()) {
                        this.interrupt();
                    }

                    break;
                }
            } catch (InterruptedException e) {
                this.setRunning(false);
                LOGGER.info("Hilo " + this.getName() + " interrumpido", e);
            } catch (Exception e) {
                this.setRunning(false);
                LOGGER.error("Error desconocido en " + this.getName(), e);
            }
        }
        LOGGER.info("Ejecucion del servicio " + this.getName() + " terminada");
    }

    /**
     * Metodo de ejecucion hilo al finalizar el servidor.
     */
    @Override
    public void interrupt() {
        LOGGER.info("Finalizando servicio " + this.getName() + "...");
        if (this.isExecuteOnShutdown()) {
            this.setRunning(true);
            this.runThread();
        }

        this.setRunning(false);
        this.shutdown();
        super.interrupt();
        LOGGER.info("Servicio " + this.getName() + " finalizado completamente");
    }

    /**
     * Indica si el servicio esta ejecutandose o no.
     */
    public boolean isRunning() {
        return running;
    }

    /**
     * Indicar la ejecucion o finalizacion del servicio.
     */
    public void setRunning(boolean running) {
        this.running = running;
    }

    /**
     * Indica si el servicio se ejecuta al iniciar el servidor.
     */
    public boolean isExecuteOnStartup() {
        return CONFIG.getBoolean(this.getClass().getName() + ".executeOnStartup", true);
    }

    /**
     * Indica si el servicio se ejecuta al parar el servidor.
     */
    public boolean isExecuteOnShutdown() {
        return CONFIG.getBoolean(this.getClass().getName() + ".executeOnShutdown", false);
    }

    /**
     * Indica si el servicio se ejecuta durante el tiempo de vida del servidor.
     */
    public boolean isExecuteOnRuntime() {
        return CONFIG.getBoolean(this.getClass().getName() + ".executeOnRuntime", false);
    }

    /**
     * Indica si el servicio se ejecuta por periodos o una sola vez.
     */
    public boolean isExecuteByPeriods() {
        return CONFIG.getBoolean(this.getClass().getName() + ".executeByPeriods", false);
    }

    /**
     * Indica el periodo de repeticion de ejecuciones.
     */
    public Integer getPeriod() {
        return CONFIG.getInt(this.getClass().getName() + ".period", 0);
    }

}
