package com.fitbank.uci.calendar;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import com.fitbank.uci.client.ToDo;
import com.fitbank.uci.client.UCILogger;
import com.fitbank.uci.common.UCIException;
import com.fitbank.uci.server.Controlador;
import com.fitbank.uci.server.mail.ErrorEvent;
import com.fitbank.uci.server.manager.DeviceEventTypes;
import com.fitbank.uci.server.manager.ParameterDetail;

/**
 * Clase que implementa el proceso de consumo de los mensajes recibidos en
 * UCI
 *
 * @author    Fit-Bank
 */
public class UCITask extends Controlador {

    /**
     * Campo utilizado para controlar la ejecución de la tarea.
     *
     * @since    empty
     */
    boolean service = false;

    /**
     * Campo utilizado para para indicar si el Controlador se encuentra bajo proceso de Shutdown
     *
     * @since    empty
     */
    boolean shutdown = false;

    /**
     * Campo utilizado para el acceso a los logs
     *
     * @since    empty
     */
    private UCILogger logger;

    /**
     * Campo utilizado para manejar la referencia a la clase que se debe ejecutar por la tarea
     *
     * @since    empty
     */
    @SuppressWarnings("unchecked")
    private Class todoClass;

    /**
     * Tiempo de espera para la ejecución de la Tarea
     *
     * @since    empty
     */
    private Integer iTimer;

    /**
     * Campo utilizado para indicar si las ejecuciones de las  tareas deben hacerlo en paralelo 
     *
     * @since    empty
     */
    private boolean paralell = false;

    /**
     * Descripción de la Tarea
     *
     * @since    empty
     */
    private String taskDescription;

    /**
     * Parametros para la tarea
     *
     * @since    empty
     */
    private String taskParameters;

    /**
     * Campo utilizado para almacenar la hora de la última ejecución.
     *
     * @since    empty
     */
    private String lastExecution = "";

    /**
     * Parametro Clase
     *
     * @since    empty
     */
    public final static String TODO_CLASS = "TODO_CLASS";

    /**
     * Parametro tiempo
     *
     * @since    empty
     */
    public final static String TIMER = "TIMER";

    /**
     * Parámetro descripción
     *
     * @since    empty
     */
    public final static String DESCRIPTION = "DESCRIPTION";

    /**
     * Parámetro paralelo.
     *
     * @since    empty
     */
    public final static String PARALELL = "PARALELL";

    /**
     * Parametro contenedor de los parámetros para la Tarea.
     *
     * @since    empty
     */
    public final static String PARAMETERS = "PARAMETERS";

    /**
     * Crea una nueva instancia de UCITask
     *
     * @since              empty
     * @throws  Exception  Error
     */
    public UCITask() throws Exception {
        logger = UCILogger.getInstance();
    }

    /**
     * Fija un nuevo valor del atributo LastExecution
     *
     * @param  lastExecution  Nuevo valor de LastExecution
     * @since                 empty
     */
    public void setLastExecution(String lastExecution) {
        this.lastExecution = lastExecution;
    }

    /**
     * Método que Permite fjar los parámetros de ejecución del Conector
     *
     * @param  pParameters  Valor de los parámetros del Conector
     * @since               empty
     * @throws  Exception   Error
     */
    @SuppressWarnings("unchecked")
    public void setParameters(String pParameters) throws Exception {
        this.addMessage("Iniciada la Tarea", DeviceEventTypes.INIT);
        service = true;

        Map<String, ParameterDetail> param = (Map<String, ParameterDetail>) this.parseParametes(pParameters);

        this.todoClass = Class.forName(param.get(TODO_CLASS).getValue());
        try {
            @SuppressWarnings("unused")
            ToDo td = (ToDo) this.todoClass.newInstance();
        } catch (ClassCastException e) {
            throw new UCIException("TASK000", "La clase " + this.todoClass.getName() + " no implementa ToDo");
        }
        this.iTimer = Integer.parseInt(param.get(TIMER).getValue());
        this.taskDescription = param.get(DESCRIPTION).getValue();
        this.taskParameters = this.name + "," + param.get(PARAMETERS).getValue();
        if (param.size() > 3) {
            this.paralell = Boolean.parseBoolean(param.get(PARALELL).getValue());
        }
        start();
    }

    /**
     * Obtiene el valor de LastExecution
     *
     * @return    El valor de LastExecution
     * @since     empty
     */
    public String getLastExecution() {
        return lastExecution;
    }

    //~ Métodos
    //**************************************************************************************

    /**
     * Método que indica si el controlador se encuentra o no conectado
     *
     * @return             Entrega el indicador
     * @since              empty
     * @throws  Exception  Error
     */
    public boolean isConnected() throws Exception {
        return service;
    }

