package com.Series40Book;

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

public class BTDiscovery
    extends MIDlet
    implements CommandListener, DiscoveryListener {

  public Display display;
  private BTDeviceList btdevicelist;
  public BTServiceList btservicelist;
  private BTHelp bthelp;

  public Form infoForm;
  private Alert BTAlert;

  public Command help;
  private Command exit;
  public Command backtodevicelist;
  private Command OdeviceInfo;
  private Command OdeviceDiscovery;

  // list of RemoteDevice discovered
  public Vector devices = new Vector();
// list of DeviceClass discovered (not used in this example)
  public Vector deviceClasses = new Vector();
// list of ServiceRecrod discovered for one RemoteDevice
  public Vector services = new Vector();
// index of the currently selected RemoteDevice, obtained from DeviceDiscoveryUI
  public static int selectedDevice = -1;


  private boolean BTError;
  private String BTErrorMsg;
  private boolean DiscoveryModeGIAC;
  static LocalDevice lDevice;
  static String lDeviceAddress;
  static String lDeviceName;
  DiscoveryAgent lDiscoveryAgent;

  private BTUtil btutil;
  private boolean wipeScreen = false;

  boolean exitConfirmed;

  private boolean BTInquiry;

  public BTDiscovery() {

    display = Display.getDisplay(this);
    infoForm = new Form("Bluetooth Discovery");
    btutil = new BTUtil(infoForm);
    btdevicelist = new BTDeviceList (this, btutil);
    btservicelist = new BTServiceList (this, btutil);
    bthelp = new BTHelp(this, btutil);

    OdeviceInfo = new Command("DeviceInfo", Command.SCREEN, 1);
    OdeviceDiscovery = new Command("DeviceDiscovery", Command.SCREEN, 2);

    infoForm.addCommand(OdeviceInfo);
    infoForm.addCommand(OdeviceDiscovery);

    help = new Command("Help", Command.OK, 1);
    infoForm.addCommand(help);

    exit = new Command("Exit", Command.EXIT, 1);
    infoForm.addCommand(exit);

    backtodevicelist = new Command ("Back", Command.OK,1);

    infoForm.setCommandListener(this);
    display.setCurrent(infoForm);

    // check, if BT is available. if not, exit the application
    // with explanation.
    initLocalDevice();
    if (isBTError()) {
      Alert BTAlert = new Alert("Bluetooth not accessible",
                                getBTErrorMsg(), null, AlertType.ERROR);
      BTAlert.setTimeout(Alert.FOREVER);
      BTAlert.setCommandListener(this);

      display.setCurrent(BTAlert, infoForm);

      safeShutdown();
    }
    else {
      infoForm.append("Please select an option to get more details about the LocalDevice or any RemoteDevice " +
                      "which will be detected by a DeviceInquiry.");
    }
  }

  /**
   * showLocalInfo
   *
   * Display the BT-specific information of the local device.
   *
   */
  public void showLocalInfo() {
    if (getLDevice() != null) {
      btutil.cls();  // clear the FORM screen
      btutil.BTDisplayLocalDevice(this.getLDevice());  // show info of local BT device
      btutil.BTDisplayLocalDeviceProperties(this.getLDevice()); // show properties of local BT device
    }
  }

  /**
   * showRemoteDevices
   *
   * Display the BT specific information of remote devices.
   */
  public void showRemoteDevices() {
    devices.removeAllElements();  // clear the list
    BTStartInquiry();             // inquiry fills the list on the fly
  }

  protected void startApp() {
    exitConfirmed = false;
  }

  protected void pauseApp() {
    // Not supported in Nokia devices
  }

  protected void destroyApp(boolean unconditional) throws
      MIDletStateChangeException {

    // Only check confirmation if this is a conditional
    // shutdown (i.e., not shutdown by pressing the
    // End key.
    if (!unconditional) {
      if (!exitConfirmed) {
        throw new MIDletStateChangeException();
      }
    }
  }

  public void safeShutdown() {
    try {
      destroyApp(false);
      notifyDestroyed();
    }
    catch (MIDletStateChangeException me) {
      display.setCurrent(new ConfirmExitScreen(this));
    }
  }

  public void resume() {
    display.setCurrent(infoForm);
  }

  public void commandAction(Command c, Displayable d) {

    // exit
    if (c == exit) {
      safeShutdown();
    }

    // local device info
    else if (c == OdeviceInfo) {
      showLocalInfo();
    }

    // remote device discovery
    else if (c == OdeviceDiscovery) {
      showRemoteDevices();
    }

    // display the help screen
    else if (c == help) {
      display.setCurrent(bthelp);
    }

    else if (c == backtodevicelist) {
      infoForm.removeCommand(backtodevicelist);
      infoForm.addCommand(help);

      showRemoteDevices();
      //display.setCurrent(btdevicelist);
    }
  }

  /**
   * initLocalDevice
   *
   * Initializes the Bluetooth stack and sets internal variables for further use.
   */
  public void initLocalDevice() {
    try {
      // initialize the JABWT stack
      lDevice = LocalDevice.getLocalDevice();

      // set discovery mode
      if (DiscoveryModeGIAC == true)
        lDevice.setDiscoverable(DiscoveryAgent.GIAC);
      else
        lDevice.setDiscoverable(DiscoveryAgent.LIAC);

        // store the local device address
      lDeviceAddress = lDevice.getBluetoothAddress();

      // store the local device name
      lDeviceName = lDevice.getFriendlyName();

      // store the reference to the discovery agent (singleton)
      lDiscoveryAgent = lDevice.getDiscoveryAgent();
      BTError = false;
    }
    catch (BluetoothStateException e) {
      //e.printStackTrace();
      btutil.log(e.toString());
      lDevice = null;
      BTError = true;
      BTErrorMsg = "Bluetooth enabled?";
    }

  }

  /**
   * startInquiry
   *
   * start a Bluetooth inquiry (GIAC mode) by using a DiscoveryListener class to handle
   * the callbacks.
   */
  public void BTStartInquiry() {

    try {
      BTInquiry = lDiscoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);
      BTError = false;
      if (BTInquiry) {
        btutil.cls();
        btutil.log("Please wait ...");
        wipeScreen = true;
      }
      else {
        BTError = true;
        BTErrorMsg = "Could not start DeviceDiscovery";
      }
    }
    catch (BluetoothStateException e) {
      e.printStackTrace();
      btutil.log(e.toString());
      BTError = true;
      BTErrorMsg = e.toString();
    }
  }

  public void BTDiscoverService(RemoteDevice remote)
   {
     try
     {
       int[] attr = null;    // null means using the default values to look for
       // it is also possible to look for specific, single attributes or an given array

       // now do the search
       // 0x1002 is PublicBrowseRoot, please see Bluetooth Core documentation for further details
       lDiscoveryAgent.searchServices( attr,new UUID[]{ new UUID( 0x1002 )  }, remote, this);
     }
     catch ( BluetoothStateException e )
     {
       //e.printStackTrace();
       btutil.log("Error searching services");
     }

   }

