/* 
* 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 sockets;
/*
 * MessageSocket.java
 *
 * Created on 20 de enero de 2005, 22:05
 */

/**
 *
 * @author Fernando
 */

import java.net.*;
import java.io.*;
import java.util.*;
import javax.swing.event.*;

public class MessageSocket {
    private int puerto;
    private boolean procesarMensajes;
    private String host;
    private String separador = "\n";
    private String errorSocket;
    private boolean _abierto;
    private Socket socket = null;
    private PrintWriter out = null;
    private Thread hiloSocket;
    private StringBuffer sb;
    private EventListenerList listenerList;
    private java.text.NumberFormat nf;
    private boolean modoBin;
    
    /** Creates a new instance of MessageSocket */
    public MessageSocket() {
        sb = new StringBuffer(4096);
        listenerList = new EventListenerList();
        nf = java.text.NumberFormat.getInstance();
        nf.setGroupingUsed(false);
        nf.setMinimumIntegerDigits(3);
        File salidaBin = new File("c:\\salida.bin");
        if(salidaBin.exists()) {
            salidaBin.delete();
        }
    }
    
    public boolean open() {
        if(_abierto) close();
        try {
            socket = new Socket();
            socket.setReceiveBufferSize(32000);
            socket.setSendBufferSize(512);
            socket.connect(new InetSocketAddress(getHost(), getPuerto()));
            _abierto = true;
            out = new PrintWriter(socket.getOutputStream());
            return iniciarHiloLectura();
        }  catch(IOException ioexc)  {
            fireErrorSocket(ioexc.getMessage(), 0);
            return false;
        }
    }
    
    public boolean close() {
        try {
            if(!_abierto) return true;
            hiloSocket.interrupt();
            socket.close();
            _abierto = false;
            return true;
        } catch(IOException ioexc)  {
            return false;
        }
    }
    
    boolean iniciarHiloLectura() {
        hiloSocket = new ThreadLecturaSocket();
        hiloSocket.start();
        return true;
    }
    
    void enviaBufer(byte[] bufer, int leidos) {
        fireBuferRecibido(bufer, leidos);
    }
    
    void procesaBufer(byte[] bufer, int leidos) {
        int largoInicial = sb.length();
        sb.append(new String(bufer, 0, leidos));
        int posInicio = 0;
        int posFinal;
        for(int i=0; i < leidos; i++) {
            if(bufer[i] == 10) {  // caracter de salto de linea
                posFinal = largoInicial + i;
                if(i > 0 && bufer[i-1] == 13) posFinal--;
                if(posFinal - posInicio > 0) {
                    nuevoMensaje(sb.substring(posInicio, posFinal));
                }
                posInicio = largoInicial + i + 1;
            }
        }
        if(posInicio > 0) {
            sb.delete(0, posInicio);
        }
    }
    
    void nuevoMensaje(String msj) {
        if(procesarMensajes)
            procesaMensaje(msj);
        else
            fireMensajeRecibido(msj, 0);
    }
    
    void procesaMensaje(String msj) {
        int numMensaje = 999;
        String mensaje;
        try {
            numMensaje = Integer.parseInt(msj.substring(0,3));
        } catch(Exception exc) {}
        if(numMensaje == 999)
            mensaje = msj;
        else
            mensaje = msj.substring(3);
        fireMensajeRecibido(mensaje, numMensaje);
    }
    
    public void addMessageSocketListener(MessageSocketListener listener) {
        listenerList.add(MessageSocketListener.class, listener);
    }
    public void removeMessageSocketListener(MessageSocketListener listener) {
        listenerList.remove(MessageSocketListener.class, listener);
    }
    
    void fireMensajeRecibido(String msj, int codigo) {
        MensajeRecibidoEvent ev = new MensajeRecibidoEvent(this, msj, codigo);
        EventListener[] listeners = listenerList.getListeners(MessageSocketListener.class);
        for(int i=0; i < listeners.length; i++) {
            MessageSocketListener listener = (MessageSocketListener)listeners[i];
            listener.mensajeRecibido(ev);
        }
    }
    
    void fireBuferRecibido(byte[] bufer, int largo) {
        BuferRecibidoEvent ev = new BuferRecibidoEvent(this, bufer, largo);
        EventListener[] listeners = listenerList.getListeners(MessageSocketListener.class);
        for(int i=0; i < listeners.length; i++) {
            MessageSocketListener listener = (MessageSocketListener)listeners[i];
            listener.buferRecibido(ev);
        }
    }
    
    void fireErrorSocket(String msj, int codigo) {
        MensajeRecibidoEvent ev = new MensajeRecibidoEvent(this, msj, codigo);
        EventListener[] listeners = listenerList.getListeners(MessageSocketListener.class);
        for(int i=0; i < listeners.length; i++) {
            MessageSocketListener listener = (MessageSocketListener)listeners[i];
            listener.errorSocket(ev);
        }
    }
    
    public void enviar(String mensaje, int codigo) {
        String data;
        if(codigo == 999)
            data = mensaje + separador;
        else
            data = nf.format(codigo) + mensaje + separador;
        enviar(data);
    }
    
    public void enviar(String mensaje) {
        out.print(mensaje);
        out.flush();
    }
    
    void grabaBufer(byte[] bufer, int leidos) {
        String lt = "\n*************************************************\n";
        byte[] lineaTonta = lt.getBytes();
        try{
            FileOutputStream salida = new FileOutputStream("c:\\salida.bin", true);
            salida.write(bufer, 0, leidos);
            salida.write(lineaTonta);
            salida.close();
            
        } catch(Exception exc){}
    }
    
    class ThreadLecturaSocket extends Thread {
        public void run() {
            byte[] bufer = new byte[4096];
            try {
                InputStream inStr = socket.getInputStream();
                while(true) {
                    int leidos = inStr.read(bufer);
                    //grabaBufer(bufer, leidos);
                    if(leidos == -1) {
                        throw(new SocketException("Connection: Desconectado del servidor"));
                    }
                    if(modoBin)
                        enviaBufer(bufer, leidos);
                    else
                        procesaBufer(bufer, leidos);
                }
            } catch(IOException ioexc) {
                fireErrorSocket(ioexc.getMessage(), 0);
            } catch(Exception exc) {
                exc.printStackTrace();
            }
        }
    }
    
    public int getPuerto() {
        return puerto;  }
    public void setPuerto(int puerto) {
        boolean reabrir = _abierto;
        if(reabrir) close();
        this.puerto = puerto;
        if(reabrir) open();
    }
    public void setProcesaMensajes(boolean procesaMensajes) {
        this.procesarMensajes = procesaMensajes;  }
    public String getHost() {
        return host;  }
    public void setHost(String host) {
        boolean reabrir = _abierto;
        if(reabrir) close();
        this.host = host;
        if(reabrir) open();
    }
    public Socket getSocket() {
        return socket; }
    
    public String ultimoError() {
        String s = errorSocket;
        errorSocket = "";
        return s;
    }
    public boolean abierto() {
        return _abierto; }
    
    public void setModoBin(boolean v) {
        modoBin = v;
    }
    public boolean isModoBin() {
        return modoBin;
    }
    
}


