package com.device.comm.mylibrary;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothProfile;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.device.comm.mylibrary.NativeDevice.BleNativeDevice;

import static android.bluetooth.BluetoothDevice.BOND_BONDED;
import static android.bluetooth.BluetoothDevice.BOND_NONE;

/**
 * Created by lvwang2002 on 16/1/23.
 */
public class BleDevice {
    private String TAG = "RN_BLE_DEVICE";
    private String mMacAddress;
    private String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
    private boolean isDiscoveredService = false;
    private Timer mServiceTimer = new Timer();

    public BleDevice() {

    }

    /** 尝试连接结果 */
    public enum ConnectResultState {
        TryConnectingSuccessful, AlreadyConnecting, AlreadyConnected, TryConnectFailed;
    }

    /** 设备当前状态 */
    public enum DeviceConnectState {
        Discovered, Connecting, Connected, Disconnect, ConnectFailed;
    }

    public interface BleDeviceCallback {
         void onConnect(BleDevice device);
         void onDisconnect(BleDevice device);
         void onBonded(BleDevice device);
         void onGetServices(BleDevice device);
         void onConnectError(BleDevice device, String error);
    }

    /** 设置原生设备的回掉,用于传递结果到界面 */
    public void setNativeDeviceCallback(BleNativeDevice.BleNativeDeviceCallback deviceCallback) {
        mNativeDevice.setNativeDeviceCallback(deviceCallback);
    }

    protected DeviceConnectState mDeviceState = DeviceConnectState.Discovered; // 0:被发现未被连接
    // 1:正在连接
    // 2:已经连接上
    // 3.断开连接

    public DeviceConnectState getDeviceState() {
        return mDeviceState;
    }

    private BleDeviceManager mDeviceManager;
    private BleNativeDevice mNativeDevice;

    public  BleDevice(BleDeviceManager deviceManager, String macAddress) {
        synchronized (BleDeviceManager.class) {
            mMacAddress = macAddress;
            mDeviceManager = deviceManager;

            String deviceName = mDeviceManager.getBluetoothAdapter()
                    .getRemoteDevice(mMacAddress).getName();
            if (deviceName == null) {
                mDeviceManager.uhOh(this, "device no name");
                return;
            }
            mNativeDevice = BleNativeDevice.getNativeDevice(deviceName, this,
                    mDeviceManager);
        }
    }

     public BleDevice(BleDeviceManager deviceManager, String macAddress, String deviceName) {
        mMacAddress = macAddress;
        mDeviceManager = deviceManager;


        if (deviceName == null) {
            mDeviceManager.uhOh(this, "device no name");
            return;
        }
        mNativeDevice = BleNativeDevice.getNativeDevice(deviceName, this,
                mDeviceManager);
    }



    public BluetoothDevice getBluetoothDevice() {
        try{
            BluetoothDevice bluetoothDevice = mDeviceManager.getBluetoothAdapter()
                    .getRemoteDevice(mMacAddress);
            return bluetoothDevice;
        }catch (Exception e){
            return null;
        }
    }

    public BleNativeDevice getNativeDevice(){
        return mNativeDevice;
    }

    public String getDeviceName() {
        BluetoothDevice bluetoothDevice = getBluetoothDevice();
        if(bluetoothDevice == null){
            return null;
        }
        return bluetoothDevice.getName();
    }

    public synchronized String getMacAddress() {
        return mMacAddress;
    }

    long mDeviceSearchDate;
    public long getDeviceSearchDate(){
        return mDeviceSearchDate;
    }

    public void setDeviceDate(long deviceSearchDate){
        mDeviceSearchDate = deviceSearchDate;
    }

    private BleDeviceConnect mDeviceConnect;

    public BleDeviceConnect getDeviceConnect() {
        return mDeviceConnect;
    }

    public void reConnect() {
        mReconnectCount--;

        if (postNeeded()) {
            post(new Runnable() {
                @Override
                public void run() {
                    connectExecute();
                }
            });
        } else {
            connectExecute();
        }
    }

    synchronized private void connectExecute() {
        ConnectResultState resultState = mDeviceConnect.execute();
//        mDeviceState = DeviceConnectState.Connecting;

        switch (resultState) {
            case AlreadyConnected:
                RNLog.d(TAG, "Already Connected");
                break;
            case AlreadyConnecting:
                RNLog.d(TAG, "Try Connecting");
                break;
            case TryConnectingSuccessful:
                mDeviceState = DeviceConnectState.Connecting;
                RNLog.d(TAG, "Start Connecting");
                break;
            case TryConnectFailed:
                mDeviceState = DeviceConnectState.ConnectFailed;
                RNLog.d(TAG, "Failed Connecting");
                mDeviceManager.uhOh(this,"connect error");
                break;
        }
    }