/*
  public void BTDisplayDeviceList() {
    btutil.log("Found Devices :(" + devices.size() + ")");

    for (int i = 0; i < devices.size(); i++) {
      RemoteDevice device = (RemoteDevice) devices.elementAt(i);
      DeviceClass deviceclass = (DeviceClass) deviceClasses.elementAt(i);
      btutil.BTDisplayRemoteDevice(device, deviceclass);
    }
  }
*/

  public boolean isBTError() {
    return BTError;
  }

  public void setBTError(boolean BTError) {
    this.BTError = BTError;
  }

  public String getBTErrorMsg() {
    return BTErrorMsg;
  }

  public void setBTErrorMsg(String BTErrorMsg) {
    this.BTErrorMsg = BTErrorMsg;
  }

  public String getLDeviceName() {
    return lDeviceName;
  }

  public void setLDeviceName(String lDeviceName) {
    this.lDeviceName = lDeviceName;
  }

  public String getLDeviceAddress() {
    return lDeviceAddress;
  }

  public LocalDevice getLDevice() {
    return lDevice;
  }

  public void setLDevice(LocalDevice lDevice) {
    this.lDevice = lDevice;
  }

  public void setLDeviceAddress(String lDeviceAddress) {
    this.lDeviceAddress = lDeviceAddress;
  }

  public int getSelectedDevice() {
    return selectedDevice;
  }

  public void setSelectedDevice(int selectedDevice) {
    this.selectedDevice = selectedDevice;
  }

    /**
     * deviceDiscovered
     *
     * Implements DiscoveryListener interface of JSR-82.
     * deviceDiscovered gets called for each RemoteDevice discovered.
     *
     * @param remoteDevice
     * @param deviceClass
     */
    public void deviceDiscovered(RemoteDevice remoteDevice,
                                 DeviceClass deviceClass) {
      // store the device entry as well as the deviceclass entry for further use
      devices.addElement(remoteDevice);
      deviceClasses.addElement(deviceClass);

      if (wipeScreen) {
        btutil.cls();
        wipeScreen = false;
      }
      btutil.BTDisplayRemoteDevice(remoteDevice, deviceClass);
    }

    /**
     * inquiryCompleted
     *
     * Implements DiscoveryListener interface of JSR-82.
     * inquiryCompleted is called when the process of device discovery is completed.
     *
     * @param BTInquiryDone
     */
    public void inquiryCompleted(int BTInquiryDone) {

      BTInquiry = false; // reset our flag
      switch (BTInquiryDone) {
        case DiscoveryListener.INQUIRY_ERROR:
          btutil.log("Found " + devices.size() + " Elements before error ocurred");
          break;
        case DiscoveryListener.INQUIRY_COMPLETED:
          btutil.log("Inquiry done...");

          if (devices.size() > 0) {
            btdevicelist.displaylist();
            display.setCurrent(btdevicelist);
          }
          else
          {
            btutil.log ("No Devices found");
          }
          break;
        case DiscoveryListener.INQUIRY_TERMINATED:
          btutil.log("Inquiry terminated");
          break;
        case DiscoveryListener.SERVICE_SEARCH_COMPLETED:
          btutil.log("Service search completed");
          break;
        case DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
          btutil.log("Device not reachable");
          break;
        case DiscoveryListener.SERVICE_SEARCH_ERROR:
          btutil.log("Service search error");
          break;
        case DiscoveryListener.SERVICE_SEARCH_NO_RECORDS:
          btutil.log("No devices found");
          break;
      }

    }

    /**
     * servicesDiscovered
     *
     * Implements DiscoveryListener interface of JSR-82.
     * servicesDiscovered gets called as the result of a service search
     * of a RemoteDevice which needs to be previous registered. The information
     * holds details of the services provided by the device.
     *
     * We use this method to store the service records for further use.
     *
     * @param transId
     * @param records
     */
    public void servicesDiscovered(int transId, ServiceRecord[] ServiceRecords) {

      if (wipeScreen) {
        btutil.cls();
        wipeScreen = false;
      }

      for (int i = 0; i < ServiceRecords.length; i++) {
        ServiceRecord record = ServiceRecords[i];
        services.addElement(record);
        btutil.BTDisplayServiceRecord(record);
      }
    }

    /**
     * serviceSearchCompleted
     *
     * Implements DiscoveryListener interface of JSR-82.
     * serviceSearchCompleted is called when the process of service discovery
     * on a single and specific RemoteDevice is completed.
     *
     * @param transId
     * @param complete
     */
    public void serviceSearchCompleted(int transId, int BTServiceDiscoveryResult) {

      switch (BTServiceDiscoveryResult){

        case SERVICE_SEARCH_COMPLETED:
          btservicelist.displayservices();
          display.setCurrent(btservicelist);
          break;
        case SERVICE_SEARCH_ERROR:
          btutil.log("Error searching for services");
          break;
        case SERVICE_SEARCH_NO_RECORDS:
          btutil.log("No service records found");
          break;
        case SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
          btutil.log("Error device not reachable");
          break;
        case SERVICE_SEARCH_TERMINATED:
          btutil.log("Error terminated");
          break;
      }
    }
}  // of BTDiscovery
