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

import java.util.*;
import java.util.zip.*;
import sockets.*;
import javax.swing.*;
import java.io.*;

public class CargadorCliente extends javax.swing.JFrame {
   //<editor-fold desc="Constantes y variables ">

   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;
   MessageSocket sock;
   String ficheroFTP;
   ReceptorFichero receptor;
   Configuracion conf;
   long versionEnviada;
   ILanzable cliente;
   public boolean autoUpdate = true;
   boolean necesarioApagar = false;
   String param;
   String[] listaZips = null;
   int siguienteZip = 0;
   boolean recibiendoZips = false;
   String raizZip = "c:\\";
   String autoRun = null;
   String sTipoCliente = "";
   //</editor-fold>

   public CargadorCliente(Configuracion conf, ILanzable cliente) {
      this.conf = conf;
      this.cliente = cliente;
      String[] ps = conf.fichero.split("\\.");
      sTipoCliente = ps[0].toLowerCase();
      initComponents();
   }

   public void lanzar(String param) {
      this.param = param;
      setSize(380, 300);
      setLocationRelativeTo(null);
      setVisible(true);
      conectar();
   }

   void MsgBox(String txt) {
      JOptionPane.showMessageDialog(this, txt);
   }

   void conectar() {
      sock = new MessageSocket();
      sock.setProcesaMensajes(true);
      if (conf.usandoUDP) {
         sockets.SocketUDP udp = new sockets.SocketUDP(conf.nombreServidor, conf.ipPort);
         String ip = udp.buscarServidorEnLaRed(2000);
         if (!ip.equals("")) {
            sock.setHost(ip);
            sock.setPuerto(conf.ipPort);
         }
         if (ip.equals("") || !sock.open()) {
            mm("No se puede localizar el servidor " + conf.nombreServidor);
            mm("Se cancela la ejecución");
            mm("Puede cerrar esta ventana");
            necesarioApagar = true;
            return;
         }
         udp.close();
      } else {
         sock.setHost(conf.ipServidor);
         sock.setPuerto(conf.ipPort);
         if (!sock.open()) {
            if (!conectarSecundario()) {
               mm("El servidor ATENEO en " + conf.ipServidor + " no contesta");
               mm("Se cancela la ejecución");
               mm("Puede cerrar esta ventana");
               necesarioApagar = true;
            }
            return;
         }
      }
      mm("Conectado al servidor " + conf.ipServidor);
      sock.addMessageSocketListener(new MessageSocketListener() {

         @Override
         public void mensajeRecibido(MensajeRecibidoEvent ev) {
            final int codigo = ev.getCodigo();
            final String msj = ev.getMensaje();
            Runnable runnable = new Runnable() {
               @Override
               public void run() {
                  mensajeServidor(codigo, msj);
               }
            };
            if(javax.swing.SwingUtilities.isEventDispatchThread()) {
               javax.swing.SwingUtilities.invokeLater(runnable);
            } else {
               mensajeServidor(ev.getCodigo(), ev.getMensaje());
            }
         }

         @Override
         public void buferRecibido(BuferRecibidoEvent ev) {
            final byte[] datos  = ev.getBufer();
            final int largo = ev.getLargo();
            Runnable runnable = new Runnable() {
               @Override
               public void run() {
                  bytesRecibidos(datos, largo);
               }
            };
            if(javax.swing.SwingUtilities.isEventDispatchThread()) {
               javax.swing.SwingUtilities.invokeLater(runnable);
            } else {
               bytesRecibidos(ev.getBufer(), ev.getLargo());
            }
         }

         @Override
         public void errorSocket(MensajeRecibidoEvent evt) {
            int codigoErr = evt.getCodigo();
            switch(codigoErr) {
               case 0:     // IOException
                  // no se pque poner aqui
                  break;
               case 1:     // Timeout en Read
                  mm(evt.getMensaje());
                  mm("Cancelando descarga...");
                  try {Thread.sleep(5000); } catch(Exception exc) {}
                  System.exit(-1);
            }

         }
      });
      enviar(MENSAJE_PEDIR_LISTA_ARCHIVOS, sTipoCliente);  // pedimos lista de zips antes que nada

   }

   boolean conectarSecundario() {
      sock = new MessageSocket();
      sock.setHost(conf.ipServidorSec);
      sock.setPuerto(conf.ipPortSec);
      sock.setProcesaMensajes(true);
      if (!sock.open()) {
         return false;
      }
      mm("Conectado al servidor " + conf.ipServidorSec);
      lanzaCliente(false);
      return true;
   }

   boolean compruebaVersion(String sVersionEnviada) {
      return compruebaVersion(sVersionEnviada, conf.fichero);
   }

