package cgmblelib.ble.gattcallback;


import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;

import androidx.annotation.NonNull;

import java.util.Calendar;
import java.util.Date;

import cgmblelib.Enum.enumError;
import cgmblelib.Enum.enumGlucoseStatus;
import cgmblelib.Enum.enumTrend;
import cgmblelib.Enum.enumUnBind;
import cgmblelib.base.BApplication;
import cgmblelib.ble.entity.AlgorithmCallBack;
import cgmblelib.ble.reconnectManagament.ReconnectManagement;
import cgmblelib.ble.scancallback.ManageScanCallback;
import cgmblelib.custom.livedata.ManagementLiveData;
import cgmblelib.custom.livedata.connect.EnumConnect;
import cgmblelib.database.entity.PocDevice;
import cgmblelib.database.entity.PocGlucose;
import cgmblelib.database.repository.RepositoryDevice;
import cgmblelib.database.repository.RepositoryGlucose;
import cgmblelib.qr.QRInformation;
import cgmblelib.utils.ConstantsLibrary;
import cgmblelib.utils.MyLog;
import cgmblelib.utils.SharedPreferencesLibraryUtil;
import cgmblelib.utils.Tool;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import ist.com.sdk.AlgorithmTools;
import ist.com.sdk.CurrentGlucose;
import ist.com.sdk.EDevice;
import ist.com.sdk.ErrorCode;
import ist.com.sdk.HistoryData;
import ist.com.sdk.LatestData;
import ist.com.sdk.ProtocolTools;
import ist.com.sdk.Trend;

/***
 * Gatt 工具类
 * @author minyuchun
 */
public class GattTool {

  /**
   * 电流进质
   */
  private final static float COEFFICIENT_CURRENT = 0.1f;

  /**
   * 获取byte数组的首个byte
   *
   * @param bytes byte数组
   * @return 返回首个byte
   */
  public static byte getFirstByte(byte[] bytes) {
    if (bytes != null && bytes.length > 0) {
      return bytes[0];
    }
    return 0;
  }

  /**
   * 算法计算 -- 需要在子子线程中进行
   *
   * @param pocGlucose new Glucose form transmitter
   * @return Glucose by algorithm
   */
  public static AlgorithmCallBack algorithm(Context context, @NonNull PocGlucose pocGlucose, @NonNull PocDevice device) {
    if (device.getAlgorithmVersion() < ConstantsLibrary.ALGORITHM_VERSION) {
      return new AlgorithmCallBack(AlgorithmCallBack.ERROR_ALGORITHM, null);
    } else {
      if (checkGlucoseNoInDB(context, pocGlucose)) {
        return algorithm(context, device, pocGlucose);
      } else {
        return new AlgorithmCallBack(AlgorithmCallBack.DATA_REPEAT, pocGlucose);
      }
    }
  }

  /**
   * 验证是否数据库中无该条数据
   *
   * @param context    上下文
   * @param pocGlucose 验证的数据
   * @return 无该数据：true
   */
  private static boolean checkGlucoseNoInDB(Context context, PocGlucose pocGlucose) {
    return RepositoryGlucose.getInstance(context).getGlucoseByIdIoThread(pocGlucose.getDeviceId(), pocGlucose.getGlucoseId()) == null;
  }

