// 
// Copyright 2016 Agencia Estatal de Administración Tibutaria
//
// Licencia con arreglo a la EUPL, Versión 1.1 o -en cuanto
// sean aprobadas por la Comisión Europea- versiones
// posteriores de la EUPL (la «Licencia»);
// Solo podrá usarse esta obra si se respeta la Licencia.
// Puede obtenerse una copia de la Licencia en:
//  
// https://joinup.ec.europa.eu/software/page/eupl
//  
// Salvo cuando lo exija la legislación aplicable o se acuerde
// por escrito, el programa distribuido con arreglo a la
// Licencia se distribuye «TAL CUAL», SIN GARANTÍAS NI CONDICIONES
// DE NINGÚN TIPO, ni expresasni implícitas.
//
// Véase la Licencia en el idioma concreto que rige los permisos y 
// limitaciones que establece la Licencia.
// 

using System;
using System.Collections.Generic;
using System.Text;
using ateneo.sockets;
using ateneo.utiles;

namespace operador
{
    public enum MSG_RECIBIDO
    {
        OPERADOR_NO_IDENTIFICADO = 99,
        MESA_YA_CONECTADA = 98,
        CIERRE_URGENTE = 80,
        REINICIAR = 81,
        CONEXION_ACEPTADA = 1,
        CLIENTE_ENVIADO = 2,
        NUEVO_CONTRIBUYENTEVINCULADO = 3,
        PENDIENTES = 4,
        MENSAJE_DEL_ADMINISTRADOR = 20,
        MENSAJE_DEL_GESTOR = 21,
        MENSAJE_RECIBIDO = 22,
        EJECUTA_SCRIPT = 23,
        MENSAJE_RECIBIDO_MASCITAS = 24,
        ESTADO_CITA = 25,
        ESTADO_COLAS = 26,
        CATEGORIA_ESTADISTICA = 27,
        ESTADO_BOTONCOLACERO = 28,
        HUECO_CONCEDIDO = 29,
        HUECO_DENEGADO = 30,
        LISTA_OPERADORES = 31,
        LISTA_ADMINISTRADORES = 123,
        COMIENZO_ENVIO_COLAS = 114,
        REGISTRO_COLAS = 404
    }

    public enum COD_ENVIADO
    {
        ESTOY_VIVO = 0,
        CONEXION = 1,
        LIBRE = 2,
        INICIO = 3,
        TRABAJO_A_LIBRE = 4,
        LIBRE_A_INICIO = 5,
        DESCANSO_A_LIBRE = 7,
        LIBRE_A_DESCANSO = 13,
        TRABAJO_A_DESCANSO = 14,
        TRABAJO_A_TRABAJO = 15,
        MENSAJE_AL_ADMINISTRADOR = 20,
        REENVIO_COLA = 21,
        ENVIO_REG_PRODUCTOS = 22,
        ENVIO_ESTADO_NAVEGACION = 23,
        MENSAJE_RECIBIDO = 24,
        MENSAJE_AL_GESTOR = 25,
        REENVIO_SEGUNDA_COLA = 26,
        CONFIRMAR_DESCANSO = 27,
        ENVIAR_RESUMEN_COLAS = 28,
        ENVIAR_ESTADO_CITA = 29,
        SOLICITAR_ESTADO_COLAS = 30,
        ENVIO_FAVORITAS_ESTADISTICAS = 31,
        ENVIO_MODIFICACION_CAT_REGISTRO = 32,
        SOLICITO_AUTOENVIO_COLACERO = 33,
        SOLICITO_HUECO = 34,
        REPONER_HUECO = 35,
        SOLICITO_LISTA_OPERADORES = 36,
        ENVIO_NOTA =37,
        SOLICITAR_LISTA_ADMINISTRADORES = 124,
    }

    public class Comunicaciones
    {
        private MessageSocket socket;
        private SocketUDP udp;
        Principal principal;
        public string operador;
        public string mesa;
        delegate void MensajeDelServidor(int codigo, String msj);
        MensajeDelServidor mensajeDelegate;
        MensajeDelServidor errorDelegate;
        private DateTime horaActual;

        public string host {
            get { return socket.host;  }
        }
        public int puerto {
            get { return socket.puerto; }
        }

