
import javax.microedition.io.*;
import javax.bluetooth.*;
import java.io.*;
import java.util.*;
import javax.microedition.lcdui.*;

/**
 * Clase encargada de manejar la conexión de bluetooth del equipo móvil.
 * Se envía un String de datos y se espera por la respuesta de otro
 * String desde el Servidor.
 *
 * @author JJDA & JPAL
 */
public class conexionBluetooth implements CommandListener {

    static final Command backCommand = new Command("Volver", Command.BACK, 0);

    private final String nombreServidor = new String("DV41212");
    //private final String nombreServidor = new String("JOSE-DESKTOP");
    private String code;

    Listener listener = new Listener();

    //Objetos Bluetooth necesarios
    public LocalDevice dispositivoLocal;
    public DiscoveryAgent da;

    //Lista de dispositivos y servicios encontrados
    public static Vector dispositivos_encontrados = new Vector();
    public static Vector servicios_encontrados = new Vector();
    int indice = -1;

    //Variables para manejar la interfaz del usuario.
    private logIn midlet;
    private Display display;
    private Form f;
    private StringItem busq;


    /**
     * Constructor de la clase conexionBluetooth.
     * 
     * @param midlet Referencia al midlet que lo invoca
     * @param disp Referencia al display de la clase que lo invoca.
     */
    public conexionBluetooth(logIn midlet, Display disp){

        this.midlet= midlet;
        this.display = disp;
        
        f= new Form("");
        busq = new StringItem("Busqueda","Buscando Servidor...");
        f.append(busq);
        f.addCommand(backCommand);
        f.setCommandListener(this);
        display.setCurrent(f);
   }


    /**
     * Método que busca el servidor que contenga el "friendly name" del servidor
     * de la aplicación.
     *
     * @return retorna el indice del vector "dispositivos_encontrados" en que está el Servidor. Si no encuentra retorna -1.
     */
    private int encontrarServidor(){

        if(dispositivos_encontrados.size()>0){
            for(int i=0;i<dispositivos_encontrados.size();i++){
                try{
                    RemoteDevice dispositivoRemoto = (RemoteDevice)dispositivos_encontrados.elementAt(i);
                    if(dispositivoRemoto.getFriendlyName(false).equals(nombreServidor))
                        return i;


                }catch(Exception e){
                    System.out.println("Se ha producido una excepcion");
                }
            }
        }

        return -1;
    }


    /**
     * Método que busca los dispositivos disponibles con conexión.
     * (Con "startInquiry" se comienza la busqueda. En la implementación de "Listener" se puede ver
     * como continua el proceso hasta enviar los datos al servidor.
     *
     * @param datos Son los datos que se desea enviar al servidor (en este caso el código de barras)
     */
    public void sendToServer(String datos){
        code = new String(datos);

        dispositivos_encontrados.removeAllElements();
        servicios_encontrados.removeAllElements();

        try{
            dispositivoLocal = LocalDevice.getLocalDevice();
            dispositivoLocal.setDiscoverable(DiscoveryAgent.GIAC);
            da = dispositivoLocal.getDiscoveryAgent();
            da.startInquiry(DiscoveryAgent.GIAC,listener);
        }
        catch(BluetoothStateException be){
            mostrarAlarma(be, 0);
        }
    }



    /**
     * Método encargado de desplegar un mensaje de Alarma en caso de alguna excepción.
     *
     * @param e Excepción ocurrida
     * @param tipo tipo de excepción que ocurrió.
     */
    public void mostrarAlarma(Exception e, int tipo){
        Alert alerta=null;

        if(tipo == 0){
            alerta = new Alert("Excepcion","Se ha producido la excepcion "+e.getClass().getName(), null, AlertType.ERROR);
        }
        else if(tipo==1){
            alerta = new Alert("Error","No ha seleccionado un dispositivo ", null, AlertType.ERROR);
        }
        else if(tipo==2){
            alerta = new Alert("Informacion","El mensaje ha sido enviado ", null, AlertType.INFO);
        }

        alerta.setTimeout(Alert.FOREVER);
        display.setCurrent(alerta);
    }


