package com.device.comm.mylibrary.NativeDevice;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.util.Log;

import com.device.comm.mylibrary.BleDevice;
import com.device.comm.mylibrary.BleDeviceManager;
import com.device.comm.mylibrary.NativeDevice.MQ.BloodGlucose;
import com.device.comm.mylibrary.RNLog;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * Created by lvwang2002 on 2019/3/25.
 */

public class BleMQDynamicGlucose extends BleNativeDevice {
    static final String TAG = "BLE_MQ";

    static final byte CMD_SWITCH = 0x06;
    static final byte RESPOND_NEW_WORK = 0x08;
    static final byte RESPOND_OLD_WORK = 0x03;
    static final byte CMD_START = 0x01;

    static final byte CMD_GET_DATA = 0x04;
    static final byte RESPOND_DATA = 0x02;
    private byte[] authCRCHi = new byte[]{(byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 1, (byte) -64, Byte.MIN_VALUE, (byte) 65, (byte) 0, (byte) -63, (byte) -127, (byte) 64};
    private byte[] authCRCLo = new byte[]{(byte) 0, (byte) -64, (byte) -63, (byte) 1, (byte) -61, (byte) 3, (byte) 2, (byte) -62, (byte) -58, (byte) 6, (byte) 7, (byte) -57, (byte) 5, (byte) -59, (byte) -60, (byte) 4, (byte) -52, (byte) 12, (byte) 13, (byte) -51, (byte) 15, (byte) -49, (byte) -50, (byte) 14, (byte) 10, (byte) -54, (byte) -53, (byte) 11, (byte) -55, (byte) 9, (byte) 8, (byte) -56, (byte) -40, (byte) 24, (byte) 25, (byte) -39, (byte) 27, (byte) -37, (byte) -38, (byte) 26, (byte) 30, (byte) -34, (byte) -33, (byte) 31, (byte) -35, (byte) 29, (byte) 28, (byte) -36, (byte) 20, (byte) -44, (byte) -43, (byte) 21, (byte) -41, (byte) 23, (byte) 22, (byte) -42, (byte) -46, (byte) 18, (byte) 19, (byte) -45, (byte) 17, (byte) -47, (byte) -48, (byte) 16, (byte) -16, (byte) 48, (byte) 49, (byte) -15, (byte) 51, (byte) -13, (byte) -14, (byte) 50, (byte) 54, (byte) -10, (byte) -9, (byte) 55, (byte) -11, (byte) 53, (byte) 52, (byte) -12, (byte) 60, (byte) -4, (byte) -3, (byte) 61, (byte) -1, (byte) 63, (byte) 62, (byte) -2, (byte) -6, (byte) 58, (byte) 59, (byte) -5, (byte) 57, (byte) -7, (byte) -8, (byte) 56, (byte) 40, (byte) -24, (byte) -23, (byte) 41, (byte) -21, (byte) 43, (byte) 42, (byte) -22, (byte) -18, (byte) 46, (byte) 47, (byte) -17, (byte) 45, (byte) -19, (byte) -20, (byte) 44, (byte) -28, (byte) 36, (byte) 37, (byte) -27, (byte) 39, (byte) -25, (byte) -26, (byte) 38, (byte) 34, (byte) -30, (byte) -29, (byte) 35, (byte) -31, (byte) 33, (byte) 32, (byte) -32, (byte) -96, (byte) 96, (byte) 97, (byte) -95, (byte) 99, (byte) -93, (byte) -94, (byte) 98, (byte) 102, (byte) -90, (byte) -89, (byte) 103, (byte) -91, (byte) 101, (byte) 100, (byte) -92, (byte) 108, (byte) -84, (byte) -83, (byte) 109, (byte) -81, (byte) 111, (byte) 110, (byte) -82, (byte) -86, (byte) 106, (byte) 107, (byte) -85, (byte) 105, (byte) -87, (byte) -88, (byte) 104, (byte) 120, (byte) -72, (byte) -71, (byte) 121, (byte) -69, (byte) 123, (byte) 122, (byte) -70, (byte) -66, (byte) 126, Byte.MAX_VALUE, (byte) -65, (byte) 125, (byte) -67, (byte) -68, (byte) 124, (byte) -76, (byte) 116, (byte) 117, (byte) -75, (byte) 119, (byte) -73, (byte) -74, (byte) 118, (byte) 114, (byte) -78, (byte) -77, (byte) 115, (byte) -79, (byte) 113, (byte) 112, (byte) -80, (byte) 80, (byte) -112, (byte) -111, (byte) 81, (byte) -109, (byte) 83, (byte) 82, (byte) -110, (byte) -106, (byte) 86, (byte) 87, (byte) -105, (byte) 85, (byte) -107, (byte) -108, (byte) 84, (byte) -100, (byte) 92, (byte) 93, (byte) -99, (byte) 95, (byte) -97, (byte) -98, (byte) 94, (byte) 90, (byte) -102, (byte) -101, (byte) 91, (byte) -103, (byte) 89, (byte) 88, (byte) -104, (byte) -120, (byte) 72, (byte) 73, (byte) -119, (byte) 75, (byte) -117, (byte) -118, (byte) 74, (byte) 78, (byte) -114, (byte) -113, (byte) 79, (byte) -115, (byte) 77, (byte) 76, (byte) -116, (byte) 68, (byte) -124, (byte) -123, (byte) 69, (byte) -121, (byte) 71, (byte) 70, (byte) -122, (byte) -126, (byte) 66, (byte) 67, (byte) -125, (byte) 65, (byte) -127, Byte.MIN_VALUE, (byte) 64};


    public static final float BATTERY_LOWER = 2.8f;
    public static final float BG_LOWER = 3.5f;
    public static final float BG_UPPER = 11.1f;
    public static final int CONNECT_COUNT = 60;
    public static final long DATA_INTERRUPT_TIME = 300000;
    public static final String DATA_OUT_NOTIFY_UUID = "00002902-0000-1000-8000-00805f9b34fb";
    public static final int DEFAULT_INITIAL_TIME = 24;
    public static final int DEFAULT_LAST_TIME = 564;
    public static final int DEFAULT_PERCENT = 18;
    public static final int ELECTRIC_LOWER = 10;
    public static final int ELECTRIC_UPPER = 1100;
    public static final int INITIAL_TIME_DEFAULT = 24;
    public static final int INITIAL_TIME_LOWER = 12;
    public static final int INITIAL_TIME_UPPER = 1440;
    public static final int INITIAL_UNIT = 60000;
    public static final int LAST_TIME_LOWER = 12;
    public static final int LAST_TIME_UPPER = 1440;
    public static final String NOTIFY_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
    public static final int PERCENT_LOWER = 0;
    public static final int PERCENT_UPPER = 99;
    public static final String SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
    public static final String WRITE_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
    private String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";

    public static final int KEY_LOADED_EXCEPTION_RESOURCE_CHECK = 255;

    BleDevice mDevice = null;
    BleDeviceManager mDeviceManager = null;

    public static final int NO_WORK = 0;
    public static final int CONTINUE_WORK = 1;
    public static final int NEW_WORK = 2;


    private int  deviceStatus = NO_WORK;

    public BleMQDynamicGlucose(BleDevice device, BleDeviceManager deviceManager){
        mDevice = device;
        mDeviceManager = deviceManager;
        mUpdateLoop = deviceManager.getUpdateLoop();
    }

    MQDynamicGlucoseInterface mDeviceCallback;
    @Override
    public void  setNativeDeviceCallback(BleNativeDeviceCallback deviceCallback){
        mDeviceCallback = (MQDynamicGlucoseInterface) deviceCallback;
    }

    private BluetoothGattCharacteristic mNotifyCharacteristic = null;
    BluetoothGattService mDeviceService = null;
    private BluetoothGattCharacteristic mGattCharacteristicTrans = null;
    @Override
    public void onServicesDiscovered_mainThread(final BluetoothGatt gatt, final int gattStatus) {
        RNLog.d(TAG,"找到服务,开始处理");
        if( gattStatus != BluetoothGatt.GATT_SUCCESS ) {
            mDeviceManager.uhOh(mDevice,"service status error:"+gattStatus);
        }

        List<BluetoothGattService> services =  gatt.getServices();

        mDeviceService = gatt.getService(UUID.fromString(SERVICE_UUID));
        if (mDeviceService == null) {
            mDeviceManager.uhOh(mDevice, "not get device server");
            return;
        }

        mNotifyCharacteristic = mDeviceService.getCharacteristic(UUID.fromString(NOTIFY_UUID));
        if (mNotifyCharacteristic == null) {
            mDeviceManager.uhOh(mDevice, "not get device Characteristic");
            return;
        }

        mGattCharacteristicTrans = mDeviceService.getCharacteristic(UUID.fromString(WRITE_UUID));
        if (mGattCharacteristicTrans == null) {
            mDeviceManager.uhOh(mDevice, "not get send device Characteristic");
            return;
        }
        mGattCharacteristicTrans.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);


        enableNotification(gatt,true,mNotifyCharacteristic);
    }

