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.BleDeviceConnect;
import com.device.comm.mylibrary.BleDeviceManager;
import com.device.comm.mylibrary.UpdateLoop;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.UUID;

/**
 * Created by lvwang2002 on 16/1/25.
 */
public class BleNativeAccuGlucoseDevice extends BleNativeDevice {
    private String TAG ="BLE_ENUO_GLUCOSE_DEVICE";

    public static final String UUID_BTLE_SERVICE = "00001808-0000-1000-8000-00805f9b34fb";
    public static final String UUID_BTLE_SEND = "49535343-8841-43f4-a8d4-ecbe34729bb3";
    public static final String UUID_BTLE_SEND_INDICATION = "00002a52-0000-1000-8000-00805f9b34fb";
    public static final String UUID_BTLE_RECEIVE = "00002a18-0000-1000-8000-00805f9b34fb";
    public static final String UUID_BTLE_RECEIVE_GLUCOSE_DATA = "00002a34-0000-1000-8000-00805f9b34fb";

    private String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";


    BleDevice mDevice = null;
    BleDeviceManager mDeviceManager = null;

    public BleNativeAccuGlucoseDevice(BleDevice device, BleDeviceManager deviceManager){
        mDevice = device;
        mDeviceManager = deviceManager;

    }


    @Override
    public String getNotifyCharacteristicUUID() {
        return UUID_BTLE_RECEIVE;
    }

    @Override
    public String getDeviceServiceUUID(){
        return  UUID_BTLE_SERVICE;
    }

    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) {
        final UpdateLoop updateLoop = mDeviceManager.getMainUpdateLoop();

        updateLoop.postIfNeeded(runnable);
    }

    public synchronized void postDelay(Runnable runnable,long delayMillis){
        final UpdateLoop updateLoop = mDeviceManager.getUpdateLoop();
        updateLoop.postDelay(runnable,delayMillis);
    }



    BleNativeAccuGlucoseDeviceCallback mDeviceCallback = null;
    public interface BleNativeAccuGlucoseDeviceCallback extends BleNativeDeviceCallback{
        public void onResult(BleDevice device, GlucoseData data);

    }

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


    BluetoothGattCharacteristic mNotifyCharacteristic = null;
    BluetoothGattCharacteristic mGlucoseDataNotifyCharacteristic = null;
    BluetoothGattCharacteristic mIndicationCharacteristic = null;
    BluetoothGattService mDeviceService = null;
    BluetoothGattCharacteristic mGattCharacteristicTrans = null;
    @Override
    public void onServicesDiscovered_mainThread(final BluetoothGatt gatt, final int gattStatus) {
        Log.d(TAG,"找到服务,开始处理");

        if( gattStatus == BluetoothGatt.GATT_SUCCESS ) {


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

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

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



            mGattCharacteristicTrans = mDeviceService.getCharacteristic(UUID.fromString(UUID_BTLE_SEND_INDICATION));
            if(mGattCharacteristicTrans == null){
                mDeviceManager.uhOh(mDevice,"not get send device Characteristic");
                return;
            }
//            sendData( new byte[]{(byte) 1, (byte) 6});

//            mGattCharacteristicTrans.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_SIGNED);

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

//            enableNotification(gatt,true,mNotifyCharacteristic);
//            enableNotification(gatt,true,mGlucoseDataNotifyCharacteristic);
//            enableIndication(gatt,true,mIndicationCharacteristic);
            enableNotificationOrIndication(gatt,mNotifyCharacteristic);
            enableNotificationOrIndication(gatt,mGlucoseDataNotifyCharacteristic);
            enableNotificationOrIndication(gatt,mIndicationCharacteristic);

            sendData( new byte[]{(byte) 1, (byte) 6});
        } else {
            mDeviceManager.uhOh(mDevice,"service status error:"+gattStatus);
        }

    }


    private static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
    /**
     * Enables notifications on given characteristic
     *
     * @return true is the request has been sent, false if one of the arguments was <code>null</code> or the characteristic does not have the CCCD.
     */
    protected final boolean enableNotifications(final BluetoothGatt gatt,final BluetoothGattCharacteristic characteristic) {
//        final BluetoothGatt gatt = mGattCharacteristicTrans;
        if (gatt == null || characteristic == null)
            return false;

        // Check characteristic property
        final int properties = characteristic.getProperties();
        if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0)
            return false;

        Log.d("BLE", "gatt.setCharacteristicNotification(" + characteristic.getUuid() + ", true)");
        gatt.setCharacteristicNotification(characteristic, true);
        final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
//        if (descriptor != null) {
//            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//            Log.v("BLE", "Enabling notifications for " + characteristic.getUuid());
//            Log.d("BLE", "gatt.writeDescriptor(" + CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID + ", value=0x01-00)");
//            return gatt.writeDescriptor(descriptor);
//        }
        return false;
    }

    /**
     * Enables indications on given characteristic
     *
     * @return true is the request has been sent, false if one of the arguments was <code>null</code> or the characteristic does not have the CCCD.
     */
    protected final boolean enableIndications(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
//        final BluetoothGatt gatt = mBluetoothGatt;
        if (gatt == null || characteristic == null)
            return false;

        // Check characteristic property
        final int properties = characteristic.getProperties();
        if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == 0)
            return false;

        Log.d("BLE", "gatt.setCharacteristicNotification(" + characteristic.getUuid() + ", true)");
        gatt.setCharacteristicNotification(characteristic, true);
        final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
//        if (descriptor != null) {
//            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
//            Log.v("BLE", "Enabling indications for " + characteristic.getUuid());
//            Log.d("BLE", "gatt.writeDescriptor(" + CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID + ", value=0x02-00)");
//            return gatt.writeDescriptor(descriptor);
//        }
        return false;
    }

