package com.example.imlinkclient.helpr;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.util.Log;

import com.example.imlinkclient.MainClientActivity;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;

import static com.example.imlinkclient.Constants.UUID_ACCEPT;
import static com.example.imlinkclient.Constants.UUID_CONNECT;

/**
 * 蓝牙连接Helper
 * 注意：车机的蓝牙连接分两步，
 * 第一步是，车盒端主动发起连接，这个时候车机会在连接完成后断开连接
 * 第二步时，提前开启一个蓝牙的accept监听，车机在主动断开连接后，会主动发起对车盒的连接。
 * 参考： https://github.com/googlearchive/android-BluetoothChat
 *
 */
public class BluetoothHelper {
    private static final String TAG = "BluetoothHelper";

    private int mCurrent_State;
    public static final int STATE_NONE = 0;       // we're doing nothing
    public static final int STATE_LISTEN = 1;     // now listening for incoming connections
    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
    public static final int STATE_CONNECTED = 3;  // now connected to a remote device

    // Name for the SDP record when creating server socket
    private static final String NAME_SECURE = "BluetoothChatSecure";

    private final BluetoothAdapter mAdapter;

    private CommunicateThread mCommunicateThread;
    private AcceptThread mAcceptThread;
    private ConnectThread mConnectThread;
    private final MainClientActivity.MainHandler mMainHandler;
    private BluetoothDevice mDevice;

    public BluetoothHelper(MainClientActivity.MainHandler mainHandler) {
        mMainHandler = mainHandler;
        mAdapter = BluetoothAdapter.getDefaultAdapter();

    }

    // 主动发起连接
    public void startConnectThread(BluetoothDevice device) {
        mDevice = device;
        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
    }

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mDevice;

        public ConnectThread(BluetoothDevice device) {
            mDevice = device;
            BluetoothSocket tmp = null;
            // Get a BluetoothSocket for a connection with the
            // given BluetoothDevice
            try {
                /*** 第一步 主动发起连接**/
                tmp = device.createInsecureRfcommSocketToServiceRecord(UUID_CONNECT);
                Log.e("MainClientActivity", " createInsecureRfcommSocketToServiceRecord");

            } catch (IOException e) {
                Log.e(TAG, "ConnectThread 连接失败： createRfcommSocketToServiceRecord 等待车机回连");
                cancel();
            } catch (Exception e) {
                Log.e("MainClientActivity", " exception : " + e.getMessage());

            }
            mmSocket = tmp;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectThread ");
            setName("ConnectThread");

            // Always cancel discovery because it will slow down a connection
            mAdapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                mmSocket.connect();
            } catch (IOException e) {
                // Close the socket
                Log.e(TAG, "ConnectThread 连接失败： mmSocket.connect();");

                try {
                    mmSocket.close();
                } catch (IOException e2) {
                    Log.e(TAG, "ConnectThread 连接失败： mmSocket.close();");
                }
                return;
            }
            Log.e(TAG, "ConnectThread 连接成功");

