package com.fitbank.uci.server.fit;

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

import com.fitbank.client.FitClient;
import com.fitbank.common.exception.ExceptionHandler;
import com.fitbank.dto.GeneralResponse;
import com.fitbank.dto.management.Detail;
import com.fitbank.uci.client.ChannelLogger;
import com.fitbank.uci.client.UCILogger;
import com.fitbank.uci.common.UCIException;
import com.fitbank.uci.core.fit.FitbankSender;
import com.fitbank.uci.server.ConnectionProcesor;
import com.fitbank.uci.server.Controlador;
import com.fitbank.uci.server.manager.DataAccess;
import com.fitbank.uci.server.manager.DeviceEventTypes;
import com.fitbank.uci.server.manager.ParameterDetail;

public class UCIFitbankSocket extends Controlador {

    private static final String IP = "IP";
    private static final String PORT = "port";
    private static final String TIMEOUT = "timeout";
    private static final String REGISTER = "register";
    private String ip;
    private String register;
    private int port;
    private int timeout;
    private Map<String, ParameterDetail> fitParameters;
    private boolean connected = false;
    private boolean started = false;

    @Override
    public void disconnect() throws Exception {
        this.addMessage("Se cierra la conexion con FIT " + this.fitParameters.get(UCIFitbankSocket.IP).getValue() + ":" +
                        this.fitParameters.get(UCIFitbankSocket.PORT).getValue(),
                        DeviceEventTypes.DISCONNECT);
        this.connected = false;
        this.started = false;
    }

    @Override
    public String formatParameters(Map<String, String> pParameters) throws Exception {
        String data = "";
        String aux = pParameters.get(UCIFitbankSocket.IP);

        if ((aux == null) || (aux.compareTo("") == 0)) {
            throw new UCIException(Controlador.ERROR_FORMAT_CODE, "IP: VALOR REQUERIDO");
        }
        data += "#" + aux;
        aux = pParameters.get(UCIFitbankSocket.PORT);
        if (aux == null) {
            throw new UCIException(Controlador.ERROR_FORMAT_CODE, "PORT: VALOR REQUERIDO");
        }
        data += "#" + aux;
        aux = pParameters.get(UCIFitbankSocket.TIMEOUT);
        if (aux == null) {
            throw new UCIException(Controlador.ERROR_FORMAT_CODE, "TIMEOUT: VALOR REQUERIDO");
        }
        data += "#" + aux;
        aux = pParameters.get(UCIFitbankSocket.REGISTER);
        if (aux != null) {
            data += "#" + aux;
        }

        return data;
    }

    @Override
    public String getDescription() throws Exception {
        return "Controlador de Acceso a FITBANK via Socket";
    }

    @Override
    public String getExtraData() {
        Map<String, Integer> m = FitbankSender.getTCount();
        Integer i = null;

        synchronized (m) {
            i = m.get(this.name);
        }
        if (i == null) {
            i = 0;
        }
        return "Mensajes en vuelo " + i;
    }

    @Override
    public String getStatus() throws Exception {
        return (this.started) ? "UP" : "DOWN";
    }

    private Map<String, ParameterDetail> initParameters() throws Exception {
        Map<String, ParameterDetail> m = new HashMap<String, ParameterDetail>();
        ParameterDetail d = new ParameterDetail(UCIFitbankSocket.IP, "IP", String.class, true);

        m.put(d.getName(), d);
        d = new ParameterDetail(UCIFitbankSocket.PORT, "Puerto", String.class, true);
        m.put(d.getName(), d);
        d = new ParameterDetail(UCIFitbankSocket.TIMEOUT, "Timeout", String.class, true);
        m.put(d.getName(), d);
        d = new ParameterDetail(UCIFitbankSocket.REGISTER, "Registrar", String.class, false);
        m.put(d.getName(), d);
        return m;
    }

    @Override
    public boolean isConnected() throws Exception {
        return this.connected;
    }

    @Override
    public boolean isStarted() throws Exception {
        return this.started;
    }

    @Override
    public Map<String, ParameterDetail> parseParametes(String pParameters) throws Exception {
        Map<String, ParameterDetail> 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(Controlador.ERROR_PARSE_CODE, "ERROR DE CONFIGURACION " + l.size());
        }

        ParameterDetail d = m.get(UCIFitbankSocket.IP);

        d.setValue(l.get(0));
        d = m.get(UCIFitbankSocket.PORT);
        d.setValue(l.get(1));
        d = m.get(UCIFitbankSocket.TIMEOUT);
        d.setValue(l.get(2));
        if (l.size() == 4) {
            d = m.get(UCIFitbankSocket.REGISTER);
            d.setValue(l.get(3));
        }
        return m;
    }

    @Override
    public Serializable receiveMessage() throws Exception {
        throw new UCIException("8171", "El controlador no permite la recepci�n de Mensajes");
    }

    @Override
    public void sendMessage(Serializable pMessage, Properties pProperties) throws Exception {
        Detail response = (Detail) pMessage;
        Detail msg = (Detail) pMessage;

        try {
            this.setLastOutputMessage(pMessage.toString());

            //Si el mensaje posee su propio tiempo máximo de espera, usarlo.
            Integer finalTimeout = this.timeout;
            if (msg.getTimeout() != null && msg.getTimeout() > 0) {
                finalTimeout = msg.getTimeout();
            }

            FitClient fitclient = new FitClient(this.ip, this.port, finalTimeout, register);

            response = fitclient.sendSerializable((Detail) pMessage);
        } catch (Throwable e) {
            Detail detail = (Detail) pMessage;
            ExceptionHandler eh = new ExceptionHandler(e,
                            ((detail == null) || (detail.getLanguage() == null)) ? "es" : detail.getLanguage());
            response.setResponse(eh.manage());
        }
        this.setLastInputMessage(response.toString());
        this.registerOK(msg, response);

        ConnectionProcesor cp = new ConnectionProcesor(response, this);
        cp.run();
    }

    protected void registerOK(Detail pMessage, Detail pResponse) throws Exception {
        GeneralResponse resp = pResponse.getResponse();

        if ((resp == null) || (pMessage.getType().compareTo("MAN") != 0)) {
            return;
        }
        if ((resp.getCode().compareTo(GeneralResponse.OK) == 0) || resp.getCode().startsWith("OK")) {
            ChannelLogger.getInstance("OK").addMessage(pMessage.toErrorXml(), false);
        }
    }

    @Override
    public void setParameters(String pParameters) throws Exception {
        this.fitParameters = this.parseParametes(pParameters);
        this.ip = this.fitParameters.get(UCIFitbankSocket.IP).getValue();
        this.port = Integer.parseInt(this.fitParameters.get(UCIFitbankSocket.PORT).getValue());
        this.timeout = Integer.parseInt(this.fitParameters.get(UCIFitbankSocket.TIMEOUT).getValue());
        this.register = this.fitParameters.get(UCIFitbankSocket.REGISTER).getValue();
    }

    @Override
    public void shutdown() throws Exception {
        this.disconnect();
    }

    @Override
    public void startup() throws Exception {
        this.connected = true;
        this.started = true;
        this.addMessage("Se establece la conexion con el Servidor " +
                        this.fitParameters.get(UCIFitbankSocket.IP).getValue() + ":" +
                        this.fitParameters.get(UCIFitbankSocket.PORT).getValue(),
                        DeviceEventTypes.CONNECT);
    }
}