        public Comunicaciones(Principal principal) {
            this.principal = principal;
            horaActual = DateTime.Now;
            mensajeDelegate = new MensajeDelServidor(mensajeDelServidor);
            errorDelegate = new MensajeDelServidor(errorEnSocket);
            //if (Configuracion.instanciaUnica().usandoUDP) {
            //    udp = new SocketUDP(Configuracion.instanciaUnica().nombreServidor, Configuracion.instanciaUnica().puerto);
            //}
        }

        public bool conectar() {
            socket = new MessageSocket();
            abrir(true);
            if (!socket.abierto) {
                principal.cerrarMiniGestor();
                FormReconexion conector = new FormReconexion(this);
                conector.ShowDialog(principal);
                if (conector.cancelado) {
                    return false;
                }
            }
            if (socket.abierto) {
                socket.mensajeRecibido += new MensajeRecibidoEventHandler(socket_mensajeRecibido);
                socket.errorSocket += new ErrorSocketEventHandler(socket_errorSocket);
                return true;
            }
            return false;
        }

        void socket_errorSocket(object sender, MensajeRecibidoArgs e) {
            principal.Invoke(errorDelegate, new Object[] { e.codigo, e.mensaje });    
        }

        public void abrir(bool principal) {
            if (!Configuracion.instanciaUnica().usandoUDP) {
                if (principal) {
                    socket.host = Configuracion.instanciaUnica().ipServidor;
                    socket.puerto = Configuracion.instanciaUnica().puerto;
                } else {
                    socket.host = Configuracion.instanciaUnica().ipServidorSec;
                    socket.puerto = Configuracion.instanciaUnica().puertoSec;
                }
            } else {
                if (udp == null) {
                    udp = new SocketUDP(Configuracion.instanciaUnica().nombreServidor, Configuracion.instanciaUnica().puerto);
                }
                string ip = udp.buscarServidorEnLaRed(1500);
                udp.close();
                udp = null;
                if (ip != "") {
                    socket.host = ip;
                    socket.puerto = Configuracion.instanciaUnica().puerto;
                } else {
                    return;
                }
            }
            socket.open();
        }

        public bool abierto {
            get { return socket.abierto; }
        }

        public void errorEnSocket(int codigo, string msg) {
            if (true) {
                principal.pararTemporizadores();
                if (conectar()) {
                    if (principal.estadoActual == ESTADO_OPERADOR.DESCANSANDO) {
                        principal.arrancarConfirmarDescanso();
                    }
                    ESTADO_OPERADOR estadoGuardado = principal.reconexionRealizada(true);
                    mesa = Configuracion.instanciaUnica().numeroMesa;
                    operador = Configuracion.instanciaUnica().operador;
                    solicitarConexion(estadoGuardado);
                } else {
                    principal.reconexionRealizada(false);
                }
            }
        }

        void socket_mensajeRecibido(object sender, MensajeRecibidoArgs e) {
            principal.BeginInvoke(mensajeDelegate, new Object[] {e.codigo, e.mensaje});            
        }

        public void enviar(COD_ENVIADO codigo, params string[] msgs) {
            StringBuilder sb = new StringBuilder();
            sb.Append(mesa).Append('|').Append(operador);
            for (int i = 0; i < msgs.Length; i++) {
                sb.Append('|').Append(msgs[i]);
            }
            socket.enviar(sb.ToString(), (int)codigo);
        }

