package com.solderbyte.openfit;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import android.annotation.SuppressLint;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

@SuppressLint("LongLogTag")
public class BluetoothLeService extends Service {
    private static final String LOG_TAG = "OpenFit:BluetoothLeService";

    private static Handler mHandler;
    private static String mDeviceMac;
    private String mBluetoothDeviceAddress;
    public static InputStream mInStream;
    public static OutputStream mOutStream;
    public static CharSequence[] pairedEntries;
    public static CharSequence[] pairedEntryValues;
    public static CharSequence[] scannedEntries;
    public static CharSequence[] scannedEntryValues;
    private static EnableBluetoothThread eBluetooth;
    private static BluetoothGatt mBluetoothGatt;
    private static BluetoothSocket mBluetoothSocket;
    private static BluetoothDevice mBluetoothDevice;
    private static BluetoothAdapter mBluetoothAdapter;
    private static BluetoothManager mBluetoothManager;
    private static Set<BluetoothDevice> pairedDevices;
    private static Set<BluetoothDevice> scannedDevices;
    private static BluetoothServerSocket mBluetoothServerSocket;
    public static BluetoothGattCharacteristic mWriteCharacteristic;

    public int mConnectionState = 0;
    public static boolean isEnabled = false;
    public static boolean isConnected = false;
    public static boolean isScanning = false;
    public static volatile boolean isThreadRunning = false;

    private static onConnectThread onconnect;
    private static ConnectThread connect;

    private static final int STATE_FORCE = 3;
    private static final long SCAN_PERIOD = 5000;
    private static final int STATE_CONNECTED = 2;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_DISCONNECTED = 0;
    public final static String EXTRA_DATA = "EXTRA_DATA";
    public final static String ACTION_DATA_AVAILABLE = "ACTION_DATA_AVAILABLE";
    public final static String ACTION_GATT_CONNECTED = "ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED = "ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED = "ACTION_GATT_SERVICES_DISCOVERED";
    private static final UUID MY_UUID_SECURE = UUID.fromString("9c86c750-870d-11e3-baa7-0800200c9a66");
    public static String[] gattStatus = {"Success", "Failure"};
    public static String[] gattState = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
    public static String[] gattServiceType = {"Primary", "Secondary"};
    //00001801-0000-1000-8000-00805f9b34fb

    // bluetoothle callback
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.d(LOG_TAG, "BluetoothLe onConnectionStateChange: "+status);