  /**
   * 算法计算
   *
   * @param pocGlucose 血糖值
   * @return 返回计算完成的血糖值
   */
  private static AlgorithmCallBack algorithm(Context context, @NonNull PocDevice device, @NonNull PocGlucose pocGlucose) {
    try {
      SharedPreferencesLibraryUtil sharedPreferencesLibraryUtil = new SharedPreferencesLibraryUtil(context);
      QRInformation information = sharedPreferencesLibraryUtil.getQRInformation();
      float k = 0;
      float r = 0;
      long time = Tool.getTime(pocGlucose.getYear(), pocGlucose.getMonth(), pocGlucose.getDay(), pocGlucose.getHour(), pocGlucose.getMinute()).getTimeInMillis();
      LatestData data = new LatestData(
        pocGlucose.getGlucoseId(), pocGlucose.getIw(), pocGlucose.getIb(), pocGlucose.getT(), 0, 0,
        time, device.getName());
      if (information != null) {
        MyLog.i("getQRInformation k=" + information.toString());
        data.setSensorInfo(information.getSensor());
        data.setTransmitterName(device.getName());
        k = information.getK();
        r = information.getR();
        data.setK0(k);
        data.setR(r);
      }
      CurrentGlucose currentGlucose;
      try {
        currentGlucose = AlgorithmTools.getInstance().algorithmLatestGlucose(data);
      } catch (Exception e) {
        e.printStackTrace();
        return new AlgorithmCallBack(AlgorithmCallBack.DATA_LOST, pocGlucose);
      }
      if (currentGlucose != null) {
        if (currentGlucose.getErrorCode() == ErrorCode.ERROR_ALGORITHM_DATA) {
          //缓存不对
          PocGlucose firstGlucose = RepositoryGlucose.getInstance(context).getGlucoseByIdIoThread(device.getId(), 0);
          if (firstGlucose != null) {
            float[] Iws = RepositoryGlucose.getInstance(context).getIwsIoThread(pocGlucose.getDeviceId(), pocGlucose.getIw());
            float[] Ibs = RepositoryGlucose.getInstance(context).getIbsIoThread(pocGlucose.getDeviceId(), pocGlucose.getIb());
            float[] Ts = RepositoryGlucose.getInstance(context).getTsIoThread(pocGlucose.getDeviceId(), pocGlucose.getT());
            HistoryData historyData = new HistoryData();
            historyData.setGlucoseId(pocGlucose.getGlucoseId());
            historyData.setIws(Iws);
            historyData.setIbs(Ibs);
            historyData.setTs(Ts);
            time = Tool.getTime(firstGlucose.getYear(), firstGlucose.getMonth(), firstGlucose.getDay(), firstGlucose.getHour(), firstGlucose.getMinute()).getTimeInMillis();
            historyData.setStartTimeMillis(time);
            historyData.setK0(k);
            historyData.setR(r);
            historyData.setTransmitterName(device.getName());
            MyLog.w("查看历史数据 " + historyData.toString());
            try {
              currentGlucose = AlgorithmTools.getInstance().algorithmGlucose(historyData);
            } catch (Exception e) {
              e.printStackTrace();
              return new AlgorithmCallBack(AlgorithmCallBack.DATA_LOST, pocGlucose);
            }
            if (currentGlucose == null) {
              return new AlgorithmCallBack(AlgorithmCallBack.DATA_LOST, pocGlucose);
            }
          }
        }
      } else {
        return new AlgorithmCallBack(AlgorithmCallBack.DATA_LOST, pocGlucose);
      }
      return GattTool.setPocGlucose(pocGlucose, currentGlucose, device);
    } catch (Exception e) {
      e.printStackTrace();
      MyLog.wLog("异常 " + e.getMessage());
    }
    return new AlgorithmCallBack(AlgorithmCallBack.DATA_LOST, pocGlucose);
  }