   boolean compruebaVersion(String sVersionEnviada, String archivo) {
      long versionActual;
      try {
         versionEnviada = Long.parseLong(sVersionEnviada);
      } catch (Exception exc) {
         versionEnviada = 0L;
      }
      try {
         File file = new File(archivo);
         versionActual = file.lastModified();
      } catch (Exception exc) {
         versionActual = 1L;
      }
      return versionEnviada != versionActual;
   }

   void enviar(int codigo) {
      sock.enviar(conf.tipoCliente, codigo);
   }

   void enviar(int codigo, String param) {
      sock.enviar(param, codigo);
   }

   void mensajeServidor(int codigo, String mensaje) {
      switch (codigo) {
         case COD_ENVIO_VERSION:
            if (!recibiendoZips) {
               if (compruebaVersion(mensaje)) {
                  enviar(MENSAJE_LISTO_PARA_RECIBIR);
               } else {
                  lanzaCliente(false);
               }
            } else {
               if (compruebaVersion(mensaje, listaZips[siguienteZip])) {
                  enviar(MENSAJE_LISTO_PARA_RECIBIR_ZIP, listaZips[siguienteZip]);
               } else {
                  pedirSiguienteZip();
               }
            }
            break;
         case COD_ENVIO_ZIP:
            preparaRecepcionFichero(mensaje, false);
            enviar(MENSAJE_COMENZAR_ENVIO_ZIP, listaZips[siguienteZip]);
            break;
         case COD_ENVIO_JAR:
            preparaRecepcionFichero(mensaje, true);
            enviar(MENSAJE_COMENZAR_ENVIO);
            break;
         case COD_ENVIO_LISTA:
            if (mensaje.isEmpty()) {   // no hay zips, pedimos el jar
               enviar(MENSAJE_ENVIAR_ARCHIVO);
            } else {
               preparaRecepcionZips(mensaje);
               if (listaZips != null && listaZips.length > 0) {
                  pedirSiguienteZip();
               }
            }
            break;
         case COD_ERROR_TRANSFERENCIA:
            mm("Transferencia abortada por el servidor");
            break;
      }
   }

   void mm(String str) {
      memo.append(str + "\n");
   }

   void preparaRecepcionZips(String msg) {
      listaZips = msg.split("\\|");
      siguienteZip = -1;
      recibiendoZips = true;
   }

   void pedirSiguienteZip() {
      siguienteZip++;
      if (siguienteZip < listaZips.length) {
         enviar(MENSAJE_ENVIAR_ZIP, listaZips[siguienteZip]);
      } else {
         recibiendoZips = false;
         enviar(MENSAJE_ENVIAR_ARCHIVO);
      }
   }

   void zipRecibido() {
      procesarZip();
      pedirSiguienteZip();
   }

   void procesarZip() {
      raizZip = "";
      autoRun = "";
      try {
         ZipFile zip = new ZipFile(listaZips[siguienteZip]);
         ZipEntry manifiestoZip = zip.getEntry("Manifiesto");
         if (manifiestoZip != null) {
            extraerManifiesto(zip.getInputStream(manifiestoZip));
         }
         mm("Extrayendo contenido de " + listaZips[siguienteZip]);
         mm("    en " + (raizZip.equals("") ? System.getProperty("user.dir") : raizZip));
         Enumeration entradas = zip.entries();
         while (entradas.hasMoreElements()) {
            ZipEntry entrada = (ZipEntry) entradas.nextElement();
            if (entrada.isDirectory()) {
               (new File(raizZip + entrada.getName())).mkdirs();
            } else if (!entrada.getName().equalsIgnoreCase("manifiesto")) {
               extraerArchivo(zip.getInputStream(entrada), raizZip + entrada.getName());
            }
         }
         zip.close();
         if (!autoRun.equals("")) {
            ejecutarAutoRun();
         }
      } catch (Exception exc) {
         exc.printStackTrace();
      }
   }

   void extraerArchivo(InputStream in, String destino) throws IOException {
      FileOutputStream out = new FileOutputStream(destino);
      byte[] bufer = new byte[1024];
      int len;
      while ((len = in.read(bufer)) >= 0) {
         out.write(bufer, 0, len);
      }
      in.close();
      out.close();
   }

   void extraerManifiesto(InputStream in) throws IOException {
      byte[] bufer = new byte[2048];
      int len = in.read(bufer);
      String contenido = new String(bufer, 0, len);
      String[] p1 = contenido.split("\\n");
      for (String s : p1) {
         String[] p2 = s.trim().toLowerCase().split("=");
         if (p2[0].equals("raiz")) {
            if (p2.length == 1 || p2[1].equals(".\\")) {
               raizZip = "";
            } else {
               raizZip = p2[1];
               if(!raizZip.endsWith("\\")) raizZip += "\\";
            }
         } else if (p2[0].equals("autorun") && p2.length > 1) {
            autoRun = p2[1];
         }
      }
      in.close();
   }