//    作者：蓝点工坊
//    链接：https://www.jianshu.com/p/8690dbafe849
//    來源：简书
//    简书著作权归作者所有，任何形式的转载都请联系作者获得授权并注明出处。

    public final static UUID CCC = UUID
            .fromString("00002902-0000-1000-8000-00805f9b34fb");


    private boolean enableIndication(final BluetoothGatt gatt, boolean enable,
                                     BluetoothGattCharacteristic characteristic) {
        if (gatt == null || characteristic == null)
            return false;
        if (!gatt.setCharacteristicNotification(characteristic,
                enable))
            return false;
        BluetoothGattDescriptor clientConfig = characteristic
                .getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
        if (clientConfig == null)
            return false;

        if (enable) {
            clientConfig
                    .setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
        } else {
            clientConfig
                    .setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return gatt.writeDescriptor(clientConfig);
    }


    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.e(TAG,"write descriptor error");
//
////            mDeviceManager.uhOh(mDevice,"write descriptor error");
//            return;
//        };

    }
    private static final String CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
    private boolean enableNotificationOrIndication(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        if (characteristic == null || !gatt.setCharacteristicNotification(characteristic, true)) {
            return false;
        }
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CHARACTERISTIC_CONFIG));
        if (descriptor != null) {
            boolean notify = (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0;
            boolean indicate = (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0;
            if (notify) {
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            }
            if (indicate) {
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
            }
            return gatt.writeDescriptor(descriptor);
        }
        return true;
    }

    public void sendReqData(){
        sendData(new byte[]{(byte) 1, (byte) 6});
    }

    private void sendData(byte[] data) {

        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,"数据长度不在指定范围内");
            }
        }
    }

    /** 接收并处理数据  */
    @Override
    public void receiveData(BluetoothGattCharacteristic characteristic){
        final byte[] data = characteristic.getValue();
        if(!checkedCommandIntegrity(data)){
            Log.d(TAG,"完整性检测失败");
            return;
        }

        double floatValue = ((double) (getFloatValue(data, 50, 12).floatValue() * 100000.0f)) / 18.02d;
        double doubleValue = new BigDecimal(floatValue).setScale(1, RoundingMode.HALF_UP).doubleValue();

        Log.d(TAG,"glucose value:"+doubleValue);
        GlucoseData gData = new GlucoseData();
        gData.setGlucose((int)(doubleValue*10));

        mDeviceCallback.onResult(this.mDevice,gData);
    }

    public static  boolean checkedCommandIntegrity(byte[] b){
        //长度检测
        if(b.length<12){
            return false;
        }

//

        return true;
    }

    public static boolean checkedCommandCRC(byte[] b){
        byte[] a = new byte[10];
        System.arraycopy(b, 4, a, 0, 10);
        int crcValue = BleDeviceCRC.calcCrc16(a,0,10,65535);
        long high = ((long) (b[15] & 0xFF))*256;
        long result = high + (long)(b[14] & 0xFF);
        return result == crcValue;
    }

    private static int queId = -1;
    /** 校验是否是重复指令 */
    public static boolean checkedRepeatCommand(byte[] b){
        int m = (b[2]&0xff);
        if(m == queId){
            return true;
        }
        //2.将该条命令的顺序id写入缓存
        queId = m;

        //4.返回false.
        return false;
    }

    public GlucoseData data2Glucose(byte[] data){
        GlucoseData glucoseData = new GlucoseData();
        glucoseData.setYear(2000+(data[6] & 0xff));
        glucoseData.setMonth(data[4] & 0xff);
        glucoseData.setDay(data[5] & 0xff);
        glucoseData.setHour(data[7] & 0xff);
        glucoseData.setMinute(data[8] & 0xff);
        glucoseData.setGlucose((data[9]&0xff) + 256*(data[10]&0xff));
        glucoseData.setMask(data[11]&0xff);
        glucoseData.setFlagValue((data[12]&0xff) + 256*(data[13]&0xff));
        return glucoseData;
    }

    @Override
    public String getDeviceDisplayName(){
        return "罗氏血糖仪";
    }

    @Override
    public boolean disableNotifications() {
        BleDeviceConnect connect  = mDevice.getDeviceConnect();
        if(connect == null){
            return false;
        }

        BluetoothGatt gatt = connect.getGatt();
        if (gatt == null || mNotifyCharacteristic == null)
            return false;

        if (!(gatt.setCharacteristicNotification(mNotifyCharacteristic, false)))
            return false;

        BluetoothGattDescriptor clientConfig = mNotifyCharacteristic.getDescriptor(UUID.fromString(this.CLIENT_CHARACTERISTIC_CONFIG));
        if (clientConfig == null)
            return false;

        Log.i(TAG, "disable notification");
        clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);

        return gatt.writeDescriptor(clientConfig);
    }

    public static Float getFloatValue(byte[] bArr, int i, int i2) {
        switch (i) {
            case 50:
                return Float.valueOf(bytesToFloat(bArr[i2], bArr[i2 + 1]));
            case 52:
            default:
                return null;
        }
    }

    public static float bytesToFloat(byte b, byte b2) {
        return (float) (Math.pow(10.0d, (double) unsignedToSigned(unsignedByteToInt(b2) >> 4, 4)) * ((double) unsignedToSigned(unsignedByteToInt(b) + ((unsignedByteToInt(b2) & 15) << 8), 12)));
    }

    public static int unsignedToSigned(int i, int i2) {
        return ((1 << (i2 + -1)) & i) != 0 ? ((1 << (i2 - 1)) - (((1 << (i2 - 1)) - 1) & i)) * -1 : i;
    }

    public static int unsignedByteToInt(byte b) {
        return b & 255;
    }


}