  /***
   * 将解析到的血糖数据补充到值
   * @param glucose 解析的血糖数据
   * @param device 设备
   * @param bytes 原始数据
   * @return 返回正式的血糖数据
   */
  public static PocGlucose getPocGlucose(@NonNull ProtocolTools.Glucose glucose, @NonNull PocDevice device, @NonNull byte[] bytes, EDevice eDevice) {
    if (glucose.getId() > 0) {
      PocGlucose lastGlucose = RepositoryGlucose.getInstance(BApplication.getContext()).getGlucoseByIdIoThread(device.getId(), glucose.getId() - 1);
      if (lastGlucose != null) {
        PocGlucose pocGlucose = new PocGlucose();
        Calendar calendar = Calendar.getInstance();
        calendar.set(lastGlucose.getYear(), lastGlucose.getMonth() - 1, lastGlucose.getDay(), lastGlucose.getHour(), lastGlucose.getMinute());
        calendar.add(Calendar.MINUTE, 3);
        pocGlucose.setTimeInMillis(calendar.getTimeInMillis());
        pocGlucose.setYear(calendar.get(Calendar.YEAR));
        pocGlucose.setMonth(calendar.get(Calendar.MONTH) + 1);
        pocGlucose.setDay(calendar.get(Calendar.DAY_OF_MONTH));
        pocGlucose.setHour(calendar.get(Calendar.HOUR_OF_DAY));
        pocGlucose.setMinute(calendar.get(Calendar.MINUTE));
        pocGlucose.setBytes(bytes);
        pocGlucose.setDeviceId(device.getId());
        pocGlucose.setGlucoseId(glucose.getId());
        pocGlucose.setIw(glucose.getIw());
        pocGlucose.setIb(glucose.getIb());
        pocGlucose.setT(glucose.getT());
        return pocGlucose;
      }
    } else {
      PocGlucose pocGlucose = new PocGlucose();
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(device.getBindDate());
      calendar.add(Calendar.MINUTE, 3);
      pocGlucose.setTimeInMillis(calendar.getTimeInMillis());
      pocGlucose.setYear(calendar.get(Calendar.YEAR));
      pocGlucose.setMonth(calendar.get(Calendar.MONTH) + 1);
      pocGlucose.setDay(calendar.get(Calendar.DAY_OF_MONTH));
      pocGlucose.setHour(calendar.get(Calendar.HOUR_OF_DAY));
      pocGlucose.setMinute(calendar.get(Calendar.MINUTE));
      pocGlucose.setBytes(bytes);
      pocGlucose.setDeviceId(device.getId());
      pocGlucose.setGlucoseId(glucose.getId());
      pocGlucose.setIw(glucose.getIw());
      pocGlucose.setIb(glucose.getIb());
      pocGlucose.setT(glucose.getT());
      return pocGlucose;
    }
    return null;
  }

  @SuppressLint("MissingPermission")
  public static boolean isGattConnect(BluetoothDevice bluetoothDevice) {
    BluetoothManager mBluetoothManager = (BluetoothManager) BApplication.getContext().getSystemService(Context.BLUETOOTH_SERVICE);
    return mBluetoothManager.getConnectionState(bluetoothDevice, BluetoothProfile.GATT) == BluetoothProfile.STATE_CONNECTED;
  }

  /**
   * 设置一个凯立特血糖数据
   *
   * @param pocGlucose 凯立特血糖数据
   * @param glucose    计算出来的血糖数据
   * @return 计算值返回
   */
  private static AlgorithmCallBack setPocGlucose(PocGlucose pocGlucose, CurrentGlucose glucose, PocDevice pocDevice) {
    if (glucose == null) {
      return new AlgorithmCallBack(AlgorithmCallBack.DATA_LOST, pocGlucose);
    }
    MyLog.wLog("CurrentGlucose = " + glucose.toString());
    pocGlucose.setBGCount(glucose.getBGCount());
    pocGlucose.setBGICount(glucose.getBGICount());
    pocGlucose.setShowGlucose(Tool.getDecimalOneNumber(Tool.divide(glucose.getGluMG(), 18).floatValue()));
    pocGlucose.setShowGlucoseMG(glucose.getGluMG());
    pocGlucose.setGlucoseStatus(getGlucoseStatus(glucose));
    pocGlucose.setTrend(getEnumTrend(glucose, pocDevice));
    pocGlucose.setErrorCode(getEnumError(glucose));
    pocGlucose.setCountdownDays(glucose.getCountdownDays());
    pocGlucose.setCountdownHours(glucose.getCountdownHours());
    pocGlucose.setCountdownMinutes(glucose.getCountdownMinutes());
    if (pocGlucose.getErrorCode() == enumError.ERROR_DATA) {
      return new AlgorithmCallBack(AlgorithmCallBack.ERROR_DATA, pocGlucose);
    }
    return new AlgorithmCallBack(AlgorithmCallBack.SUCCESS, pocGlucose);
  }

