package com.zhou.android.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Handler;
import android.os.Message;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

/**
 * Bluetooth 蓝牙通信服务
 * Created by ZhOu on 2017/6/14.
 */

public class BluetoothChatService {

    private UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    public final static int StateNone = 0;
    public final static int StateListen = 1;
    public final static int StateConnecting = 2;
    public final static int StateConnected = 3;

    public String deviceName = "";

    private final BluetoothAdapter bluetoothAdapter;
    private Handler handler;
    private AcceptThread acceptThread;
    private ConnectThread connectThread;
    private ConnectedThread connectedThread;

    private int state = StateNone;

    public BluetoothChatService(Context context, Handler handler) {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        this.handler = handler;
    }

    class AcceptThread extends Thread {

        private BluetoothServerSocket bluetoothServerSocket;

        public AcceptThread() {

            try {
                bluetoothServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord("BluetoothChat", uuid);
            } catch (IOException e) {
//                e.printStackTrace();
                bluetoothServerSocket = null;
            }
        }

        @Override
        public void run() {
            BluetoothSocket bluetoothSocket;
            while (state != StateConnected) {
                try {
                    bluetoothSocket = bluetoothServerSocket.accept();
                } catch (Exception e) {
//                    e.printStackTrace();
                    break;
                }
                if (bluetoothSocket != null) {
                    synchronized (BluetoothChatService.this) {
                        switch (state) {
                            case StateNone:
                            case StateListen:
                            case StateConnecting:
                                connected(bluetoothSocket, bluetoothSocket.getRemoteDevice());
                                break;
                            case StateConnected:
                                try {
                                    bluetoothSocket.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                break;
                        }
                    }
                }
            }
        }

        public void cancel() {
            try {
                bluetoothServerSocket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class ConnectThread extends Thread {

        private BluetoothSocket bluetoothSocket;
        private BluetoothDevice bluetoothDevice;

        public ConnectThread(BluetoothDevice device) {
            bluetoothDevice = device;
            BluetoothSocket tmp;
            try {
                tmp = device.createRfcommSocketToServiceRecord(uuid);
            } catch (IOException e) {
//                e.printStackTrace();
                tmp = null;
            }
            bluetoothSocket = tmp;
        }

        @Override
        public void run() {
            bluetoothAdapter.cancelDiscovery();
            try {
                bluetoothSocket.connect();
            } catch (IOException e) {
//                e.printStackTrace();
                try {
                    bluetoothSocket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                    //连接失败
                    connectionFailed();
                    return;
                }
            }
            synchronized (BluetoothChatService.this) {
                connectThread = null;
            }
            //连接
            connected(bluetoothSocket, bluetoothDevice);
        }


        public void cancel() {
            try {
                bluetoothSocket.close();
            } catch (Exception e) {
//                e.printStackTrace();
            }
        }
    }

    class ConnectedThread extends Thread {
        private BluetoothSocket bluetoothSocket;
        //        private BluetoothDevice bluetoothDevice;
        private InputStream inputStream;
        private OutputStream outputStream;

        public ConnectedThread(BluetoothSocket bluetoothSocket) {
            this.bluetoothSocket = bluetoothSocket;
            try {
                inputStream = bluetoothSocket.getInputStream();
                outputStream = bluetoothSocket.getOutputStream();
            } catch (IOException e) {
//                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int len;
            while (true) {
                if (inputStream == null)
                    break;
                try {
                    len = inputStream.read(buffer);
                    if (len != -1) {
                        String v = new String(buffer, 0, len);
                        handler.obtainMessage(MessageType.MESSAGE_READ, v).sendToTarget();
                    }
                } catch (IOException e) {
//                    e.printStackTrace();
                    connectionLost();
                    BluetoothChatService.this.start();
                    break;
                } catch (NullPointerException e) {
//                    e.printStackTrace();
                    break;
                }
            }
        }

        public void write(byte[] buffer) {
            if (buffer == null) {
                return;
            }
            try {
                outputStream.write(buffer);
                handler.obtainMessage(MessageType.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
            } catch (Exception e) {
//                e.printStackTrace();
            }
        }

        public void disconnect() {
            try {
                outputStream.write(MessageType.MESSAGE_DISCONNECT_DEVICE.getBytes());
            } catch (Exception e) {
//                e.printStackTrace();
            }
        }

        public void cancel() {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    inputStream.close();
                }
                bluetoothSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void start() {
        if (connectThread != null) {
            connectThread.cancel();
            connectThread = null;
        }
        if (connectedThread != null) {
            connectedThread.cancel();
            connectedThread = null;
        }

        setState(StateListen);
        if (acceptThread == null) {
            acceptThread = new AcceptThread();
            acceptThread.start();
        }
    }

    public synchronized void connect(BluetoothDevice bluetoothDevice) {

        if (state == StateConnecting) {
            if (connectThread != null) {
                connectThread.cancel();
                connectThread = null;
            }
        }

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

        connectThread = new ConnectThread(bluetoothDevice);
        connectThread.start();

        setState(StateConnecting);
    }

    public synchronized void connected(BluetoothSocket bSocket, BluetoothDevice bDevice) {
        if (acceptThread != null) {
            acceptThread.cancel();
            acceptThread = null;
        }
        if (connectThread != null) {
            connectThread.cancel();
            connectThread = null;
        }

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

        connectedThread = new ConnectedThread(bSocket);
        connectedThread.start();

        handler.obtainMessage(MessageType.MESSAGE_DEVICE, deviceName = bDevice.getName()).sendToTarget();

        setState(StateConnected);
    }

    private void connectionFailed() {
        Message msg = handler.obtainMessage(MessageType.MESSAGE_FAILED);
        msg.obj = "Unable to connect device.";
        handler.sendMessage(msg);
        BluetoothChatService.this.start();
    }

    private void connectionLost() {
        Message msg = handler.obtainMessage(MessageType.MESSAGE_LOST);
        msg.obj = "Device connection was lost.";
        handler.sendMessage(msg);
        BluetoothChatService.this.start();
    }

    public synchronized void stop() {

        if (connectThread != null) {
            connectThread.cancel();
            connectThread = null;
        }
        if (connectedThread != null) {
            connectedThread.cancel();
            connectedThread = null;
        }
        if (acceptThread != null) {
            acceptThread.cancel();
            acceptThread = null;
        }
        setState(StateNone);
    }

    /**
     * 主动关闭,发送关闭消息通知对方
     */
    public void initiativeStop() {
        disconnect();
        stop();
    }

    public void write(byte[] buffer) {
        ConnectedThread t;
        synchronized (BluetoothChatService.this) {
            if (state != StateConnected) return;
            t = connectedThread;
        }
        if (t != null)
            t.write(buffer);
    }

    private void disconnect() {
        ConnectedThread t;
        synchronized (BluetoothChatService.this) {
            if (state != StateConnected) return;
            t = connectedThread;
        }
        if (t != null)
            t.disconnect();
    }

    private void setState(int state) {
        this.state = state;
        handler.obtainMessage(MessageType.MESSAGE_STATE_CHANGE, state).sendToTarget();
    }

    public synchronized int getState() {
        return state;
    }
}