package net.benhui.btgallery.obex_gui;

import java.io.*;
import java.util.*;

import javax.bluetooth.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

import net.benhui.btgallery.bluelet.*;
import net.benhui.btgallery.Util;

/**
 *
 * Title: Example OBEX MIDlet that doesn't need javax.obex
 * This OBEX MIDlet demostrate how to access OBEX service without using
 * javax.obex package. Many JSR-82 devices out there doesn't actually support
 * OBEX profile, but many people are interested in finding out how to
 * get around it. This example show that you can get around by using RFCOMM
 * connection to connect to a OBEX service and send OBEX bytes directly over.
 * Notice, this is not an implementation of OBEX and this example is VERY
 * simple (just send the first OBEX packet). If you want OBEX support, you may
 * need to implements OBEX stack yourself.
 *
 * @author Ben Hui (www.benhui.net)
 * @version 1.0
 *
 * LICENSE:
 * This code is licensed under GPL. (See http://www.gnu.org/copyleft/gpl.html)
 */

public class OBEX_MIDlet extends MIDlet implements CommandListener
{
  // self singleton instance
  public static OBEX_MIDlet instance = null;
  // reference to display
  public static Display display = null;
  // a text screen to display log
  public OBEX_Screen screen;

  // Bluelet to perform device discovery and service discovery
  BLUElet bluelet = null;

  public OBEX_MIDlet()
  {
    instance = this;
    bluelet = new BLUElet( this, this );
  }
  protected void startApp() throws javax.microedition.midlet.MIDletStateChangeException
  {
    display = Display.getDisplay(this);
    screen = new OBEX_Screen();

    bluelet.startApp();

    display.setCurrent( screen );
  }
  protected void pauseApp()
  {
    bluelet.pauseApp();
  }
  protected void destroyApp(boolean unconditional) throws javax.microedition.midlet.MIDletStateChangeException
  {
    bluelet.destroyApp( unconditional );
  }

  public void commandAction(Command c, Displayable d)
  {
    if ( d == screen && c.getLabel().equals("Search") )
    {
      //
      // we start device discovery and service discovery
      // in service discovery, we search for OBEX Push Profile 0x1106
      //
      bluelet.startInquiry( DiscoveryAgent.GIAC, new UUID[]{  new UUID( 0x1106 ) }); // OBEX PUSH PROFILE 0x1106
      display.setCurrent( bluelet.getUI() );

    } else if ( c.equals( BLUElet.COMPLETED ) )
    {
      display.setCurrent( screen );

      //
      // This is the main logic
      // 1. Obtain URL to remote obex service
      //    this URL is in form of btgoep://.....
      // 2. Find the RFCOMM channel id that this service is bind to
      // 3. Construct a Serial Port Profile based URL using the channel id
      //    this URL is in the form of btspp://......
      // 4. Connect to the spp URL, send a OBEX header bytes over
      // 5. Read in some bytes from input stream
      ServiceRecord r = bluelet.getSelectedService();
      RemoteDevice remote = bluelet.getSelectedDevice();
      if ( r == null )
      {
        log("No Profile Found");
        return;
      }

      String url  = r.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false );
      log("OBEX url:"+url);


      // finding channel id is an important steps
      // because no device support javax.obex package, so we need to connect using
      // serial port profile (btspp). To connect using btspp, we need to know the channel id
      // of this obex service
      int channel = findChannelId( r );
      log("channel id:"+channel);

      String url2 = "btspp://"+remote.getBluetoothAddress()+":"+channel+";master=false;encrypt=false;authenticate=false";
      log("SPP url:"+url2);

      try
      {
        // obtain connection and stream to this service
        StreamConnection con = (StreamConnection) Connector.open( url2 );
        DataOutputStream out = con.openDataOutputStream();

        log("open output stream ok");

        // this bytes values are taken from OBEX Specification 1.3 example
        byte[] data = new byte[]{ (byte)0x80, 0x00, 0x07, 0x10, 0x00, 0x20, 0x00};

        // write data into serial stream
        out.write( data );
        out.flush();

        log("write data and flush ok");

        DataInputStream in = con.openDataInputStream();

        log("open input stream ok");

        byte[] indata = new byte[1000];
        int read = in.read( indata );

        log("read in "+read+" bytes from OBEX service");

        // print the read in value in HEX format
        byte[] data2 = new byte[read];
        System.arraycopy(indata, 0, data2, 0, read );
        String hex = Util.toHexString( data2 );
        for ( int i=0; i< read; i++)
        {
          log(hex);
        }

        // finish, close connection
        con.close();

      } catch (Exception e)
      {
        e.printStackTrace();
      }

    }
  }

  /**
   * Find channel id of this service record.
   * channel id is store under ProtocolDescriptorList (0x0004) attribute,
   * then under RFCOMM protocol (0x0003) data element.
   * @param r ServiceRecord
   * @return int
   */
  public int findChannelId(ServiceRecord r)
  {
    // e1 is ProtocolDescriptorList
    DataElement e1 = r.getAttributeValue( 0x0004 );
    for ( Enumeration enum1 = (Enumeration)e1.getValue(); enum1.hasMoreElements(); )
    {
      // e2 is one ProtocolDescriptor
      DataElement e2 = (DataElement)enum1.nextElement();
      Enumeration enum2 = (Enumeration)e2.getValue();

      // e3 is RFCOMM (0x0003)
      DataElement e3 = (DataElement)enum2.nextElement();
      if ( e3.getValue().equals( new UUID(0x0003) ) )
      {
        // e4 is channel id
        DataElement e4 = (DataElement)enum2.nextElement();
        int id = (int)e4.getLong();
        return id;
      }
    }
    return -1;
  }

  public void log(String m)
  {
    System.out.println(m);
    screen.add(m);
  }

}