  /**
   * 获取POC趋势
   *
   * @param glucose   计算的血糖数据类
   * @param pocDevice
   * @return 返回POC趋势
   */
  private static enumTrend getEnumTrend(CurrentGlucose glucose, PocDevice pocDevice) {
    try {
      if (glucose.getTrend() != Trend.TREND_NONE) {
        return enumTrend.getEnumTrend(glucose.getTrend().getTrendId());
      } else {
        return enumTrend.NONE;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return enumTrend.getEnumTrend(glucose.getTrend().getTrendId());
  }

  private static enumError getEnumError(CurrentGlucose glucose) {
    return enumError.getEnumError(glucose.getErrorCode().getErrorId());
  }

  private static enumGlucoseStatus getGlucoseStatus(CurrentGlucose glucose) {
    if (glucose != null && glucose.getGluMG() > 0 && glucose.getErrorCode() == ErrorCode.ERROR_NONE) {
      float glu = Tool.getDecimalOneNumber(Tool.divide(glucose.getGluMG(), 18).floatValue());
      if (glu > ConstantsLibrary.LIMIT_HYPERGLYCEMIA_MM_DEFAULT) {
        return enumGlucoseStatus.HYPERGLYCEMIA;
      } else if (glu < ConstantsLibrary.LIMIT_HYPOGLYCEMIA_MM_DEFAULT) {
        return enumGlucoseStatus.HYPOGLYCEMIA;
      }
      return enumGlucoseStatus.NORMAL;
    }
    return enumGlucoseStatus.NONE;
  }

  /**
   * 修改设备表，验证是否当前点满足结束初始化，若满足，重置设备为完成初始化状态
   *
   * @param context    上下文
   * @param pocGlucose 血糖
   * @param device     设备
   */
  public static PocDevice deviceInitComplete(Context context, @NonNull PocGlucose pocGlucose, @NonNull PocDevice device) {
    if (context != null && device.isInit()) {
      if (pocGlucose.getBGICount() > 0) {
        device.setInitEndDate(new Date());
        int updateId = RepositoryDevice.getInstance(context).updateDeviceIoThread(device);
        if (updateId > 0) {
          return RepositoryDevice.getInstance(context).getDeviceByIdIoThread(device.getId());
        }
      }
    }
    return device;
  }

  /**
   * 主线程接触绑定
   *
   * @param context    上下文
   * @param unBindType 解除绑定的类型
   */
  public static void unBindInDatabase(Context context, @NonNull enumUnBind unBindType) {
    Observable.just(context)
      .subscribeOn(Schedulers.io())
      .map(con -> {
        PocDevice device = RepositoryDevice.getInstance(con).getLatestDeviceIoThread();
        return unBindInDatabaseIoThread(con, device, unBindType);
      })
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe();
  }

  /**
   * 解除数据库绑定 -- 修改数据库中的信息
   *
   * @param context    上下文
   * @param device     设备信息
   * @param unBindType 解除的类型
   */
  public static boolean unBindInDatabaseIoThread(Context context, @NonNull PocDevice device, @NonNull enumUnBind unBindType) {
    boolean isSuccess = RepositoryDevice.getInstance(context).unBindIoThread(device, unBindType);
    if (isSuccess) {
      switch (unBindType) {
        case UNBIND_RESET:
          //电池重置
          ManagementLiveData.getInstance().updateOperationConnect(EnumConnect.CONNECT_UNBIND_RESET);
          break;
        default:
          //其他,正常解除绑定
          ManagementLiveData.getInstance().updateOperationConnect(EnumConnect.CONNECT_UNBIND_SUCCESS);
          break;
      }
      ManageGattCallback.getInstance().unBindType = enumUnBind.NONE;
      MyLog.clearLogAdapter();
      ManageScanCallback.getInstance().stopLeScan();
      ReconnectManagement.getInstance().stopReconnect();
      ManageGattCallback.getInstance().removeHandler();
    }
    return isSuccess;
  }
}