    private boolean postNeeded() {
        return mDeviceManager.getUpdateLoop().postNeeded();
    }

    private void post(final Runnable runnable) {
        final UpdateLoop updateLoop = mDeviceManager.getUpdateLoop();

        updateLoop.postIfNeeded(runnable);
    }

    private boolean postMainThreadNeeded() {
        return mDeviceManager.getMainUpdateLoop().postNeeded();
    }

    private void postMainThread(final Runnable runnable) {
        if(mDeviceManager == null){
            return;
        }
        final UpdateLoop updateLoop = mDeviceManager.getMainUpdateLoop();

        updateLoop.postIfNeeded(runnable);
    }

    /** 得到自身 */
    private BleDevice getSelfDevice() {
        return this;
    }

    /** 断开蓝牙连接 */
    public void disconnect() {
        if(mConnectTimeoutRunnable!=null){
            mDeviceManager.getUpdateLoop().removeCallback(mConnectTimeoutRunnable);
        }

        if(postNeeded()){
            post(new Runnable() {
                @Override
                public void run() {
                    try{
                        mDeviceState = DeviceConnectState.Disconnect;
                        mNativeDevice.disableNotifications();

                        try{
                            Thread.sleep(300);
                        }catch (InterruptedException error){
                            error.printStackTrace();
                        }

                        if(mDeviceConnect != null){
                            mDeviceConnect.closeGatt(false);
                        }

                        mDeviceManager.removeDevice(getSelfDevice());
                    }catch (Error error){
//                        Log.e(TAG,"crash"+error.toString());
                    }
                }
            });
        }else{
            try{
                mDeviceState = DeviceConnectState.Disconnect;
                mNativeDevice.disableNotifications();

                try{
                    Thread.sleep(300);
                }catch (InterruptedException error){
                    error.printStackTrace();
                }

                if(mDeviceConnect != null){
                    mDeviceConnect.closeGatt(false);
                }

                mDeviceManager.removeDevice(getSelfDevice());
            }catch (Error error){
//                        Log.e(TAG,"crash"+error.toString());
            }
        }
    }

    /** 是否已经连接 */
    public boolean isNativelyConnected() {
        return (mDeviceState == DeviceConnectState.Connected);
    }

    /** 是否正在连接 */
    public boolean isNativelyConnecting() {
        return (mDeviceState == DeviceConnectState.Connecting);
    }

    /** 是否自动连接 */
    private boolean mShouldUseAutoConnect = false;

    public boolean shouldUseAutoConnect() {
        return mShouldUseAutoConnect;
    }