        void mensajeDelServidor(int iCodigo, string msg) {
            MSG_RECIBIDO codigo = (MSG_RECIBIDO)iCodigo;
            try {
                switch (codigo) {
                    case MSG_RECIBIDO.OPERADOR_NO_IDENTIFICADO:
                        principal.msgOperadorNoIdentificado();
                        break;
                    case MSG_RECIBIDO.MESA_YA_CONECTADA:
                        principal.msgMesaYaConectada();
                        break;
                    case MSG_RECIBIDO.CIERRE_URGENTE:
                        principal.peticionDeCierre(msg);
                        break;
                    case MSG_RECIBIDO.CONEXION_ACEPTADA:
                        principal.conexionAceptada(msg);
                        break;
                    case MSG_RECIBIDO.CLIENTE_ENVIADO:
                        principal.nuevoClienteNoVinculado(msg);
                        principal.recibirCliente(msg, true);
                        principal.crearFicheroEstadoCliente();
                        break;
                    case MSG_RECIBIDO.NUEVO_CONTRIBUYENTEVINCULADO:
                        principal.recibirVinculado(msg);
                        principal.crearFicheroEstadoCliente();
                        if (principal.estadoActual == ESTADO_OPERADOR.TRABAJANDO) {
                            principal.cambiarFinalizar(false);
                        }
                        break;
                    case MSG_RECIBIDO.MENSAJE_DEL_ADMINISTRADOR:
                        principal.mensajeRecibidodelAdministrador(msg);
                        break;
                    case MSG_RECIBIDO.MENSAJE_DEL_GESTOR:
                        principal.mensajeRecibidodelGestor(msg);
                        break;
                    case MSG_RECIBIDO.MENSAJE_RECIBIDO:
                        principal.confirmaMensajeRecibido();
                        break;
                    case MSG_RECIBIDO.COMIENZO_ENVIO_COLAS:
                        principal.iniciarRecepcionColas();
                        break;
                    case MSG_RECIBIDO.REGISTRO_COLAS:
                        principal.recibidoRegistroDeColas(msg);
                        break;
                    case MSG_RECIBIDO.CATEGORIA_ESTADISTICA:
                        principal.recibidaCategoriaEstadistica(msg);
                        break;
                    case MSG_RECIBIDO.PENDIENTES:
                        principal.recibirCuentaPendientes(msg);
                        break;
                    case MSG_RECIBIDO.EJECUTA_SCRIPT:
                        principal.ejecutaScript(msg);
                        break;
                    case MSG_RECIBIDO.MENSAJE_RECIBIDO_MASCITAS:
                        principal.prepararSegundasCitas(msg);
                        break;
                    case MSG_RECIBIDO.REINICIAR:
                        principal.reiniciarConEstado();
                        break;
                    case MSG_RECIBIDO.ESTADO_COLAS:
                        principal.reeenviar(msg);
                        break;
                    case MSG_RECIBIDO.ESTADO_BOTONCOLACERO:
                        principal.prepararBotoncolaCeroADM(msg);
                        break;
                    case MSG_RECIBIDO.HUECO_CONCEDIDO:
                        principal.huecoAceptado(msg);
                        break;
                    case MSG_RECIBIDO.HUECO_DENEGADO:
                        principal.huecoDenegado(msg);
                        break;
                    case MSG_RECIBIDO.LISTA_OPERADORES:
                        principal.mostrarOperadoresEnFicha(msg);
                        break;



                }
            } catch (Exception exc) {
                LogExcepcion.guardar("Excepcion en NuevoClienteNoVinculado", exc);
            }
        }

        internal void solicitarConexion(ESTADO_OPERADOR estadoInicio) {
            enviar(COD_ENVIADO.CONEXION, ((int)estadoInicio).ToString(), principal.version + " (.NET)");
        }

        public void enviarCambioEstado(COD_ENVIADO codigo, string id) {
            enviar(codigo, id, calcularIntervalo());
        }

        public void enviarFinalizacion(COD_ENVIADO codigo, string id, string mensaje) {
            enviar(codigo, id, calcularIntervalo(), mensaje);
        }

        private string calcularIntervalo() {
            TimeSpan ts = DateTime.Now.Subtract(horaActual);
            horaActual = DateTime.Now;
            int valor = System.Convert.ToInt32(ts.TotalSeconds);
            return valor.ToString();
        }

        public void enviarEstoyVivo() {
            enviar(COD_ENVIADO.ESTOY_VIVO);
        }

        internal void enviarEstadoNavegacion(bool navegar) {
            enviar(COD_ENVIADO.ENVIO_ESTADO_NAVEGACION, navegar? "0" : "1");
        }

        internal void desconectar() {
            socket.close();
        }

        internal void solicitarListaAdministradores() {
            enviar(COD_ENVIADO.SOLICITAR_LISTA_ADMINISTRADORES  , "-1");
        }
        public void solicitarOperadores(string cola)
        {
            enviar(COD_ENVIADO.SOLICITO_LISTA_OPERADORES, cola);
        }
    }
}