            if(newState == BluetoothProfile.STATE_CONNECTED) {
                Log.d(LOG_TAG, "BluetoothLe Connected to GATT: status:"+status+", state: "+gattState[newState]);
                isConnected = true;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(ACTION_GATT_CONNECTED);
                if(mHandler != null) {
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("bluetooth", "isConnected");
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
                // attempts to discover services after successful connection.
                Log.d(LOG_TAG, "Starting discoverServices");
                mBluetoothGatt.discoverServices();
            }
            else if(newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.d(LOG_TAG, "BluetoothLe Disconnected from GATT");
                isConnected = false;
                mConnectionState = STATE_DISCONNECTED;
                broadcastUpdate(ACTION_GATT_DISCONNECTED);
                if(mHandler != null) {
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("bluetooth", "isDisconnected");
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(LOG_TAG, "onServicesDiscovered: "+status);
            if(status == BluetoothGatt.GATT_SUCCESS) {    
                // loops through available GATT Services.
                for(BluetoothGattService gattService : gatt.getServices()) {
                    String uuid = gattService.getUuid().toString();
                    String type = gattServiceType[gattService.getType()];

                    Log.d(LOG_TAG, "onServicesDiscovered type: "+type);
                    Log.d(LOG_TAG, "onServicesDiscovered uuid: "+uuid);
                    //Log.d(LOG_TAG, "onServicesDiscovered: getCharacteristic: "+mWriteCharacteristic);
                    for(BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) {
                        String cUuid = gattCharacteristic.getUuid().toString();
                        int cInstanceId = gattCharacteristic.getInstanceId();
                        int cPermissions = gattCharacteristic.getPermissions();
                        int cProperties = gattCharacteristic.getProperties();
                        byte[] cValue = gattCharacteristic.getValue();
                        int cWriteType = gattCharacteristic.getWriteType();

                        Log.d(LOG_TAG, "onServicesDiscovered cUuid: "+cUuid);
                        Log.d(LOG_TAG, "onServicesDiscovered cInstanceId: "+cInstanceId);
                        Log.d(LOG_TAG, "onServicesDiscovered cPermissions: "+cPermissions);
                        Log.d(LOG_TAG, "onServicesDiscovered cProperties: "+cProperties);
                        Log.d(LOG_TAG, "onServicesDiscovered cValue: "+cValue);
                        Log.d(LOG_TAG, "onServicesDiscovered cWriteType: "+cWriteType);
                    }
                }
                Log.d(LOG_TAG, "BluetoothLe Service discovered: "+status);
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            }
            else {
                Log.d(LOG_TAG, "BluetoothLe onServicesDiscovered received: "+status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if(status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(LOG_TAG, "BluetoothLe onCharacteristicRead received: "+status);
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.d(LOG_TAG, "BluetoothLe onCharacteristicChanged received: "+characteristic);
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    };

    private void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
        Log.d(LOG_TAG, "BluetoothLe broadcastUpdate received: "+characteristic);
        final Intent intent = new Intent(action);
        // for all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if(data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data) {
                stringBuilder.append(String.format("%02X ", byteChar));
            }
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
        }
        sendBroadcast(intent);
    }

    public class LocalBinder extends Binder {
        BluetoothLeService getService() {
            Log.d(LOG_TAG, "getService");
            return BluetoothLeService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(LOG_TAG, "BluetoothLe onBind.");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        stopSelf();
        close();
        Log.d(LOG_TAG, "unbind.");
        mHandler = null;
        return super.onUnbind(intent);
    }

    private final IBinder mBinder = new LocalBinder();

    public boolean initialize() {
        Log.d(LOG_TAG, "BLE Initialize.");
        if(mBluetoothManager == null) {
            Log.d(LOG_TAG, "Initialize BluetoothManager.");
            mBluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
            if(mBluetoothManager == null) {
                Log.e(LOG_TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }
        scannedDevices = new LinkedHashSet<BluetoothDevice>();
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if(mBluetoothAdapter == null) {
            Log.e(LOG_TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }
        if(mBluetoothAdapter.isEnabled()) {
            isEnabled = true;
        }
        else {
            Log.d(LOG_TAG, "Bluetooth is not enabled.");
            isEnabled = false;
        }
        return true;
    }

    public boolean connect(final String address) {
        Log.d(LOG_TAG, "BLE connect: "+address);
        if(mBluetoothAdapter == null || address == null) {
            Log.d(LOG_TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        if(mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
            Log.d(LOG_TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if(mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                forceConnect();
                return true;
            }
            else {
                return false;
            }
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.d(LOG_TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // auto connect to the device
        mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
        Log.d(LOG_TAG, "Trying to create a new connection to: "+address);
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    public void disconnect() {
        Log.d(LOG_TAG, "BLE disconnect");
        if(mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.d(LOG_TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }

    public void forceConnect() {
        if(mBluetoothDeviceAddress != null) {
            Log.d(LOG_TAG, "Trying to force connection: "+mBluetoothDeviceAddress);
            mConnectionState = STATE_FORCE;
            mBluetoothGatt.disconnect();
            final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mBluetoothDeviceAddress);
            mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
        }
        else {
            Log.d(LOG_TAG, "Force connect called without previous connection");
        }
    }

    public void close() {
        Log.d(LOG_TAG, "BLE close");
        if(mBluetoothGatt != null) {
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        /*if(onconnect != null) {
            onconnect.close();
            onconnect = null;
        }*/
        if(connect != null) {
            connect.close();
            connect = null;
        }
    }

    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if(mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(LOG_TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
        if(mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(LOG_TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    }

    public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
        if(mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(LOG_TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

    public List<BluetoothGattService> getSupportedGattServices() {
        if(mBluetoothGatt == null) {
            Log.w(LOG_TAG, "getSupportedGattServices mBluetoothGatt not initialized");
            return null;
        }
        Log.d(LOG_TAG, "getSupportedGattServices");
        return mBluetoothGatt.getServices();
    }

    /* Helper Functions */
    public void setHandler(Handler mHndlr) {
        Log.d(LOG_TAG, "Setting handler");
        mHandler = mHndlr;
    }

    public void connectRfcomm() {
        if(!mBluetoothAdapter.isEnabled()) {
            Log.d(LOG_TAG, "connect called when Bluetooth is not enabled.");
            return;
        }
        if(mBluetoothDevice != null) {
            connect = new ConnectThread();
            connect.start();
            Log.d(LOG_TAG, "Connecting to Rfcomm");
        }
        else {
            Log.d(LOG_TAG, "connectRfcomm called mBluetoothDevice is null");
        }
    }

    public void disconnectRfcomm() {
        if(mBluetoothDevice != null && isConnected) {
            connect.close();
            Log.d(LOG_TAG, "closing connectRmcomm");
        }
        else {
            Log.d(LOG_TAG, "disconnectRfcomm called while not connected");
        }
    }

    public void enableBluetooth() {
        if(!mBluetoothAdapter.isEnabled()) {
            eBluetooth = new EnableBluetoothThread();
            eBluetooth.start();
        }
        else {
            Log.d(LOG_TAG, "enableBluetooth called when BT enabled");
        }
    }

    public void disableBluetooth() {
        mBluetoothAdapter.disable();
        isEnabled = false;
    }

    public void setDevice(String devMac) {
        Log.d(LOG_TAG, "setDevice: " + devMac);
        mDeviceMac = devMac;
        setBluetoothDevice();
    }

    public static void setBluetoothDevice() {
        Log.d(LOG_TAG, "setBluetoothDevice: " + mDeviceMac);
        // loop through devices
        if(pairedDevices != null) {
            Log.d(LOG_TAG, "setting from paired devices");
            for(BluetoothDevice device : pairedDevices) {
                if(device.getAddress().equals(mDeviceMac)) {
                    Log.d(LOG_TAG, "Set paired device: "+device.getName()+":"+device.getAddress());
                    mBluetoothDevice = device;
                }
            }
        }
        if(scannedDevices.size() > 0) {
            Log.d(LOG_TAG, "setting from scanned devices");
            for(BluetoothDevice device : scannedDevices) {
                if(device.getAddress().equals(mDeviceMac)) {
                    Log.d(LOG_TAG, "Set scanned device: "+device.getName()+":"+device.getAddress());
                    mBluetoothDevice = device;
                }
            }
        }

        if(pairedDevices == null && scannedDevices.size() <= 0) {
            Log.d(LOG_TAG, "setBluetoothDevice called with empty no paired or scanned devices");
        }
    }

    public void setEntries() {
        Log.d(LOG_TAG, "setEntries");
        if(isEnabled) {
            List<CharSequence> entries = new ArrayList<CharSequence>();
            List<CharSequence> values = new ArrayList<CharSequence>();
            pairedDevices = mBluetoothAdapter.getBondedDevices();
            // loop through paired devices
            if(pairedDevices.size() > 0) {
                for(BluetoothDevice device : pairedDevices) {
                    String deviceName = device.getName();
                    String deviceAddr = device.getAddress();
                    Log.d(LOG_TAG, "Paired Device: "+deviceName+":"+deviceAddr);
                    if(deviceName != null && !deviceName.isEmpty() && deviceAddr != null && !deviceAddr.isEmpty()) {
                        entries.add(deviceName);
                        values.add(deviceAddr);
                    }
                }
            }
            else {
                Log.d(LOG_TAG, "No pairedDevices");
            }
            // loop trough scanned devices
            if(scannedDevices.size() > 0) {
                for(BluetoothDevice device : scannedDevices) {
                    // make sure we dont add duplicates
                    if(!entries.contains(device.getName())) {
                        String deviceName = device.getName();
                        String deviceAddr = device.getAddress();
                        Log.d(LOG_TAG, "Scanned Device: "+deviceName+":"+deviceAddr);
                        if(deviceName != null && !deviceName.isEmpty() && deviceAddr != null && !deviceAddr.isEmpty()) {
                            entries.add(deviceName);
                            values.add(deviceAddr);
                        }
                    }
                }
            }
            else {
                Log.d(LOG_TAG, "No scannedDevices");
            }
            
            pairedEntries = entries.toArray(new CharSequence[entries.size()]);
            pairedEntryValues = values.toArray(new CharSequence[values.size()]);

            Message msg = mHandler.obtainMessage();
            Bundle b = new Bundle();
            b.putString("bluetoothDevicesList", "bluetoothDevicesList");
            b.putCharSequenceArray("bluetoothEntries", pairedEntries);
            b.putCharSequenceArray("bluetoothEntryValues", pairedEntryValues);
            msg.setData(b);
            mHandler.sendMessage(msg);
        }
        else {
            Log.d(LOG_TAG, "setEntries called without BT enabled");
        }
    }

    public static CharSequence[] getEntries() {
        if(pairedEntries != null && pairedEntries.length > 0) {
            return pairedEntries;
        }
        else {
            CharSequence[] entries = {"No Devices"};
            return entries;
        }
    }

    public static CharSequence[] getEntryValues() {
        if(pairedEntryValues!= null && pairedEntryValues.length > 0) {
            return pairedEntryValues;
        }
        else {
            CharSequence[] entryValues = {"None"};
            return entryValues;
        }
    }

    public void scanLeDevice() {
        Log.d(LOG_TAG, "scanLeDevice");
        if(isEnabled) {
            if(mHandler != null) {
                if(!isScanning) {
                    /*if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                        Log.d(LOG_TAG, "scanning with startLeScan");
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                isScanning = false;
                                mBluetoothAdapter.stopLeScan(mLeScanCallback);
                                Message msg = mHandler.obtainMessage();
                                Bundle b = new Bundle();
                                b.putString("bluetooth", "scanStopped");
                                msg.setData(b);
                                mHandler.sendMessage(msg);
                                setEntries();
                            }
                        }, SCAN_PERIOD);
                        isScanning = true;
                        mBluetoothAdapter.startLeScan(mLeScanCallback);
                    }
                    
                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        Log.d(LOG_TAG, "scanning with startScan"+ mBluetoothAdapter);
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                isScanning = false;
                                BluetoothLeScanner mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
                                mBluetoothLeScanner.stopScan(mScanCallback);
                                Message msg = mHandler.obtainMessage();
                                Bundle b = new Bundle();
                                b.putString("bluetooth", "scanStopped");
                                msg.setData(b);
                                mHandler.sendMessage(msg);
                                setEntries();
                            }
                        }, SCAN_PERIOD);
                        
                        isScanning = true;
                        BluetoothLeScanner mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
                        mBluetoothLeScanner.startScan(mScanCallback);

                    }*/

                    // Stops scanning after a pre-defined scan period.
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            isScanning = false;
                            mBluetoothAdapter.cancelDiscovery();
                            Message msg = mHandler.obtainMessage();
                            Bundle b = new Bundle();
                            b.putString("bluetooth", "scanStopped");
                            msg.setData(b);
                            mHandler.sendMessage(msg);
                            setEntries();
                        }
                    }, SCAN_PERIOD);
                    
                    Log.d(LOG_TAG, "scanLeDevice starting scan for: "+SCAN_PERIOD+"ms");
                    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 
                    registerReceiver(mScanReceiver, filter);
                    Log.d(LOG_TAG, "starting discovery");
                    isScanning = true;
                    mBluetoothAdapter.startDiscovery();
                }
                else {
                    Log.d(LOG_TAG, "scanLeDevice currently scanning");
                }
            }
            else{
                Log.d(LOG_TAG, "scanLeDevice no mHandler");
            }
        }
        else {
            Log.d(LOG_TAG, "scanLeDevice called without BT enabled");
        }
    }

    public void write(byte[] bytes) {
        if(onconnect != null) {
            Log.d(LOG_TAG, "Writting bytes");
            onconnect.write(bytes);
        }
        else {
            Log.d(LOG_TAG, "write called without BT connected");
        }
    }

    /*private LeScanCallback mLeScanCallback = new LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            if(scannedDevices.add(device)) {
                Log.d(LOG_TAG, device.getName()+" : "+device.getAddress()+" : "+device.getType()+" : "+device.getBondState());
                Message msg = mHandler.obtainMessage();
                Bundle b = new Bundle();
                b.putString("bluetoothDevice", device.getName()+","+device.getAddress());
                msg.setData(b);
                mHandler.sendMessage(msg);
                setEntries();
            }
        }
    };

    @SuppressLint("NewApi")
    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            BluetoothDevice device = result.getDevice();
            if(scannedDevices.add(device)) {
                Log.d(LOG_TAG, device.getName()+" : "+device.getAddress()+" : "+device.getType()+" : "+device.getBondState());
                Message msg = mHandler.obtainMessage();
                Bundle b = new Bundle();
                b.putString("bluetoothDevice", device.getName()+","+device.getAddress());
                msg.setData(b);
                mHandler.sendMessage(msg);
                setEntries();
            }
        }
    };*/

    private final BroadcastReceiver mScanReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if(scannedDevices.add(device)) {
                    Log.d(LOG_TAG, device.getName()+" : "+device.getAddress()+" : "+device.getType()+" : "+device.getBondState());
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("bluetoothDevice", device.getName()+","+device.getAddress());
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                    setEntries();
                }
            }
        }
    };

    private class EnableBluetoothThread extends Thread {
        public void run() {
            boolean bluetoothEnabled = true;
            long timeStart = Calendar.getInstance().getTimeInMillis();
            Log.d(LOG_TAG, "Enabling Bluetooth: "+timeStart);

            mBluetoothAdapter.enable();
            while(!mBluetoothAdapter.isEnabled()) {
                try
                {
                    long timeDiff =  Calendar.getInstance().getTimeInMillis() - timeStart;
                    if(timeDiff >= 5000) {
                        bluetoothEnabled = false;
                        break;
                    }
                    Thread.sleep(100L);
                }
                catch (InterruptedException ie)
                {
                    // unexpected interruption while enabling bluetooth
                    Thread.currentThread().interrupt(); // restore interrupted flag
                    return;
                }
            }
            if(bluetoothEnabled) {
                isEnabled = true;
                Message msg = mHandler.obtainMessage();
                Bundle b = new Bundle();
                b.putString("bluetooth", "isEnabled");
                msg.setData(b);
                mHandler.sendMessage(msg);
                Log.d(LOG_TAG, "Enabled");
            }
            else {
                Message msg = mHandler.obtainMessage();
                Bundle b = new Bundle();
                b.putString("bluetooth", "isEnabledFailed");
                msg.setData(b);
                mHandler.sendMessage(msg);
                Log.d(LOG_TAG, "Enabling Bluetooth timed out");
            }
        }
    }

    private class ConnectThread extends Thread {
        public ConnectThread() {
            Log.d(LOG_TAG, "Initializing ConnectThread");

            // get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                Log.d(LOG_TAG, "try ConnectThread: "+mBluetoothDevice.getName()+" with UUID: "+MY_UUID_SECURE.toString());
                mBluetoothSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
            }
            catch(Exception e) {
                Log.e(LOG_TAG, "Error: mBluetoothDevice.createRfcommSocketToServiceRecord", e);
            }
        }

        public void run() {
            Log.d(LOG_TAG, "Running ConnectThread");
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();

            try {
                // connect the device through the socket. This will block until it succeeds or throws an exception
                mBluetoothSocket.connect();
                isConnected = true;
                if(mHandler != null) {
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("bluetooth", "isConnectedRfcomm");
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
            }
            catch(IOException connectException) {
                Log.e(LOG_TAG, "Error: mBluetoothSocket.connect()", connectException);
                try {
                    mBluetoothSocket.close();
                    if(mHandler != null) {
                        Message msg = mHandler.obtainMessage();
                        Bundle b = new Bundle();
                        b.putString("bluetooth", "isConnectedRfcommFailed");
                        msg.setData(b);
                        mHandler.sendMessage(msg);
                    }
                }
                catch (IOException closeException) {
                    Log.e(LOG_TAG, "Error: mBluetoothSocket.close()", closeException);
                }
                return;
            }
            Log.d(LOG_TAG, "ConnectThread connected");
            // Do work to manage the connection
            onconnect = new onConnectThread();
            onconnect.start();
        }

        public void close() {
            if(onconnect != null) {
                isThreadRunning = false;
                onconnect.close();
                isConnected = false;
                Log.d(LOG_TAG, "Bluetooth rfComm Disconnected");
                if(mHandler != null) {
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("bluetooth", "isDisconnectedRfComm");
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
            }
        }
    }

    public class onConnectThread extends Thread {
        public onConnectThread() {
            Log.d(LOG_TAG, "Initializing onConnectThread");

            // get the input and output streams
            try {
                mInStream = mBluetoothSocket.getInputStream();
                mOutStream = mBluetoothSocket.getOutputStream();
                isThreadRunning = true;
            }
            catch(IOException e) {
                close();
                Log.e(LOG_TAG, "Error: mBluetoothSocket.getInputStream()/socket.getOutputStream()", e);
            }
        }

        public void run() {
            Log.d(LOG_TAG, "Running onConnectThread");
            ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];

            // listen to the InputStream
            while(isThreadRunning) {
                try {
                    int bytes = mInStream.read(buffer);
                    byteArray.write(buffer, 0, bytes);
                    Log.d(LOG_TAG, "Received: "+byteArray);
                    try {
                        Message msg = mHandler.obtainMessage();
                        Bundle b = new Bundle();
                        b.putString("bluetoothData", "bluetoothData");
                        b.putByteArray("data", byteArray.toByteArray());
                        msg.setData(b);
                        mHandler.sendMessage(msg);
                    }
                    catch(Exception e) {
                        Log.e(LOG_TAG, "Error: mHandler.obtainMessage()", e);
                    }
                    
                    byteArray.reset();
                }
                catch (IOException e) {
                    if(isThreadRunning) {
                        Log.e(LOG_TAG, "Error: mInStream.read()", e);
                        close();
                        onconnect.close();
                        if(connect != null) {
                            connect.close();
                            connect = null;
                        }
                    }
                }
            }
        }

        public void write(byte[] bytes) {
            try {
                ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
                byteArray.write(bytes, 0, bytes.length);
                Log.d(LOG_TAG, "Sending: "+byteArray);
                mOutStream.write(bytes);
                mOutStream.flush();
            }
            catch(IOException e) {
                Log.e(LOG_TAG, "Error: mOutStream.write()", e);
            }
        }

        public void close() {
            try {
                mInStream.close();
            }
            catch(IOException e) {
                Log.e(LOG_TAG, "Error: mInStream.close()", e);
            }
            try {
                mOutStream.close();
            }
            catch(IOException e) {
                Log.e(LOG_TAG, "Error: mOutStream.close()", e);
            }
            try {
                mBluetoothSocket.close();
            }
            catch(IOException e) {
                Log.e(LOG_TAG, "Error: mBluetoothSocket.close()", e);
            }
        }
    }

    @SuppressWarnings("unused")
    private class ServerThread extends Thread {
        public ServerThread() {
            Log.d(LOG_TAG, "Initializing ServerThread");

            try {
                Log.d(LOG_TAG, "try ServerThread with UUID: "+MY_UUID_SECURE);
                mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("SessionManagerSecure", MY_UUID_SECURE);
            } catch (IOException e1) {
                Log.e(LOG_TAG, "Error listenUsingRfcommWithServiceRecord");
                e1.printStackTrace();
            }
        }

        public void run() {
            Log.d(LOG_TAG, "Running ServerThread");

            try {
                mBluetoothServerSocket.accept();
                Log.d(LOG_TAG, "mBluetoothServerSocket.accept() success");
            } catch (IOException e1) {
                Log.e(LOG_TAG, "Error mBluetoothServerSocket.accept()");
                e1.printStackTrace();
            }
        }

        public void close() {
            try {
                mBluetoothServerSocket.close();
            }
            catch (IOException e) {
                Log.e(LOG_TAG, "Error: mmSocket.close()", e);
            }
        }
    }
}