    private void enableNotification(final BluetoothGatt gatt,final boolean enable,final BluetoothGattCharacteristic characteristic) {
        if (gatt == null){
            Log.e(TAG,"gatt 为空");
            return;
        }

        if (!(gatt.setCharacteristicNotification(characteristic, enable))){
            Log.e(TAG,"设置Gatt失败");
            mDeviceManager.uhOh(mDevice,"set gatt notify error");
            return;
        }

        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID
                .fromString(CLIENT_CHARACTERISTIC_CONFIG));
        if (clientConfig == null){
            Log.e(TAG,"clientConfig为空,但好像也不是特别影响");
            return;
        }
        byte[] b = {
                (byte) 0x00, (byte) 0x01
        };
        try{
            clientConfig.setValue(b);
        }catch (Exception e) {
            mDeviceManager.uhOh(mDevice,"null exception");
            return;
        }

        if (enable) {
            Log.i(TAG, "enable notification");
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            Log.i(TAG, "disable notification");
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }

        if(!gatt.writeDescriptor(clientConfig)){
            Log.i(TAG,"write descriptor error");

            mDeviceManager.uhOh(mDevice,"write descriptor error");
            return;
        }

    }

    @Override
    public synchronized void receiveData(BluetoothGattCharacteristic characteristic){
        final byte[] data = characteristic.getValue();
        RNLog.d(TAG,"received data:"+Bytes2HexString(data,0,data.length));
        receiveHandler(data);

    }

    void receiveHandler(byte[] data){
        if(!checkCRC(data)){
            return;
        }

        byte command = data[2];

        switch (command){
            case CMD_START:{
                RNLog.d(TAG,"cmd start "+ deviceStatus);
                if(mDeviceCallback==null){
                    return;
                }
                if(deviceStatus == NEW_WORK){
                    mDeviceCallback.onStartPolarization(mDevice);
                }else{
                    mDeviceCallback.onStartWork(mDevice);
                }

                deviceStatus = NO_WORK;
            }
            break;
            case CMD_SWITCH:{
                RNLog.d(TAG,"cmd switch "+deviceStatus);
                final int status = data[4]&0xff;

                if(mDeviceCallback==null){
                    return;
                }
                mDeviceCallback.onGetMonitorStatus(mDevice,status);

                postDelay(new Runnable() {
                    @Override
                    public void run() {
                        if(deviceStatus == NEW_WORK){
                            respondNewWork();
                        }else if(deviceStatus == CONTINUE_WORK){
                            respondOldWork();
                        }else{

                        }

                    }
                },200);

            }
            break;
            case CMD_GET_DATA:{
                RNLog.d(TAG,"cmd get data");
                BloodGlucose bloodGlucose = getBloodGlucose(data);
                if(mDeviceCallback==null){
                    return;
                }
                mDeviceCallback.onUpdateNewData(mDevice,bloodGlucose);

                postDelay(new Runnable() {
                    @Override
                    public void run() {
                        respondData();
                    }
                },200);
            }
            break;
        }


    }



    public void respondNewWork(){
        RNLog.d(TAG,"res new work");
        byte[] data = assembleCommand(RESPOND_NEW_WORK, new byte[]{(byte)0x00});
        sendData(data);
    }

    public void respondOldWork(){
        RNLog.d(TAG,"res continue work");
        byte[] data = assembleCommand(RESPOND_OLD_WORK, new byte[]{(byte)0x00});
        sendData(data);
    }

    public void respondData(){
        byte[] data = assembleCommand(RESPOND_DATA, new byte[]{(byte)0x00});
        sendData(data);
    }

    public void startWork(){
        deviceStatus = NEW_WORK;
    }

    public void continueWork(){
        deviceStatus = CONTINUE_WORK;
    }

    private void sendData(byte[] data) {
        try {
            if (data != null && data.length > 0 && data.length < 21) {
                if (mGattCharacteristicTrans.setValue(data)
                        && mDevice.getDeviceConnect().getGatt().writeCharacteristic(mGattCharacteristicTrans)) {
                    Log.d(TAG, "send data OK");
                }else{
                    Log.d(TAG,"数据长度不在指定范围内");
                }
            }
        } catch (NullPointerException e) {
            e.printStackTrace();

            Log.d(TAG,"崩溃了");
        }


    }

    private byte[] assembleCommand(byte command, byte[] data){
        int commandLength = data.length+6;
        byte[] cmdData = new byte[commandLength];

        cmdData[0] = (byte)0x5a;
        cmdData[1] = (byte)0xa5;
        cmdData[2] = command;
        cmdData[3] = (byte)data.length;
        for(int i=0;i<data.length;i++){
            cmdData[i+4] = data[i];
        }
        cmdData = getCRC(cmdData);
        return cmdData;
    }

    private int calcCrc16(byte[] bArr, int i, int i2) {
        int i3 = KEY_LOADED_EXCEPTION_RESOURCE_CHECK;
        int i4 = KEY_LOADED_EXCEPTION_RESOURCE_CHECK;
        for (int i5 = 0; i5 < i2; i5++) {
            int i6 = (i3 ^ bArr[i + i5]) & KEY_LOADED_EXCEPTION_RESOURCE_CHECK;
            i3 = this.authCRCHi[i6] ^ i4;
            i4 = this.authCRCLo[i6];
        }
        return (i3 & KEY_LOADED_EXCEPTION_RESOURCE_CHECK) | ((i4 & KEY_LOADED_EXCEPTION_RESOURCE_CHECK) << 8);
    }

    private byte[] getCRC(byte[] bArr) {
        int calcCrc16 = calcCrc16(bArr, 0, bArr.length - 2);
        bArr[bArr.length - 2] = (byte) ((65280 & calcCrc16) >> 8);
        bArr[bArr.length - 1] = (byte) (calcCrc16 & KEY_LOADED_EXCEPTION_RESOURCE_CHECK);
        return bArr;
    }

    private boolean checkCRC(byte[] bArr) {
        return (((bArr[bArr.length + -2] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK) << 8) | (bArr[bArr.length + -1] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK)) == calcCrc16(bArr, 0, bArr.length + -2);
    }

    public BloodGlucose getBloodGlucose(byte[] bArr) {
        BloodGlucose bloodGlucose = new BloodGlucose();
        bloodGlucose.setIndex(((bArr[6] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK) << 8) | (bArr[5] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK));
        bloodGlucose.setOriginalElectric(((bArr[8] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK) << 8) | (bArr[7] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK));
        bloodGlucose.setBGElectric((float) bloodGlucose.getOriginalElectric());
        bloodGlucose.setPower(((float) (bArr[9] & KEY_LOADED_EXCEPTION_RESOURCE_CHECK)) / 10.0f);
        bloodGlucose.setBGBytes(bArr);
        bloodGlucose.setFull(false);
        return bloodGlucose;
    }


}