    /**
     * Método que entrega la descripción del conector
     *
     * @return             Entrega la descripción del conector
     * @since              empty
     * @throws  Exception  Error
     */
    public String getDescription() throws Exception {
        return "Tarea Calendarizada " + this.taskDescription;
    }

    /**
     * Método que entrga un indicador de si el Proceso de consumo de Mensajes se
     * encuentra levantado
     *
     * @return             Entrega el indicador
     * @since              empty
     * @throws  Exception  Error
     */
    public boolean isStarted() throws Exception {
        return service;
    }

    /**
     * Método que entrega el estatus del Servidor
     *
     * @return             Entrega el valor del Estatus.
     * @since              empty
     * @throws  Exception  Error
     */
    public String getStatus() throws Exception {
        return (this.service) ? "UP" : "DOWN";
    }

    /**
     * Obtiene el valor de TodoClass
     *
     * @return    El valor de TodoClass
     * @since     empty
     */
    @SuppressWarnings("unchecked")
    public Class getTodoClass() {
        return todoClass;
    }

    /**
     * Obtiene el valor de TaskDescription
     *
     * @return    El valor de TaskDescription
     * @since     empty
     */
    public String getTaskDescription() {
        return taskDescription;
    }

    /**
     * Obtiene el valor de ExtraData
     *
     * @return    El valor de ExtraData
     * @since     empty
     */
    public String getExtraData() {

        return "ULTIMA EJECUCION:" + this.lastExecution;
    }

    /**
     * Obtiene el valor de TaskParameters
     *
     * @return    El valor de TaskParameters
     * @since     empty
     */
    public String getTaskParameters() {
        return taskParameters;
    }

    /**
     * Método que Permite desconectar el conector de la cola de entrada
     *
     * @since              empty
     * @throws  Exception  Error
     */
    public void disconnect() throws Exception {
        shutdown = true;
        service = false;

        this.addMessage("Tarea detenida ", DeviceEventTypes.FINISH);
    }

    /**
     * Método formal
     *
     * @return             Entrega el valor de
     * @since              empty
     * @throws  Exception  Error
     */
    public Serializable receiveMessage() throws Exception {
        throw new UCIException("UCI-0001", "El controlador UCITask no permite la recepción de Mensajes");
    }

    /**
     * Método que implementa el consumo de mensajes
     *
     * @since    empty
     */
    public void run() {
        while (service) {
            try {
                this.addMessage("Se inicia la Ejecucion de  " + this.taskDescription, DeviceEventTypes.CONNECT);
                this.setLastInputMessage("");
                if (!this.paralell) {
                    ToDo td = (ToDo) this.todoClass.newInstance();

                    try {
                        String result = td.execute(this.taskParameters);

                        if (result != null) {
                            this.lastExecution = result;
                            this.addMessage("Finaliza la Ejecucion de  " + this.taskDescription + " " + result,
                                            DeviceEventTypes.DISCONNECT);
                        }
                    } catch (Exception e) {
                        UCILogger.getInstance().throwing(e);
                        this.addMessage("La tarea  " + this.taskDescription + ":" + e.getMessage(),
                                        DeviceEventTypes.ERROR);
                    }
                } else {
                    new Runner(this);
                }

                //Se ejecuta una vez al iniciar el Controlador.
                //Solo permite repetir ejecuciones para milisegundos
                //mayores a 0ms
                if (this.iTimer > 0) {
                    sleep(this.iTimer * 1000);
                } else {
                    shutdown();
                    break;
                }
            } catch (Exception e) {
                service = false;
                try {
                    if (!this.shutdown) {
                        UCILogger.getInstance().throwing(e);
                        this.addMessage("" + e.getMessage(), DeviceEventTypes.ERROR);
                        this.addMessage("Desconectado de la Cola de Entrada", DeviceEventTypes.FINISH);
                        ErrorEvent.getInstance().process(this.name + " Desconectado: " + this.getServer() + " " +
                                        e.getMessage());
                    }
                } catch (Exception e1) {
                    logger.throwing(e1);
                }

                break;
            }
        }
    }

    /**
     * Método formal
     *
     * @param  pMessage     Valor de
     * @param  pProperties  Valor de
     * @since               empty
     * @throws  Exception   Error
     */
    public void sendMessage(Serializable pMessage, Properties pProperties) throws Exception {
        throw new UCIException("UCI-0002", "El controlador UCITask no permite el envío de Mensajes");
    }

    /**
     * Método que Permite detener el controlador de consumo de mensajes
     *
     * @since              empty
     * @throws  Exception  Error
     */
    public void shutdown() throws Exception {
        disconnect();
    }

    /**
     * Método que Permite iniciar el consumo de mensajes
     *
     * @since              empty
     * @throws  Exception  Error
     */
    public void startup() throws Exception {
        service = true;
    }

