#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_BPXBATTERYDEFINITIONS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_BPXBATTERYDEFINITIONS_H_

#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/serialize/SerialLinkedListAdapter.h>

#include <cstdint>

#include "fsfw/devicehandlers/DeviceHandlerIF.h"

namespace BpxBattery {

enum LocalPoolIds {
  CHARGE_CURRENT = 0,
  DISCHARGE_CURRENT = 1,
  HEATER_CURRENT = 2,
  BATT_VOLTAGE = 3,
  BATT_TEMP_1 = 4,
  BATT_TEMP_2 = 5,
  BATT_TEMP_3 = 6,
  BATT_TEMP_4 = 7,
  REBOOT_COUNTER = 8,
  BOOTCAUSE = 9,

  BATTERY_HEATER_MODE = 10,
  BATTHEAT_LOW_LIMIT = 11,
  BATTHEAT_HIGH_LIMIT = 12
};

static constexpr DeviceCommandId_t GET_HK = 0;
static constexpr DeviceCommandId_t PING = 1;
static constexpr DeviceCommandId_t REBOOT = 2;
static constexpr DeviceCommandId_t RESET_COUNTERS = 3;
// This is the mnemonic GomSpace chose, but this command actually restores the default config
static constexpr DeviceCommandId_t CONFIG_CMD = 4;
static constexpr DeviceCommandId_t CONFIG_GET = 5;
static constexpr DeviceCommandId_t CONFIG_SET = 6;

static constexpr DeviceCommandId_t MAN_HEAT_ON = 10;
static constexpr DeviceCommandId_t MAN_HEAT_OFF = 11;

static constexpr uint8_t RESET_COUNTERS_MAGIC_VALUE = 0x42;
static constexpr uint8_t DEFAULT_PING_SENT_BYTE = 0x07;

static constexpr uint32_t HK_SET_ID = GET_HK;
static constexpr uint32_t CFG_SET_ID = CONFIG_GET;

static constexpr size_t GET_HK_REPLY_LEN = 23;
static constexpr size_t PING_REPLY_LEN = 3;
static constexpr size_t EMPTY_REPLY_LEN = 2;
static constexpr size_t CONFIG_GET_REPLY_LEN = 5;

static constexpr uint8_t PORT_PING = 1;
static constexpr uint8_t PORT_REBOOT = 4;
static constexpr uint8_t PORT_GET_HK = 9;
static constexpr uint8_t PORT_RESET_COUNTERS = 15;
static constexpr uint8_t PORT_CONFIG_CMD = 17;
static constexpr uint8_t PORT_CONFIG_GET = 18;
static constexpr uint8_t PORT_CONFIG_SET = 19;
static constexpr uint8_t PORT_MAN_HEAT_ON = 20;
static constexpr uint8_t PORT_MAN_HEAT_OFF = 21;

static constexpr uint8_t HK_ENTRIES = 10;
static constexpr uint8_t CFG_ENTRIES = 3;

// Taken from BPX manual 3.14
typedef struct __attribute__((packed)) {
  //! Mode for battheater [0=OFF,1=Auto]
  uint8_t battheater_mode;
  int8_t battheater_low;
  //! Turn heater on at [degC]
  int8_t battheater_high;
  //! Turn heater off at [degC]
} bpx_config_t;

//! Not used for more but might still be useful
class BpxHkDeserializer : public SerialLinkedListAdapter<SerializeIF> {
 public:
  BpxHkDeserializer() { setLinks(); }

  //! Charge current in mA
  SerializeElement<uint16_t> chargeCurrent;
  //! Discharge current in mA
  SerializeElement<uint16_t> dischargeCurrent;
  //! Heater current in mA
  SerializeElement<uint16_t> heaterCurrent;

  //! Battery voltage in mV
  SerializeElement<uint16_t> battVoltage;
  //! Battery temperature 1 in degC
  SerializeElement<int16_t> battTemp1;
  //! Battery temperature 2 in degC
  SerializeElement<int16_t> battTemp2;
  //! Battery temperature 3 in degC
  SerializeElement<int16_t> battTemp3;
  //! Battery temperature 4 in degC
  SerializeElement<int16_t> battTemp4;

  SerializeElement<uint32_t> rebootCounter;
  SerializeElement<uint8_t> bootcause;

 private:
  void setLinks() {
    setStart(&chargeCurrent);
    chargeCurrent.setNext(&dischargeCurrent);
    dischargeCurrent.setNext(&heaterCurrent);
    heaterCurrent.setNext(&battVoltage);
    battVoltage.setNext(&battTemp1);
    battTemp1.setNext(&battTemp2);
    battTemp2.setNext(&battTemp3);
    battTemp3.setNext(&battTemp4);
    battTemp4.setNext(&rebootCounter);
    rebootCounter.setNext(&bootcause);
  }
};

};  // namespace BpxBattery

