package profilesj2me;

import javax.bluetooth.*;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.ServiceRecord;
import java.util.Vector;
import java.util.Hashtable;

import profiles.*;

/**
 * Si occupa di rilevare i dispositivi nelle vicinanze. Il funzionamento è
 * incapsulato in un thread che costantemente aggiorna la tabella dei
 * dispositivi vicini.
 *
 * @author Lorenzo Moretti
 * @version 1.0
 */

class J2MEBasicServiceDiscoverer extends Thread
    implements DiscoveryListener {
  DiscoveryAgent agent;
  Vector devicesInRange = new Vector();
  Vector tempDevicesInRange = new Vector();
  boolean keepRunning = true;
  long updateInterval = 20000;
  int examinedDevices;
  Vector temp2DevicesInRange = new Vector();
  J2MEBasicService basicService = null;
  Object syncObj = new Object();
  Hashtable devicesLookupTables = new Hashtable();

  J2MEBasicServiceDiscoverer(J2MEBasicService bs) throws BluetoothStateException {
    agent = LocalDevice.getLocalDevice().getDiscoveryAgent();
    basicService = bs;
  }

  public long getUpdateInterval() {
    return updateInterval;
  }

  public void setUpdateInterval(long interval) {
    updateInterval = interval;
  }

  /**
   * Termina l'esecuzione del sistema di aggiornamento.
   */
  public void stopUpdating() {
    keepRunning = false;
  }

  /**
   * Codice di esecuzione del processo. Chiama start() per attivare
   * l'elaborazione su un'altro thread.
   */
  public void run() {
    // Esegui l'aggiornamento
    while (keepRunning) {
      tempDevicesInRange.removeAllElements();
      temp2DevicesInRange.removeAllElements();

      try {
        agent.startInquiry(agent.GIAC, this);
      }
      catch (BluetoothStateException ex) {
        System.out.println(ex);
      }

      try {
        synchronized(syncObj) {
          syncObj.wait();
        }

        // Aggiorna BasicService con la nuova tabella di routing
        basicService.setDevicesInRange(devicesInRange);

        agent.cancelInquiry(this);
        sleep(updateInterval);
      } catch (InterruptedException ex) {
        ex.printStackTrace();
      }
    }
  }

  public synchronized void deviceDiscovered(RemoteDevice remoteDevice,
                               DeviceClass deviceClass) {
    tempDevicesInRange.addElement(remoteDevice);
  }

  public synchronized void servicesDiscovered(int int0, ServiceRecord[] serviceRecordArray) {
    String deviceName = serviceRecordArray[0].getHostDevice().getBluetoothAddress();
    String deviceURL = serviceRecordArray[0].getConnectionURL(
        ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);

    // Se conosce già la tabella di routing della periferica, la inserisce,
    // altrimenti inserisce il record senza tabella.
    J2MEDevice newDevice = (J2MEDevice)devicesLookupTables.get(deviceName);
    if (newDevice == null) {
      newDevice = new J2MEDevice(deviceName);
      newDevice.routingTable = new RoutingTable(newDevice);
    }

    newDevice.setURL(deviceURL);
    System.out.println("Service discovered:" + newDevice);
    temp2DevicesInRange.addElement(newDevice);


    // Richiesta della tabella di routing al vicino
    try {
      TextMessage msg =
          new TextMessage("", LocalDevice.getLocalDevice().getBluetoothAddress(),
                          Message.RT_REQ);
      Vector route = new Vector();
      route.addElement(newDevice.getId());
      msg.setRoute(route);
      basicService.deliverMessage(msg);
    } catch (BluetoothStateException ex) {
      ex.printStackTrace();
    }

  }

  public synchronized void serviceSearchCompleted(int transId, int respCode) {
    devicesInRange.removeAllElements();
    for (int i = 0; i < temp2DevicesInRange.size(); i++) {
      devicesInRange.addElement(temp2DevicesInRange.elementAt(i));
    }


    synchronized(syncObj) {
      syncObj.notifyAll();
    }

  }


  public void inquiryCompleted(int int0) {
    examinedDevices = 0;
    //temp2DevicesInRange.removeAllElements();
    if (tempDevicesInRange.size() > 0) {
      for (int i = 0; i < tempDevicesInRange.size(); i++)
        searchForService((RemoteDevice)tempDevicesInRange.elementAt(i));
    } else {
      devicesInRange.removeAllElements();
      try {
        synchronized(syncObj) {
          syncObj.notifyAll();
        }
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }

  void searchForService(RemoteDevice remoteDevice) {
    UUID[] uuids = new UUID[1];
    uuids[0] = J2MEBasicService.SERVICE_UUID;
    int[] attrSet = new int[2];

    try {
      agent.searchServices(attrSet, uuids, remoteDevice, this);
    }
    catch (BluetoothStateException ex) {
      System.out.print(ex);
      ex.printStackTrace();
    }
  }


  public void rtReceived(J2MEDevice d) {
    // Rimozione dei cicli
    d.getRoutingTable().removeCiclicLoop(basicService.getRegisteredListenerDevice());
    devicesLookupTables.remove(d.getId());
    devicesLookupTables.put(d.getId(),d);
  }
}