   void ejecutarAutoRun() {
      try {
         mm("Ejecuto " + autoRun);
         Process proceso = Runtime.getRuntime().exec(autoRun);
         proceso.waitFor();
      } catch (InterruptedException interruptedExc) {
         System.err.println(interruptedExc.getMessage());
      } catch (Exception exc) {
      }
   }

   void preparaRecepcionFichero(String msg, boolean esJar) {
      try {
         String[] partes = msg.split("\\|");
         receptor = new ReceptorFichero(partes[0], partes[1]);
         if (esJar) {
            if (autoUpdate) {
               ficheroFTP = "FicheroTemporal.jar";
            } else {
               ficheroFTP = conf.fichero;
            }
         } else {
            ficheroFTP = listaZips[siguienteZip];
         }
         mm("Preparo recepción de " + ficheroFTP);
         receptor.abrir(new File(ficheroFTP));
         sock.setModoBin(true);
         sock.ponerSocketTimeout(10000);
      } catch (Exception exc) {
         MsgBox(exc.getClass().getName() + " = " + exc.getMessage());
      }
   }

   void bytesRecibidos(byte[] bufer, int largo) {
      try {
         if (!receptor.masBytes(bufer, largo)) {
            sock.setModoBin(false);
            sock.quitarSocketTimeout();
            mm("Recibido " + ficheroFTP);
            mm("Bytes: " + receptor.getEscritos());
            if (receptor.isCrcOK()) {
               receptor.fichero.setLastModified(versionEnviada);
               mm("CRC OK");
               if (!recibiendoZips) {
                  lanzaCliente(autoUpdate);
               } else {
                  zipRecibido();
               }
            } else {
               mm("CRC  MAL !!");
               mm("CRC Esperado: " + receptor.getCrcEsperado() + "  CRC Recibido: " + receptor.getCrc() + "\n");
               if (!recibiendoZips) {
                  mm("IMPOSIBLE CONTINUAR");
                  mm("Cierre esta ventana");
                  volcarLogConsola();
                  File file = new File(ficheroFTP);
                  file.delete();
                  necesarioApagar = true;
               }
            }
         }
      } catch (IOException exc) {
         MsgBox("Error en la grabacion de los bytes recibidos");
         receptor.dispose();
         sock.setModoBin(false);
      }
   }

   void lanzaCliente(boolean externo) {
      mm("Lanzando aplicación");
      volcarLogConsola();
      sock.close();
      if (externo) {
         try {
            File file = new File("InstalaJAR.vbs");
            PrintStream salida = new PrintStream(new FileOutputStream(file, false));
            salida.println("WScript.sleep 1500");
            salida.println("Set fso = CreateObject(\"Scripting.FileSystemObject\")");
            salida.println("if fso.FileExists(\"FicheroTemporal.jar\") then");
            salida.println("    if fso.FileExists(\"" + conf.fichero + "\") then");
            salida.println("        fso.DeleteFile \"" + conf.fichero + "\""); //, \"anterior.jar\"");

            salida.println("    end if");
            salida.println("    fso.MoveFile \"FicheroTemporal.jar\", \"" + conf.fichero + "\"");
            salida.println("end if");
            salida.println("set shell = wscript.CreateObject(\"wscript.Shell\")");
            salida.println("shell.Exec \"javaw -jar " + conf.fichero + " ejecutarSinDescargar" + " " + param + "\"");
            salida.println("fso.DeleteFile \"InstalaJAR.vbs\"");
            salida.close();
            Runtime.getRuntime().exec("CSCRIPT InstalaJAR.vbs");
            System.exit(0);
         } catch (Exception exc) {
            exc.printStackTrace();
         }
      } else {
         setVisible(false);
         cliente.lanzaCliente();
      }
   }

   void volcarLogConsola() {
      try {
         PrintStream log = new PrintStream("logConsola.txt");
         log.print(memo.getText());
      } catch (Exception exc) {
      }
   }

   // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   private void initComponents() {

      jScrollPane1 = new javax.swing.JScrollPane();
      memo = new javax.swing.JTextArea();

      setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
      setTitle("Cargador Cliente ATENEO");
      addWindowListener(new java.awt.event.WindowAdapter() {
         public void windowClosed(java.awt.event.WindowEvent evt) {
            formWindowClosed(evt);
         }
      });

      jScrollPane1.setViewportView(memo);

      getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);

      pack();
   }// </editor-fold>//GEN-END:initComponents
    private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
       if (this.necesarioApagar) {
          System.exit(-1);
       }
    }//GEN-LAST:event_formWindowClosed
   // Variables declaration - do not modify//GEN-BEGIN:variables
   private javax.swing.JScrollPane jScrollPane1;
   private javax.swing.JTextArea memo;
   // End of variables declaration//GEN-END:variables
}