    /**
     * Método encargado de enviar el string pasado como parámetro al Servidor
     * y recibir la respuesta.
     *
     * @param msg String que se desea enviar el servidor.
     * @return retorna el string recibido del servidor.
     */
    public String enviarMensaje(String msg){

        StreamConnection con = null;
        DataOutputStream out = null;
        DataInputStream in = null;

        String entrada = null;
        ServiceRecord sr = (ServiceRecord)servicios_encontrados.elementAt(indice);

        //Se obtiene la URL asociada a este servicio en el dispositivo remoto
        String URL = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT,false);

        try{
            //Se obtiene la conexio y el stream de este servicio
            con = (StreamConnection)Connector.open(URL);
            out = con.openDataOutputStream();
            in = con.openDataInputStream();

            //Se escriben datos en el stream
            out.writeUTF(msg);
            out.flush();

            entrada = in.readUTF();
            
        } catch(Exception e){
            mostrarAlarma(e, 0);
        }finally {
            try{
              //Se cierra el Stream de entrada
              if(in!= null) {
                in.close();
              }
              //Se cierra el Stream de salida
              if(out != null) {
                out.close();
              }
              //Se cierra la conexión
              if(con != null) {
                con.close();
              }

            }catch(IOException e) {}
        }
        
        return entrada;
    }


    /**
     * Implementación de la clase DiscoveryListener, requerida para comunicación con bluetooth.
     */
    public class Listener implements DiscoveryListener{

        /**
         * Método que se invoca cada vez que el llamado "startInquiry" encuentra un nuevo
         * dispositivo remoto. En esta implementación tan solo se agrega el dispositivo encontrado
         * a un vector.
         *
         * @param dispositivoRemoto Dispositivo encontrado.
         * @param clase Tipo de dispositivo encontrado.
         */
         public void deviceDiscovered(RemoteDevice dispositivoRemoto, DeviceClass clase){

            dispositivos_encontrados.addElement(dispositivoRemoto);

         }


        /**
         *  Método que se invoca cuando ha terminado la busqueda de dispositivos remotos
         * por parte del llamado "startInquiry" o "cancelInquiry". En esta implementación
         * el método confirma que existan dispositivos encontrados y muestra un mensaje de alerta.
         * Por otro lado, si encuentra dispositivos, invoca al método "encontrarServidor" el cual
         * buscará si el servidor de la aplicación se encuentra dentro del vector de dspositivos
         * encontrados. Si no es encontrado se muestra una alarma. Si es encontrado se busca que
         * el servicio del servidor exista.
         *
         * @param discType Indica el motivo de término de la busqueda.
         */
         public void inquiryCompleted(int discType){

            switch(discType) {
                case DiscoveryListener.INQUIRY_COMPLETED:
                    if(dispositivos_encontrados.size()==0){
                        Alert alerta = new Alert("Problema de Rango","Servidor fuera de alcance",null, AlertType.INFO);
                        alerta.setTimeout(Alert.FOREVER);
                        display.setCurrent(alerta);
                        mostrarVolver();
                    }
                    else{
                        indice = encontrarServidor();

                        if(indice == -1){

                            Alert alerta = new Alert("Problema de Rango","Servidor fuera de alcance",null, AlertType.INFO);
                            //alerta.addCommand(SecondMidletServlet.backCommand);
                            alerta.setTimeout(Alert.FOREVER);
                            display.setCurrent(alerta);
                            mostrarVolver();
                        }else{

                            servicios_encontrados.removeAllElements();
                            //Se busca el servicio de puerto serie en el dispositivo seleccionado
                            RemoteDevice dispositivo_remoto =(RemoteDevice)dispositivos_encontrados.elementAt(indice);
                            try{
                                //Buscamos en el puerto serie 0x1101
                                da.searchServices(null,new UUID[]{new UUID(0x1101)},dispositivo_remoto,new Listener());
                            }
                            catch(BluetoothStateException be){
                                mostrarAlarma(be, 0);
                            }
                        }
                    }
                    break;

                case DiscoveryListener.INQUIRY_TERMINATED: //Cancelado
                    Alert alerta = new Alert("Acción de Usuario","Busqueda cancelada",null, AlertType.INFO);
                    dispositivos_encontrados.removeAllElements();
                    servicios_encontrados.removeAllElements();
                    alerta.setTimeout(Alert.FOREVER);
                    display.setCurrent(alerta);
                    break;
                case DiscoveryListener.INQUIRY_ERROR: //Falla
                    alerta = new Alert("Error","Busqueda terminada por error",null, AlertType.ERROR);
                    dispositivos_encontrados.removeAllElements();
                    servicios_encontrados.removeAllElements();
                    alerta.setTimeout(Alert.FOREVER);
                    display.setCurrent(alerta);
                    mostrarVolver();
                    break;
            }

         }


        /**
         * Método que se invoca cada vez que se encuentra un servicio buscado por el llamado
         * "searchServices". En esta implementación cada servicio encontrado se almacena en
         * el vector "servicios_encontrados".
         *
         * @param transID Identificador de búsqueda.
         * @param servRecord Describe las características de servicio.
         */
        public void servicesDiscovered(int transID, ServiceRecord[] servRecord){
            
            for(int i=0;i<servRecord.length;i++){
                servicios_encontrados.addElement(servRecord[i]);
            }
        }


        /**
         * Método que se invoca al terminar la busqueda de serviios por parte de llamado
         * "searchServices". Esta implementación confirma que se haya encontrado el servicio
         * deseado, si lo encuentra invoca el metodo "enviarMensaje" con el string "code" como
         * parámetro. Si no encuentra muestra una alarma indicando que no hay servicio disponible.
         *
         * @param transID Identificador de la búsqueda.
         * @param respCode Motivo de la finalización.
         */
        public void serviceSearchCompleted(int transID, int respCode){

            switch(respCode) {
                case DiscoveryListener.SERVICE_SEARCH_COMPLETED: //Busqueda terminada
                    if(servicios_encontrados.size()>0){
                        String rec = enviarMensaje(code);

                        if(rec.equals("false")){
                            busq.setLabel("Autenticación");
                            busq.setText("Login o password incorrecto");
                            f.append(busq);
                            display.setCurrent(f);
                        }
                        else{
                                busq.setLabel("Autenticación");
                             busq.setText("Bienvenido");
                                f.append(busq);
                                display.setCurrent(f);
                        }
                    }
                    break;
                case DiscoveryListener.SERVICE_SEARCH_TERMINATED: //Cancelar
                    Alert alerta= new Alert("Servicio","Busqueda cancelada",null, AlertType.INFO);
                    alerta.setTimeout(Alert.FOREVER);
                    display.setCurrent(alerta);
                    break;

                case DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE:  //Fuera de alcance
                    alerta = new Alert("Problema con el Servidor","Servicio fuera de alcance",null, AlertType.ERROR);
                    alerta.setTimeout(Alert.FOREVER);
                    display.setCurrent(alerta);
                    mostrarVolver();
                    break;

                case DiscoveryListener.SERVICE_SEARCH_NO_RECORDS:  //Servicio no disponible
                    alerta = new Alert("Problema con el Servidor","Servicio no disponible",null, AlertType.INFO);
                    alerta.setTimeout(Alert.FOREVER);
                    display.setCurrent(alerta);
                    mostrarVolver();
                    break;

                case DiscoveryListener.SERVICE_SEARCH_ERROR:  //Error de busqueda
                    alerta = new Alert("Problema con el Servidor","Ocurrió un error en la busqueda",null, AlertType.ERROR);
                    alerta.setTimeout(Alert.FOREVER);
                    display.setCurrent(alerta);
                    mostrarVolver();
                    break;
            }

        }
    }


    /**
     * Método que cambia el texto a desplegar cuando termina el proceso de busqueda
     * de servidor y servicio.
     */
    private void mostrarVolver(){
        busq.setText("Volver para ir al menú");
        f.append(busq);
        display.setCurrent(f);
    }


    /**
     * Implementación del commandAction. Se define el botón que permite
     * regresar al menú principal.
     */
    public void commandAction(Command c, Displayable d) {
        String label = c.getLabel();

        if (label.equals("Volver")){
            da.cancelInquiry(listener);
            
            midlet.mainMenu();

        }
    }
}