/**
 * @brief   BPX HK data holder
 */
class BpxBatteryHk : public StaticLocalDataSet<BpxBattery::HK_ENTRIES> {
 public:
  /**
   * Constructor for data users
   * @param gyroId
   */
  BpxBatteryHk(object_id_t bpxId) : StaticLocalDataSet(sid_t(bpxId, BpxBattery::HK_SET_ID)) {
    setAllVariablesReadOnly();
  }

  ReturnValue_t parseRawHk(const uint8_t* data, size_t size) {
    size_t remSize = size;
    ReturnValue_t result =
        chargeCurrent.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = dischargeCurrent.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = heaterCurrent.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = battVoltage.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = battTemp1.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = battTemp2.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = battTemp3.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = battTemp4.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = rebootCounter.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    result = bootcause.deSerialize(&data, &remSize, SerializeIF::Endianness::NETWORK);
    if (result != returnvalue::OK) {
      return result;
    }
    return result;
  }

  //! Charge current in mA
  lp_var_t<uint16_t> chargeCurrent =
      lp_var_t<uint16_t>(sid.objectId, BpxBattery::CHARGE_CURRENT, this);
  //! Discharge current in mA
  lp_var_t<uint16_t> dischargeCurrent =
      lp_var_t<uint16_t>(sid.objectId, BpxBattery::DISCHARGE_CURRENT, this);
  //! Heater current in mA
  lp_var_t<uint16_t> heaterCurrent =
      lp_var_t<uint16_t>(sid.objectId, BpxBattery::HEATER_CURRENT, this);

  //! Battery voltage in mV
  lp_var_t<uint16_t> battVoltage = lp_var_t<uint16_t>(sid.objectId, BpxBattery::BATT_VOLTAGE, this);
  //! Battery temperature 1 in degC
  lp_var_t<int16_t> battTemp1 = lp_var_t<int16_t>(sid.objectId, BpxBattery::BATT_TEMP_1, this);
  //! Battery temperature 2 in degC
  lp_var_t<int16_t> battTemp2 = lp_var_t<int16_t>(sid.objectId, BpxBattery::BATT_TEMP_2, this);
  //! Battery temperature 3 in degC
  lp_var_t<int16_t> battTemp3 = lp_var_t<int16_t>(sid.objectId, BpxBattery::BATT_TEMP_3, this);
  //! Battery temperature 4 in degC
  lp_var_t<int16_t> battTemp4 = lp_var_t<int16_t>(sid.objectId, BpxBattery::BATT_TEMP_4, this);
  lp_var_t<uint32_t> rebootCounter =
      lp_var_t<uint32_t>(sid.objectId, BpxBattery::REBOOT_COUNTER, this);
  lp_var_t<uint8_t> bootcause = lp_var_t<uint8_t>(sid.objectId, BpxBattery::BOOTCAUSE, this);

 private:
  friend class BpxBatteryHandler;
  /**
   * Constructor for data creator
   * @param hkOwner
   */
  BpxBatteryHk(HasLocalDataPoolIF* hkOwner) : StaticLocalDataSet(hkOwner, BpxBattery::HK_SET_ID) {}
};

class BpxBatteryCfg : public StaticLocalDataSet<BpxBattery::CFG_ENTRIES> {
 public:
  /**
   * Constructor for data users
   * @param gyroId
   */
  BpxBatteryCfg(object_id_t bpxId) : StaticLocalDataSet(sid_t(bpxId, BpxBattery::CFG_SET_ID)) {
    setAllVariablesReadOnly();
  }

  ReturnValue_t parseRawHk(const uint8_t* data, size_t size) {
    if (size < 3) {
      return SerializeIF::STREAM_TOO_SHORT;
    }
    battheatermode.value = data[0];
    battheaterLow.value = data[1];
    battheaterHigh.value = data[2];
    return returnvalue::OK;
  }

  //! Mode for battheater [0=OFF,1=Auto]
  lp_var_t<uint8_t> battheatermode =
      lp_var_t<uint8_t>(sid.objectId, BpxBattery::BATTERY_HEATER_MODE, this);
  //! Turn heater on at [degC]
  lp_var_t<int8_t> battheaterLow =
      lp_var_t<int8_t>(sid.objectId, BpxBattery::BATTHEAT_LOW_LIMIT, this);
  //! Turn heater off at [degC]
  lp_var_t<int8_t> battheaterHigh =
      lp_var_t<int8_t>(sid.objectId, BpxBattery::BATTHEAT_HIGH_LIMIT, this);

 private:
  friend class BpxBatteryHandler;
  /**
   * Constructor for data creator
   * @param hkOwner
   */
  BpxBatteryCfg(HasLocalDataPoolIF* hkOwner)
      : StaticLocalDataSet(hkOwner, BpxBattery::CFG_SET_ID) {}
};

#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_BPXBATTERYDEFINITIONS_H_ */