package cgmblelib.ble.gattcallback;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;

import androidx.annotation.NonNull;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import cgmblelib.base.BApplication;
import cgmblelib.database.entity.PocDevice;
import cgmblelib.database.repository.RepositoryDevice;
import cgmblelib.utils.MyLog;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import ist.com.sdk.EDevice;
import ist.com.sdk.HexadecimalTools;

/**
 * Created by minyuchun on 2019/1/28.
 * 蓝牙连接 回调控制
 *
 * @author minyuchun
 */
public class BleGattCallback extends BluetoothGattCallback {
    /**
     * 协调器
     */
    private Disposable mDisposable;
    /**
     * 新建立的 mBluetoothGatt
     */
    private BluetoothGatt mBluetoothGatt;
    /**
     * 需要写入的数据
     */
    private BluetoothGattCharacteristic mCharacteristic = null;
    /**
     * 算法发射器类型
     */
    private EDevice mEDevice = EDevice.DEVICE1;


    /**
     * <init>
     */
    private BleGattCallback() {
    }

    private static class SingletonInstance {
        private static final BleGattCallback INSTANCE = new BleGattCallback();
    }

    public static BleGattCallback getInstance() {
        return SingletonInstance.INSTANCE;
    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        //连接状态改变
        ManageGattCallback.getInstance().onConnectionStateChange(mEDevice, gatt, status, newState);
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorWrite(gatt, descriptor, status);
        //特征写入
        ManageGattCallback.getInstance().onDescriptorWrite(mEDevice, gatt, descriptor, status);
    }

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

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        //服务发现
        ManageGattCallback.getInstance().onServicesDiscovered(mEDevice, gatt, status);
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        //数据写入
        ManageGattCallback.getInstance().onCharacteristicWrite(mEDevice, gatt, characteristic, status);
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        //数据输出
        if (characteristic != null && characteristic.getValue() != null && characteristic.getValue().length > 0) {
            //数据收集无异常
            byte startRead = characteristic.getValue()[0];
            if (startRead == 0x44 || startRead == 0x07) {
                //主动上传的命令
                ManageGattCallback.getInstance().onCharacteristicChanged(mEDevice, gatt, startRead, characteristic.getValue());
            } else if (mCharacteristic != null
                    && mCharacteristic.getValue() != null
                    && mCharacteristic.getValue().length > 0) {
                byte startWrite = mCharacteristic.getValue()[0];
                //与发送的数据头部一致
                if ((startRead == 0x04 && startWrite == 0x03) || (startRead == 0x66 && startWrite == 0x67)) {
                    //与数据回传啊
                    dispose();
                    ManageGattCallback.getInstance().onCharacteristicChanged(mEDevice, gatt, startRead, characteristic.getValue());
                } else if (startRead == startWrite) {
                    //与发送的数据头部一致
                    dispose();
                    ManageGattCallback.getInstance().onCharacteristicChanged(mEDevice, gatt, startRead, characteristic.getValue());
                } else {
                    MyLog.d("回了什么 " + HexadecimalTools.ByteArrayToStrs16(characteristic.getValue()) + "\n"
                            + "发了什么 " + HexadecimalTools.ByteArrayToStrs16(mCharacteristic.getValue()));

                }
            } else {
                MyLog.d("回了什么 " + HexadecimalTools.ByteArrayToStrs16(characteristic.getValue()));
            }
        } else {
            MyLog.d("什么都回的吗");
        }
    }

    /**
     * connect pocDevice
     *
     * @param context context
     * @param device  device you want to connect by scan
     */
    @SuppressLint("MissingPermission")
    public BluetoothGatt connectDevice(Context context, BluetoothDevice device) {
        closeGatt();
        if (context != null) {
            mEDevice = EDevice.getEnumDevice(device.getName());
            mBluetoothGatt = device.connectGatt(context, false, this, BluetoothDevice.TRANSPORT_LE);
            return mBluetoothGatt;
        }
        return null;
    }


    /**
     * 发现服务
     *
     * @return 是否成功发现服务
     */
    @SuppressLint("MissingPermission")
    public boolean discoverServices() {
        boolean isDiscover = false;
        try {
            if (mBluetoothGatt != null) {
                isDiscover = mBluetoothGatt.discoverServices();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (!isDiscover) {
                closeGatt();
            }
        }
        return isDiscover;
    }

    /**
     * 使用gatt连接
     *
     * @return 返回是否操作成功
     */
    @SuppressLint("MissingPermission")
    public boolean connectGatt() {
        try {
            if (mBluetoothGatt != null) {
                return mBluetoothGatt.connect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 断开Gatt连接
     */
    @SuppressLint("MissingPermission")
    public void disConnectGatt() {
        try {
            if (mBluetoothGatt != null) {
                if (mBluetoothGatt.getDevice() != null) {
                    if (GattTool.isGattConnect(mBluetoothGatt.getDevice())) {
                        mBluetoothGatt.disconnect();
                    }
                } else {
                    mBluetoothGatt.disconnect();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭gatt连接
     */
    @SuppressLint("MissingPermission")
    public void closeGatt() {
        try {
            if (mBluetoothGatt != null) {
                mBluetoothGatt.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mBluetoothGatt = null;
            dispose();
            Observable.just(BApplication.getContext())
                    .subscribeOn(Schedulers.io())
                    .map(context -> {
                        PocDevice device = RepositoryDevice.getInstance(context).getLatestDeviceIoThread();
                        if (device != null && device.isBoundAndConnect()) {
                            return RepositoryDevice.getInstance(context).connectIoThread(device, false);
                        }
                        return false;
                    })
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<Boolean>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable d) {

                        }

                        @Override
                        public void onNext(@NonNull Boolean aBoolean) {
                            if (aBoolean) {
                                MyLog.i("清除缓存强制结束");
                            }
                        }

                        @Override
                        public void onError(@NonNull Throwable e) {
                            MyLog.e(e.getMessage());
                        }

                        @Override
                        public void onComplete() {

                        }
                    });
        }
    }

    /**
     * setCharacteristicNotification
     *
     * @return isSuccess
     */
    @SuppressLint("MissingPermission")
    public boolean setCharacteristicNotification() {
        boolean isSetSuccess = false;
        BluetoothGattService service = null;
        BluetoothGattCharacteristic characteristic = null;
        try {
            if (mBluetoothGatt != null) {
                service = mBluetoothGatt.getService(UUID.fromString(mEDevice.geteGattMessage().getUUID_SERVICE()));
                if (service != null) {
                    MyLog.i("enter service");
                    characteristic = service.getCharacteristic(UUID.fromString(mEDevice.geteGattMessage().getUUID_READ()));
                    if (characteristic != null) {
                        isSetSuccess = mBluetoothGatt.setCharacteristicNotification(characteristic, true);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (isSetSuccess) {

                for (BluetoothGattDescriptor dp : characteristic.getDescriptors()) {
                    if (dp != null) {
                        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                            dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                        } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
                            dp.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        } else {
                            dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                        }
                        mBluetoothGatt.writeDescriptor(dp);
                    }
                }
            } else {
                disConnectGatt();
            }
        }
        return isSetSuccess;
    }

    /**
     * send information to transmitter
     *
     * @param value information
     * @return 是否发送数据成功运行
     */
    public boolean writeCharacteristic(@NonNull byte[] value) {
        return writeCharacteristic(value, 5, 100, 800);
    }

    /**
     * send information to transmitter
     *
     * @param value information
     * @return 是否发送数据成功运行
     */
    public boolean writeCharacteristic(@NonNull byte[] value, int count, long initialDelay, long period) {
        boolean isSetWrite = false;
        try {
            dispose();
            if (mBluetoothGatt != null) {
                BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(mEDevice.geteGattMessage().getUUID_SERVICE()));
                if (service != null) {
                    mCharacteristic = service.getCharacteristic(UUID.fromString(mEDevice.geteGattMessage().getUUID_WRITE()));
                    if (mCharacteristic != null) {
                        isSetWrite = mCharacteristic.setValue(value);
                        intervalRange(count, initialDelay, period);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (!isSetWrite) {
                disConnectGatt();
            }
        }
        return isSetWrite;
    }


    @SuppressLint("MissingPermission")
    private void intervalRange(int count, long initialDelay, long period) {
        if (count <= 1) {
            count = 5;
        }
        if (initialDelay > 500 || initialDelay < 100) {
            initialDelay = 100;
        }
        if (period > 3000 || period < 800) {
            period = 800;
        }
        // Capture current characteristic to avoid null races inside the async lambda
        final BluetoothGattCharacteristic characteristic = mCharacteristic;

        mDisposable = Observable.intervalRange(1, count, initialDelay, period, TimeUnit.MILLISECONDS)
                .observeOn(Schedulers.io())
                .subscribe(aLong -> {
                    if (mBluetoothGatt != null && characteristic != null) {
                        byte[] value = characteristic.getValue();
                        MyLog.d("intervalRange循环 发送数据 次数 " + aLong + "数据 " + HexadecimalTools.ByteArrayToStrs16(value));
                        mBluetoothGatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_BALANCED);
                        mBluetoothGatt.writeCharacteristic(characteristic);
                        if (aLong >= 5 && value != null && value.length > 0 && value[0] != 0x17) {
                            MyLog.e("超出命令发送的最大限制 关闭连接");
                            closeGatt();
                        }
                    } else {
                        MyLog.w("intervalRange 循环没有数据发送 " + aLong);
                        dispose();
                    }
                }, throwable -> {
                    MyLog.w("intervalRange 异常 " + throwable.getMessage());
                    dispose();
                });
    }

    private void dispose() {
        try {
            if (mDisposable != null && !mDisposable.isDisposed()) {
//                MyLog.d("被狗贼暂停了");
                mDisposable.dispose();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mCharacteristic = null;
        }
    }

    public BluetoothDevice checkGattDevice(@NonNull PocDevice device) {
        try {
            if (mBluetoothGatt != null) {
                BluetoothDevice bluetoothDevice = mBluetoothGatt.getDevice();
                if (bluetoothDevice != null && bluetoothDevice.getAddress().equals(device.getAddress())) {
                    return bluetoothDevice;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