    public BluetoothGattCallback deviceGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(final BluetoothGatt gatt,
                                            final int status, final int newState) {
            super.onConnectionStateChange(gatt, status, newState);
//            Log.i(TAG, "接收到蓝牙状态: " + status + " " + newState);

            if (postNeeded()) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        onConnectionStateChange_mainThread(gatt, status,
                                newState);
                    }
                });
            } else {
                onConnectionStateChange_mainThread(gatt, status, newState);
            }

        }

        @Override
        public void onServicesDiscovered(final BluetoothGatt gatt,
                                         final int status) {
            super.onServicesDiscovered(gatt, status);
            Log.d(TAG, "找到服务:" + gatt.getDevice().getBondState());
            Log.d(TAG, "找到服务:" + gatt.getDevice().getName());

            // mNativeDevice.onServicesDiscovered_mainThread(gatt,status);
            isDiscoveredService = true;
            if(mServiceTimer != null){
                mServiceTimer.cancel();
                mServiceTimer.purge();
            }

            if (postNeeded()) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        mNativeDevice.onServicesDiscovered_mainThread(gatt,
                                status);
                    }
                });
            } else {
                mNativeDevice.onServicesDiscovered_mainThread(gatt, status);
            }

            if (postMainThreadNeeded()) {
                postMainThread(new Runnable() {
                    @Override
                    public void run() {
                        mDeviceCallback.onGetServices(getSelfDevice());
                    }
                });
            } else {
                mDeviceCallback.onGetServices(getSelfDevice());
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
//            Log.d(TAG, "接收到信息");

//            Log.d(TAG, characteristic.toString());
            byte[] charac = characteristic.getValue();
            UUID uuid = characteristic.getUuid();
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
//            Log.d(TAG, "写信息成功");

        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            final BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
//            Log.i(TAG,
//                    "接收到信息"
//                            + Bytes2HexString(characteristic.getValue(), 0,
//                            characteristic.getValue().length));
                final BluetoothGattCharacteristic tempCharacteristic = new BluetoothGattCharacteristic(
                        characteristic.getUuid(), 0x02, 0);
                tempCharacteristic.setValue(characteristic.getValue());
                // Log.d(TAG, characteristic.toString());
                // byte[] charac = characteristic.getValue();
                // UUID uuid = characteristic.getUuid();
                if (postNeeded()) {
                    post(new Runnable() {
                        @Override
                        public void run() {
                            mNativeDevice.receiveData(tempCharacteristic);
                        }
                    });
                } else {
                    mNativeDevice.receiveData(tempCharacteristic);
                }
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt,
                                     BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
//            Log.i(TAG, "onDescriptorRead status:" + status);

        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt,
                                      BluetoothGattDescriptor descriptor, int status) {
//            super.onDescriptorWrite(gatt, descriptor, status);
//            Log.i(TAG, "onDescriptorWrite status:" + status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // mDeviceManager.uhOh(getSelfDevice(),"onDescriptorWrite error");
                mNativeDevice.setIsGetWriteDescription(gatt,true);
            }
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        // @Override
        // public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
        // super.onMtuChanged(gatt, mtu, status);
        // }
    };

    private int mReconnectCount = 3;

    private void onConnectionStateChange_mainThread(final BluetoothGatt gatt,
                                                    final int gattStatus, final int newState) {
        // ---i DRK > NOTE: For some devices disconnecting by turning off the
        // peripheral comes back with a status of 8, which is
        // BluetoothGatt.GATT_SERVER.
        // --- For that same device disconnecting from the app the status is 0.
        // Just an FYI to future developers in case they want to distinguish
        // --- between the two as far as user intent or something.

        RNLog.d(TAG, "bond status:" + getBluetoothDevice().getBondState()+" gattStatus:" + newState);
//        RNLog.d(TAG, "gattStatus:" + newState);

        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // if(mDeviceState != DeviceConnectState.Disconnect &&
            // mReconnectCount>0){
            // //如果并未是主动连接的断开,尝试重新连接
            // reConnect();
            // }else{
            // //如果断开的话,保险再次关闭下.反正里面有保护
            // disconnect();
            // }
            // 如果断开的话,保险再次关闭下.反正里面有保护
            disconnect();
            final BleDevice device = this;

            if (gattStatus == 133) {
//                Log.i(TAG, "抢占式失败，不报告上层！");
                if (postMainThreadNeeded()) {
                    postMainThread(new Runnable() {
                        @Override
                        public void run() {
                            if(mDeviceCallback == null){
                                return;
                            }

                            mDeviceCallback.onConnectError(device,"133");
                        }
                    });
                } else {
                    if(mDeviceCallback == null){
                        return;
                    }
                    mDeviceCallback.onConnectError(device,"133");
                }
                return;
            }


            if (postMainThreadNeeded()) {
                postMainThread(new Runnable() {
                    @Override
                    public void run() {
                        if(mDeviceCallback == null){
                            return;
                        }

                        mDeviceCallback.onDisconnect(device);
                    }
                });
            } else {
                if(mDeviceCallback == null){
                    return;
                }
                mDeviceCallback.onDisconnect(device);
            }

        } else if (newState == BluetoothProfile.STATE_CONNECTING) {
            mDeviceState = DeviceConnectState.Connecting;
        } else if (newState == BluetoothProfile.STATE_CONNECTED) {
            if(mConnectTimeoutRunnable!=null){
                mDeviceManager.getUpdateLoop().removeCallback(mConnectTimeoutRunnable);
            }
            mDeviceState = DeviceConnectState.Connected;

            mDeviceCallback.onConnect(this);

            // if(getDeviceName().startsWith("iGate") &&
            // getBluetoothDevice().getBondState() !=
            // BluetoothDevice.BOND_BONDED){
            // Log.i(TAG,if (postNeeded()) {
//                post(new Runnable() {
//                    @Override
//                    public void run() {
//                        executeService(gatt, gattStatus);
//                    }
//                });
//
//
//            } else {
//                executeService(gatt, gattStatus);
//            }"还没有绑定成功,不找服务");
            // return;
            // }
            mDeviceManager.getUpdateLoop().postDelay(new Runnable() {
                @Override
                public void run() {
                    executeService(gatt, gattStatus);
                }
            },200);
//
        }
    }

    /** 执行查找服务 */
    private void executeService(final BluetoothGatt gatt, final int gattStatus) {
        if (gattStatus != BluetoothGatt.GATT_SUCCESS) {
            mDeviceManager.uhOh(this, "service status error:" + gattStatus);
            return;
        }

        if (!gatt.discoverServices()) {
            mDeviceManager.uhOh(this, "get service error");
            return;
        }

        isDiscoveredService = false;
        if(mServiceTimer != null){
            mServiceTimer.cancel();
            mServiceTimer.purge();
        }

        mServiceTimer  = new Timer();
        mServiceTimer.schedule(new TimerTask() {
            @Override

            public void run() {
                if(!isDiscoveredService){
                    mDeviceManager.uhOh(BleDevice.this, "get service error");
                }
            }
        },5000);
    }

    BleDeviceCallback mDeviceCallback = null;

    /** 设置设备callback  */
    public void setDeviceCallback(BleDeviceCallback deviceCallback){
        mDeviceCallback = deviceCallback;
    }

    Runnable mConnectTimeoutRunnable = null;
    /** 蓝牙连接 */
    public void connect(BleDeviceCallback deviceCallback) {
        RNLog.d(TAG, "开始连接");
        mDeviceCallback = deviceCallback;
        if (mDeviceConnect == null) {
            mDeviceConnect = new BleDeviceConnect(mDeviceManager, this);
        }

        mReconnectCount = 3;
        if (postMainThreadNeeded()) {
            postMainThread(new Runnable() {
                @Override
                public void run() {
                    connectExecute();
                }
            });
        } else {
            connectExecute();
        }

        mConnectTimeoutRunnable = new Runnable() {
            @Override
            public void run() {
                if(mDeviceState == DeviceConnectState.Disconnect){
                    //重复连接的时候如果下次连接在时间间隔之内，就会重复触发，很危险。
                    RNLog.d(TAG,"already disconnected");
                    return;
                }

                RNLog.d(TAG,"check status:"+ mDeviceState);
                if(mDeviceState == DeviceConnectState.Connected){
                    RNLog.d(TAG,"already connected");
                    return;
                }


                mDeviceManager.uhOh(getSelfDevice(),"connect over time");

                if(mDeviceCallback == null){
                    return;
                }

                postMainThread(new Runnable() {
                    @Override
                    public void run() {
                        mDeviceCallback.onConnectError(getSelfDevice(),"连接错误，请重新连接");
                    }
                });
            }
        };
        mDeviceManager.getUpdateLoop().postDelay(mConnectTimeoutRunnable,50*1000);
    }

    /** 蓝牙连接 */
    public void autoConnect(BleDeviceCallback deviceCallback) {
        RNLog.d(TAG, "开始连接");
        mDeviceCallback = deviceCallback;
        if (mDeviceConnect == null) {
            mDeviceConnect = new BleDeviceConnect(mDeviceManager, this);
        }

        mReconnectCount = 3;
        if (postMainThreadNeeded()) {
            postMainThread(new Runnable() {
                @Override
                public void run() {
                    connectExecute();
                }
            });
        } else {
            connectExecute();
        }


    }



    public void deviceBonded() {
        RNLog.d(TAG, "broadcast bond status:" + getBluetoothDevice().getBondState());
        RNLog.d(TAG, "status" + mDeviceState);


        if (postMainThreadNeeded()) {
            postMainThread(new Runnable() {
                @Override
                public void run() {
                    if(mDeviceCallback == null){
                        return;
                    }

                    mDeviceCallback.onBonded(BleDevice.this);
                }
            });
        } else {
            if(mDeviceCallback == null){
                return;
            }
            mDeviceCallback.onBonded(BleDevice.this);
        }



        if (postNeeded()) {
            post(new Runnable() {
                @Override
                public void run() {
                    executeService(mDeviceConnect.getGatt(),
                            BluetoothGatt.GATT_SUCCESS);
                }
            });
        } else {
            executeService(mDeviceConnect.getGatt(), BluetoothGatt.GATT_SUCCESS);
        }

    }

    public static String Bytes2HexString(byte[] b, int start, int len) {
        String ret = "";
        for (int i = start; i < len; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            ret += hex.toUpperCase();
        }
        return ret;
    }

    synchronized public void forceDisconnect(){
        disconnect();
        postMainThread(new Runnable() {
            @Override
            public void run() {
                if(mDeviceCallback != null){
                    mDeviceCallback.onDisconnect(getSelfDevice());
                }
            }
        });
    }

    /** 得到设备的显示名称  */
    public String getDeviceDisplayName(){
        if(mNativeDevice == null){
            return "怡诺科技医疗设备";
        }
        return mNativeDevice.getDeviceDisplayName();
    }

}


