package com.fitbank.uci.calendar;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.fitbank.common.ApplicationDates;
import com.fitbank.common.Helper;
import com.fitbank.common.conectivity.HbSession;
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 UCITimeTask extends Controlador {

    public static void main(String[] args) {
        try {
            GregorianCalendar gc = new GregorianCalendar();

            gc.setTimeInMillis(System.currentTimeMillis());
            gc.set(Calendar.SECOND, 0);
            gc.set(Calendar.MILLISECOND, 0);

            String hour = "19:00:00";
            SimpleDateFormat sdf = new SimpleDateFormat("kk:mm:ss");

            UCILogger.getInstance().info(sdf.parse(hour).toString());

            GregorianCalendar gc2 = new GregorianCalendar();

            gc2.setTimeInMillis(sdf.parse(hour).getTime());

            GregorianCalendar gc3 = new GregorianCalendar();

            gc3.setTimeInMillis(System.currentTimeMillis());
            gc3.set(Calendar.HOUR_OF_DAY, gc2.get(Calendar.HOUR_OF_DAY));
            gc3.set(Calendar.HOUR, gc2.get(Calendar.HOUR));
            gc3.set(Calendar.MINUTE, gc2.get(Calendar.MINUTE));
            gc3.set(Calendar.SECOND, gc2.get(Calendar.SECOND));
            gc3.set(Calendar.MILLISECOND, 0);
            gc3.set(Calendar.AM_PM, gc2.get(Calendar.AM_PM));

            UCILogger.getInstance().info("" + gc);
            //System.out.println(gc2);
            UCILogger.getInstance().info("" + gc3);
            UCILogger.getInstance().info("" + (gc3.getTimeInMillis() - gc.getTimeInMillis()));
        } catch (Exception e) {
            UCILogger.getInstance().throwing(e);
        }

    }

    /**
     * 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 shutdownField = 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;

    /**
     * Hora a la que se va a ejecutar la Tarea
     *
     * @since empty
     */
    private GregorianCalendar iTimer;

    /**
     * Fecha Actual
     *
     * @since empty
     */
    private GregorianCalendar actualDate;

    /**
     * Fecha a la que se ejecutara la tarea
     *
     * @since empty
     */
    private GregorianCalendar dateToExecute;

    /**
     * Segundos transcurridos desde el minuto en el cual se calculo el tiempo para ejecutar la proxima tarea
     *
     * @since empty
     */
    private int secondsResized;

    /**
     * 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;

    /**
     * nDate 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 UCITimeTask() throws Exception {
        this.logger = UCILogger.getInstance();
    }

    private void closeFitSession() throws Exception {
        try {
            Helper.closeSession();
        } catch (Throwable e) {
            throw new Exception(e);
        }
    }

    /**
     * Mï¿½todo que Permite desconectar el conector de la cola de entrada
     *
     * @since empty
     * @throws Exception Error
     */
    @Override
    public void disconnect() throws Exception {
        this.shutdownField = true;
        this.service = false;

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

    //~ M�todos
    //**************************************************************************************

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

        if (pParameters.get(TODO_CLASS) == null) {
            throw new UCIException(ERROR_PARSE_CODE, "TODO_CLASS: CLASE A EJECUTAR REQUERIDA");
        }

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

        Integer timer = null;

        try {
            timer = Integer.parseInt((String) pParameters.get(TIMER));
        } catch (Exception e) {
            UCIException uci = new UCIException(ERROR_PARSE_CODE, "TIMER: Se esperaba un Numero", e);

            throw uci;
        }
        if (timer == null) {
            throw new UCIException(ERROR_PARSE_CODE, "TIMER: TIEMPO DE ESPERA REQUERIDO");
        }
        data += "#" + timer;

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

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

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

        if (param == null) {
            throw new UCIException(ERROR_PARSE_CODE, "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;
    }

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

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

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

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

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

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

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

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

    /**
     * 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", GregorianCalendar.class, true);
        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;
    }

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

    /**
     * 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
     */
    @Override
    public boolean isStarted() throws Exception {
        return this.service;
    }

    private boolean isTheSameHour() throws Exception {
        if (this.dateToExecute.getTimeInMillis() - this.actualDate.getTimeInMillis() == 0) {
            return true;
        }
        return false;
    }

    private void openFitSession() throws Exception {
        try {
            Helper.setSession(HbSession.getInstance().getSession());
        } catch (Throwable e) {
            throw new Exception(e);
        }
    }

    private void paralellProcess() throws Exception {
        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);
        }
    }

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

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

        List<String> l = this.parseString(pParameters);

        if ((l.size() != 3) && (l.size() != 4)) {
            throw new UCIException(ERROR_PARSE_CODE, "ERROR DE CONFIGURACION");
        }

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

        this.validateStringParameter(d, "CLASE A EJECUTAR: REQUERIDA", l.get(0));

        d = (ParameterDetail) m.get(TIMER);
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("kk:mm:ss");

            sdf.parse(l.get(1));
            d.setValue(l.get(1));
        } catch (Exception e) {
            throw new UCIException(ERROR_PARSE_CODE, "TIMER: SE ESPERABA UNA HORA HH:MM:SS", e);
        }
        d = (ParameterDetail) m.get(DESCRIPTION);
        this.validateStringParameter(d, "DESCRIPCION: REQUERIDA", l.get(2));
        d = (ParameterDetail) m.get(PARAMETERS);
        this.validateStringParameter(d, "PARAMETROS: REQUERIDA", l.get(3));
        d = (ParameterDetail) m.get(PARALELL);
        try {
            if (l.size() > 4) {
                d.setValue(Boolean.valueOf(l.get(4)).toString());
            }
        } catch (Exception e) {
            throw new UCIException(ERROR_PARSE_CODE, "PARALELO: CAMPO BOOLEANO", e);
        }
        return m;
    }

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

    private long reckonTimeToSleep() throws Exception {
        long l = this.dateToExecute.getTimeInMillis() - this.actualDate.getTimeInMillis();

        l = l - this.secondsResized;
        if (l < 0) {
            l = l + 86400000;
        }
        UCILogger.getInstance().info("TIME TO WAIT  ==============> " + (l / 1000) / 60 + " MINUTES");
        return l;
    }

    /**
     * Mï¿½todo que implementa el consumo de mensajes
     *
     * @since empty
     */
    @Override
    public void run() {
        while (this.service) {
            try {

                UCILogger.getInstance().info("JAVA PATH   " + System.getProperties().getProperty("java.library.path"));
                this.setDatesToCompare();
                if (this.isTheSameHour()) {
                    this.addMessage("Se inicia la Ejecucion de  " + this.taskDescription, DeviceEventTypes.CONNECT);
                    this.setLastInputMessage("");
                    if (!this.paralell) {
                        this.paralellProcess();
                    } else {
                        new TimeRunner(this);
                    }
                    sleep(60000);
                } else {

                    sleep(this.reckonTimeToSleep());
                }
            } catch (Exception e) {
                this.service = false;
                try {
                    if (!this.shutdownField) {
                        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) {
                    this.logger.throwing(e1);
                }

                break;
            }
        }
    }

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

    private void setDatesToCompare() throws Exception {
        this.actualDate = new GregorianCalendar();
        this.openFitSession();
        try {
            this.actualDate.setTimeInMillis(ApplicationDates.getInstance().getDataBaseTimestamp().getTime());
        } finally {
            this.closeFitSession();
        }
        this.secondsResized = this.actualDate.get(Calendar.SECOND);
        this.actualDate.set(Calendar.SECOND, 0);
        this.actualDate.set(Calendar.MILLISECOND, 0);
        this.dateToExecute = (GregorianCalendar) this.actualDate.clone();
        this.dateToExecute.set(Calendar.HOUR_OF_DAY, this.iTimer.get(Calendar.HOUR_OF_DAY));
        this.dateToExecute.set(Calendar.HOUR, this.iTimer.get(Calendar.HOUR));
        this.dateToExecute.set(Calendar.MINUTE, this.iTimer.get(Calendar.MINUTE));
        this.dateToExecute.set(Calendar.SECOND, 0);
        this.dateToExecute.set(Calendar.MILLISECOND, 0);
        this.dateToExecute.set(Calendar.AM_PM, this.iTimer.get(Calendar.AM_PM));
        UCILogger.getInstance().info("ACTUAL DATE  ==============> " + this.actualDate.getTime().toString());
        //UCILogger.getInstance().info("EXECUTE DATE ==============> "+this.dateToExecute.getTime().toString());
        //this.dateToExecute.set(Calendar.SECOND, this.iTimer.get(Calendar.SECOND));
    }

    /**
     * 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
     */
    @Override
    @SuppressWarnings(UNCHECKED)
    public void setParameters(String pParameters) throws Exception {
        this.addMessage("Iniciada la Tarea", DeviceEventTypes.INIT);
        this.service = true;

        Map<String, ParameterDetail> param = 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", e);
        }

        SimpleDateFormat sdf = new SimpleDateFormat("kk:mm:ss");

        this.iTimer = new GregorianCalendar();
        this.iTimer.setTimeInMillis(sdf.parse(param.get(TIMER).getValue()).getTime());
        this.taskDescription = param.get(DESCRIPTION).getValue();
        this.taskParameters = param.get(PARAMETERS).getValue();
        if (param.size() > 3) {
            this.paralell = Boolean.parseBoolean(param.get(PARALELL).getValue());
        }
        this.start();
    }

    /**
     * Mï¿½todo que Permite detener el controlador de consumo de mensajes
     *
     * @since empty
     * @throws Exception Error
     */
    @Override
    public void shutdown() throws Exception {
        this.disconnect();
    }

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

}
