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

package servidor.comunicaciones;

import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.zip.Adler32;
import servidor.ServidorMain;
import servidor.utiles.Global;

public class MensajeCargador extends Mensaje {

   private static final int MENSAJE_ENVIAR_ARCHIVO = 880;
   private static final int MENSAJE_LISTO_PARA_RECIBIR = 881;
   private static final int MENSAJE_COMENZAR_ENVIO = 882;
   private static final int MENSAJE_ENVIAR_ZIP = 883;
   private static final int MENSAJE_LISTO_PARA_RECIBIR_ZIP = 884;
   private static final int MENSAJE_COMENZAR_ENVIO_ZIP = 885;
   private static final int MENSAJE_PEDIR_LISTA_ARCHIVOS = 889;
   private static final int COD_ENVIO_VERSION = 880;
   private static final int COD_ENVIO_JAR = 881;
   private static final int COD_ENVIO_ZIP = 882;
   private static final int COD_ENVIO_LISTA = 883;
   private static final int COD_ERROR_TRANSFERENCIA = 889;
   int tipoCliente = 0;
   static final String[] fichero = {"", "operador.jar", "gestor.jar", "monitor.jar", "administrador.jar", "minigestor.jar", "Autoservicio.jar"};
   static HashMap<String, DatosFichero> datos = new HashMap<>();

   public MensajeCargador(String str, org.quickserver.net.server.ClientHandler handler) {
      super(str, handler);
      tipoCliente = this.getIntParam(0);
   }

   @Override
   public void procesar() {
      try {
         switch (codigo) {
            case MENSAJE_PEDIR_LISTA_ARCHIVOS:
               enviarListaArchivos(this.params[0]);
               break;
            case MENSAJE_ENVIAR_ARCHIVO:
               calculaCrc();
               enviaVersionFicheroACliente();
               break;
            case MENSAJE_ENVIAR_ZIP:
               calculaCrc(params[0]);
               enviaVersionFicheroACliente(params[0]);
               break;
            case MENSAJE_LISTO_PARA_RECIBIR:
               comunicaEnvioFichero();
               break;
            case MENSAJE_LISTO_PARA_RECIBIR_ZIP:
               comunicaEnvioFichero(params[0], false);
               break;
            case MENSAJE_COMENZAR_ENVIO:
               enviaFichero();
               break;
            case MENSAJE_COMENZAR_ENVIO_ZIP:
               enviaFichero(params[0]);
               break;
         }

      } catch (Exception exc) {
         ServidorMain.logException("Error procesando el mensaje de Cargador:\n" + this.toString(), exc);
      }
   }

   void enviarListaArchivos(final String tipo) {
      File carpeta = new File("clientes");
      File[] disponibles = carpeta.listFiles((File dir, String name) -> {
          String n = name.toLowerCase();
          return (n.startsWith(tipo) && n.endsWith(".zip"));
      });
      if (disponibles.length > 0) {
         StringBuilder sb = new StringBuilder();
         for (File f : disponibles) {
            sb.append(f.getName()).append('|');
         }
         sb.setLength(sb.length() - 1);
         enviarLista(sb.toString());
      } else {
         enviarLista(null);
      }
   }

   void enviarLista(String txt) {
      try {
         enviar(COD_ENVIO_LISTA, txt);
      } catch (Exception exc) {
         ServidorMain.logException("Error enviando lista de Zips", exc);
      }
   }

   void enviaVersionFicheroACliente() {
      enviaVersionFicheroACliente(fichero[tipoCliente]);
   }

   void enviaVersionFicheroACliente(String fichero) {
      String version = "";
      try {
         File file = new File("clientes\\" + fichero);
         version = String.valueOf(file.lastModified());
         enviar(COD_ENVIO_VERSION, version);
      } catch (Exception exc) {
         ServidorMain.logException("Error procesando mensaje de Cargador:\n" + this.toString(), exc);
      }
   }

   static public void resetCrc() {
      System.out.println(" ** Este comando ya no es necesario **");
   }

   void calculaCrc() {
      calculaCrc(fichero[tipoCliente]);
   }

   void calculaCrc(String fichero) {
      File file = new File("clientes\\" + fichero);
      if (datos.containsKey(fichero)) {
         DatosFichero df = datos.get(fichero);
         if (file.lastModified() == df.fecha) {
            return;
         }
         datos.remove(fichero);
      }
      try {
         FileInputStream fuente = new FileInputStream(file);
         Adler32 adler = new Adler32();
         byte[] bufer = new byte[1024 * 4];
         int leidos;
         long total = 0;
         while ((leidos = fuente.read(bufer)) > 0) {
            total += leidos;
            adler.update(bufer, 0, leidos);
         }
         fuente.close();
         DatosFichero df = new DatosFichero();
         df.crc = adler.getValue();
         df.size = total;
         df.fecha = file.lastModified();
         datos.put(fichero, df);
      } catch (Exception exc) {
         ServidorMain.logException("ERROR al calcular CRC de " + fichero, exc);
      }
   }

   void comunicaEnvioFichero() {
      comunicaEnvioFichero(fichero[tipoCliente], true);
   }

   void comunicaEnvioFichero(String fichero, boolean esJar) {
      try {
         DatosFichero df = datos.get(fichero);
         enviar((esJar ? COD_ENVIO_JAR : COD_ENVIO_ZIP), df.size + "|" + df.crc);
      } catch (Exception exc) {
         System.out.println(exc.getMessage());
         try {
            enviar(COD_ERROR_TRANSFERENCIA, "Envio cancelado");
         } catch (Exception e) {
         }
         ServidorMain.log.log(Level.WARNING, "Se cancel\u00f3 el envio de {0} a {1}", new Object[]{fichero, handler.getSocket().getInetAddress().toString()});
      }
   }

   void enviaFichero() {
      enviaFichero(fichero[tipoCliente]);
   }

   int maxRetrasoPorVuelta = 5000;
   void enviaFichero(String fichero) {
      FileInputStream  fip = null;
      try {
         int retrasos = 0;
         OutputStream os = handler.getOutputStream();
         byte[] bufer = new byte[1024 * 4];
         int leidos;
         fip = new FileInputStream("clientes\\" + fichero);
         while (true) {
            Date t1 = new Date();
            // intervalo cronometrado
            leidos = fip.read(bufer);
            if(leidos <= 0) break;
            os.write(bufer, 0, leidos);
            os.flush();
            //
            long tt = new Date().getTime() - t1.getTime();
            if(tt > maxRetrasoPorVuelta) {
               retrasos++;
               if(retrasos == 2) { // dos retrasos seguidos, muy malo
                  fip.close();
                  System.err.println("Tomó demasiado tiempo enviar " + fichero + " a " + this.ip);
                  System.err.println("Se cierra la conexión desde este lado");
                  handler.closeConnection();
                  return;
               }
            } else {
               retrasos = 0;
            }
         }
      } catch (Exception exc) {
         ServidorMain.logException("ERROR al enviar " + fichero + " a " + handler.getSocket().getInetAddress().toString(), exc);
      } finally {
         if(fip != null) try {
            fip.close();
         } catch(Exception exc){}
      }
   }

   void enviar(int codigo, String texto) throws Exception {
      String txt = Global.formateaCodigo(codigo);
      if (texto != null) {
         txt += texto;
      }
      handler.sendClientMsg(txt);
   }

   class DatosFichero {

      public long crc;
      public long size;
      public long fecha;
   }
}