    /**
     * Metodo que permite
     *
     * @param  pParameters    Valor de
     * @return                Entrega
     * @exception  Exception  Error
     * @since                 empty
     */
    @SuppressWarnings("unchecked")
    public String formatParameters(Map pParameters) throws Exception {
        String data = "";

        data += (String) pParameters.get(TODO_CLASS);

        if (data == null) {
            throw new UCIException("PARAM-0014", "TODO_CLASS: CLASE A EJECUTAR REQUERIDA");
        }

        Integer timer = null;

        try {
            timer = Integer.parseInt((String) pParameters.get(TIMER));
        } catch (Exception e) {
            throw new UCIException("PARAM-0014", "TIMER: Se esperaba un Numero");
        }
        if (timer == null) {
            throw new UCIException("PARAM-0014", "TIMER: TIEMPO DE ESPERA REQUERIDO");
        }
        data += "#" + timer;

        String des = (String) pParameters.get(DESCRIPTION);

        if (des == null) {
            throw new UCIException("PARAM-0014", "DESCRIPCION: DESCRIPCION DE LA TAREA REQUERIDA");
        }
        data += "#" + des;

        String param = (String) pParameters.get(PARAMETERS);

        if (param == null) {
            throw new UCIException("PARAM-0014", "PARAMETROS: PARAMETROS DE LA TAREA REQUERIDO");
        }
        data += "#" + param;
        des = (String) pParameters.get(PARALELL);
        if (des != null) {
            if (des.compareTo("true") != 0) {
                des = "false";
            }
            data += "#" + des;
        }
        return data;
    }

    /**
     * Metodo que permite
     *
     * @param  pParameters    Valor de
     * @return                Entrega
     * @exception  Exception  Error
     * @since                 empty
     */
    @SuppressWarnings("unchecked")
    public Map parseParametes(String pParameters) throws Exception {
        Map m = this.initParameters();

        if (pParameters == null || pParameters.compareTo("") == 0) {
            return m;
        }

        StringTokenizer st = new StringTokenizer(pParameters, "#");
        List<String> l = new ArrayList<String>();

        while (st.hasMoreElements()) {
            l.add((String) st.nextElement());
        }
        if (l.size() != 3 && l.size() != 4) {
            throw new UCIException("PARAM-0014", "ERROR DE CONFIGURACION");
        }

        ParameterDetail d = (ParameterDetail) m.get(TODO_CLASS);

        try {
            d.setValue((String) l.get(0));
        } catch (Exception e) {
            throw new UCIException("PARAM-0014", "CLASE A EJECUTAR: REQUERIDA");
        }
        d = (ParameterDetail) m.get(TIMER);
        try {
            d.setValue("" + Integer.parseInt(l.get(1)));
        } catch (Exception e) {
            throw new UCIException("PARAM-0014", "TIMER: SE ESPERABA UN NUMERO");
        }
        d = (ParameterDetail) m.get(DESCRIPTION);
        try {
            d.setValue((String) l.get(2));
        } catch (Exception e) {
            throw new UCIException("PARAM-0014", "DESCRIPCION: REQUERIDA");
        }
        d = (ParameterDetail) m.get(PARAMETERS);
        try {
            d.setValue((String) l.get(3));
        } catch (Exception e) {
            throw new UCIException("PARAM-0014", "PARAMETROS: REQUERIDA");
        }
        d = (ParameterDetail) m.get(PARALELL);
        try {
            if (l.size() > 4) {
                d.setValue(Boolean.valueOf((String) l.get(4)).toString());
            }
        } catch (Exception e) {
            throw new UCIException("PARAM-0014", "PARALELO: CAMPO BOOLEANO");
        }
        return m;
    }

    /**
     * Metodo que permite
     *
     * @return                Entrega
     * @exception  Exception  Error
     * @since                 empty
     */
    private Map<String, Object> initParameters() throws Exception {
        Map<String, Object> m = new HashMap<String, Object>();
        ParameterDetail d = new ParameterDetail(TODO_CLASS, "Clase a ejecutar", Class.class, true);

        m.put(d.getName(), d);
        d = new ParameterDetail(TIMER, "Tiempo de espera entre ejecuciones", Integer.class, false);
        m.put(d.getName(), d);
        d = new ParameterDetail(DESCRIPTION, "Descripcion de la Tarea", String.class, false);
        m.put(d.getName(), d);
        d = new ParameterDetail(PARAMETERS, "Parametros de ejecucion de la tarea", String.class, false);
        m.put(d.getName(), d);
        d = new ParameterDetail(PARALELL, "Indicador de si las ejecuciones de la Tarea se pueden ejecutar en paralelo",
                        Boolean.class, false);
        m.put(d.getName(), d);
        return m;
    }
}