            InputStream inputStream = null;
            try {
                inputStream = mmSocket.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "ConnectThread 连接断开： mmSocket.getInputStream();");
                cancel();
            }

            byte[] buffer = new byte[1024];
            while (true) {
                try {
                    // Read from the InputStream
                   inputStream.read(buffer);
                    Log.d(TAG, " ConnectThread 读取数据 : " + Arrays.toString(buffer));
                } catch (IOException e) {
                    Log.e(TAG, "ConnectThread 连接断开： inputStream.read(buffer);");
                    cancel();
                    break;
                }
            }

        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }


    /** 被动连接 线程**/
    public void startAcceptThread() {
        mAcceptThread = new AcceptThread();
        mAcceptThread.start();
    }
    private class AcceptThread extends Thread {
        // The local server socket
        private final BluetoothServerSocket mmServerSocket;
        public AcceptThread() {
            BluetoothServerSocket tmp = null;
            // Create a new listening server socket
            try {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
                            UUID_ACCEPT);
            } catch (IOException e) {
                Log.e(TAG, "AcceptThread 连接失败： listenUsingRfcommWithServiceRecord" + e.getMessage());
            }
            mmServerSocket = tmp;

        }

        public void run() {

            setName("AcceptThread");
            BluetoothSocket socket = null;
            while (true) {
                try {
                    /*** 第二步 等待车机主动发起连接 **/
                    socket = mmServerSocket.accept();
                    Log.e(TAG, "AcceptThread 监听连接  accept");
                    if (socket != null) {
                        connected(socket, socket.getRemoteDevice());
                        Log.e(TAG, "AcceptThread 连接成功");
                        break;
                    }
                } catch (IOException e) {
                    Log.e(TAG, "AcceptThread 连接失败 accept() failed" + e.getMessage());
                    cancel();
                    sendBTDisconnect();
                    break;
                }
            }
            Log.i(TAG, "END mAcceptThread");
        }

        public void cancel() {
            Log.d(TAG,  "cancel " + this);
            try {
                mmServerSocket.close();
            } catch (IOException e) {
                Log.e(TAG,  "close() of server failed", e);
            }
        }
    }


    private synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
        Log.d(TAG, "connected");

        // Cancel the thread that completed the connection
        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }
        // Start the thread to manage the connection and perform transmissions
        mCommunicateThread = new CommunicateThread(socket);
        mCommunicateThread.start();
        sendBTConnected(device);
    }

    public void write(byte[] out) {
        // Create temporary object
        CommunicateThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mCurrent_State != STATE_CONNECTED) return;
            r = mCommunicateThread;
        }
        // Perform the write unsynchronized
        r.write(out);
    }

    /**
     * This thread runs during a connection with a remote device.
     * It handles all incoming and outgoing transmissions.
     */
    private class CommunicateThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public CommunicateThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "CommunicateThread 连接失败  temp sockets not created", e);
                sendBTDisconnect();
                cancel();
            }
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            byte[] buffer = new byte[1024];
            int length;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // Read from the InputStream
                    length = mmInStream.read(buffer);
                    readData(length, buffer);
                    Log.d(TAG, " read : " + Arrays.toString(buffer));
                } catch (IOException e) {
                    cancel();
                    sendBTDisconnect();
                    Log.d(TAG, "CommunicateThread 连接断开  mmInStream.read(buffer)", e);
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         *
         * @param buffer The bytes to write
         */
        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
                Log.e(TAG, "CommunicateThread 写入数据：" + Arrays.toString(buffer));
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }

    public void stopAll() {
        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }

        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        if (mCommunicateThread != null) {
            mCommunicateThread.cancel();
            mCommunicateThread = null;
        }
    }

    //得到配对的设备列表，清除已配对的设备
    public void removePairDevice(){
        if(mAdapter!=null){
            Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
            for(BluetoothDevice device : bondedDevices ){
                unpairDevice(device);
            }
        }

    }

    public void unpairDevice() {
        if (mDevice != null) {
            unpairDevice(mDevice);
            Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
            for (BluetoothDevice device :
                    bondedDevices) {

            }
        }
    }

    //反射来调用BluetoothDevice.removeBond取消设备的配对
    public void unpairDevice(BluetoothDevice device) {
        try {
            Method m = device.getClass()
                    .getMethod("removeBond", (Class[]) null);
            m.invoke(device, (Object[]) null);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }


    /** handle 命令抽取 start**/
    private void sendBTConnected(BluetoothDevice device) {
        mCurrent_State = STATE_CONNECTED;
        mMainHandler.obtainMessage(MainClientActivity.MainHandler.MSG_BT_STATUS, STATE_CONNECTED, -1, device.getAddress())
                .sendToTarget();
    }

    private void sendBTDisconnect() {
        mCurrent_State = STATE_NONE;
        mMainHandler.obtainMessage(MainClientActivity.MainHandler.MSG_BT_STATUS, STATE_NONE, -1)
                .sendToTarget();
    }

    // 从车机端读取信息
    private void readData(int length, byte[] buffer) {
        mMainHandler.obtainMessage(MainClientActivity.MainHandler.MSG_BT_DATA_R, length, -1, buffer)
                .sendToTarget();
    }

    /**
     * handle 命令抽取 end
     **/

    public int getState() {
        return mCurrent_State;
    }
}
