#include "ACUHandler.h"

#include "OBSWConfig.h"

ACUHandler::ACUHandler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie,
                       FailureIsolationBase *customFdir)
    : GomspaceDeviceHandler(objectId, comIF, comCookie, cfg, customFdir),
      coreHk(this),
      auxHk(this) {
  cfg.maxConfigTableAddress = ACU::MAX_CONFIGTABLE_ADDRESS;
  cfg.maxHkTableAddress = ACU::MAX_HKTABLE_ADDRESS;
  cfg.hkTableSize = ACU::HK_TABLE_SIZE;
  cfg.cfgTableSize = ACU::CONFIG_TABLE_SIZE;
}

ACUHandler::~ACUHandler() {}

ReturnValue_t ACUHandler::buildNormalDeviceCommand(DeviceCommandId_t *id) {
  *id = GOMSPACE::REQUEST_HK_TABLE;
  return buildCommandFromCommand(*id, nullptr, 0);
}

void ACUHandler::fillCommandAndReplyMap() { GomspaceDeviceHandler::fillCommandAndReplyMap(); }

void ACUHandler::letChildHandleHkReply(DeviceCommandId_t id, const uint8_t *packet) {
  parseHkTableReply(packet);
  if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1
    PoolReadGuard pg0(&auxHk);
    PoolReadGuard pg1(&coreHk);
    if (pg0.getReadResult() != returnvalue::OK or pg1.getReadResult() != returnvalue::OK) {
      return;
    }
    for (size_t idx = 0; idx < 3; idx++) {
      float tempC = coreHk.temperatures[idx] * 0.1;
      sif::info << "ACU: Temperature " << idx << ": " << tempC << " °C" << std::endl;
    }
    sif::info << "ACU: Ground Watchdog Timer Count: " << auxHk.wdtCntGnd.value << std::endl;
    sif::info << "ACU: Ground watchdog timer, seconds left before reboot: "
              << auxHk.wdtGndLeft.value << std::endl;
#endif
  }
}

void ACUHandler::letChildHandleConfigReply(DeviceCommandId_t id, const uint8_t *packet) {
  handleDeviceTm(packet, ACU::CONFIG_TABLE_SIZE, id);
}

LocalPoolDataSetBase *ACUHandler::getDataSetHandle(sid_t sid) {
  if (sid == coreHk.getSid()) {
    return &coreHk;
  } else if (sid == auxHk.getSid()) {
    return &auxHk;
  }
  return nullptr;
}

ReturnValue_t ACUHandler::parseHkTableReply(const uint8_t *packet) {
  PoolReadGuard pg0(&coreHk);
  PoolReadGuard pg1(&auxHk);
  auto res0 = pg0.getReadResult();
  auto res1 = pg1.getReadResult();
  if (res0 != returnvalue::OK) {
    return res0;
  }
  if (res1 != returnvalue::OK) {
    return res1;
  }
  for (size_t idx = 0; idx < 6; idx++) {
    coreHk.currentInChannels[idx] = as<int16_t>(packet + (idx * 2));
  }
  for (size_t idx = 0; idx < 6; idx++) {
    coreHk.voltageInChannels[idx] = as<uint16_t>(packet + 0xc + (idx * 2));
  }

  coreHk.vcc = as<uint16_t>(packet + 0x1a);
  coreHk.vbat = as<uint16_t>(packet + 0x18);

  for (size_t idx = 0; idx < 3; idx++) {
    coreHk.temperatures[idx] = as<int16_t>(packet + 0x1c + (idx * 2)) * 0.1;
  }

  coreHk.mpptMode = packet[0x22];

  for (size_t idx = 0; idx < 6; idx++) {
    coreHk.vboostInChannels[idx] = as<uint16_t>(packet + 0x24 + (idx * 2));
  }
  for (size_t idx = 0; idx < 6; idx++) {
    coreHk.powerInChannels[idx] = as<uint16_t>(packet + 0x30 + (idx * 2));
  }
  for (size_t idx = 0; idx < 3; idx++) {
    auxHk.dacEnables[idx] = *(packet + 0x3c + idx);
  }
  for (size_t idx = 0; idx < 6; idx++) {
    auxHk.dacRawChannelVals[idx] = as<uint16_t>(packet + 0x40 + (idx * 2));
  }

  auxHk.bootCause = as<uint32_t>(packet + 0x50);
  coreHk.bootcnt = as<uint32_t>(packet + 0x54);
  coreHk.uptime = as<uint32_t>(packet + 0x58);
  auxHk.resetCause = as<uint16_t>(packet + 0x5c);
  coreHk.mpptTime = as<uint16_t>(packet + 0x5e);
  coreHk.mpptPeriod = as<uint16_t>(packet + 0x60);

  for (size_t idx = 0; idx < 8; idx++) {
    auxHk.deviceTypes[idx] = *(packet + 0x64 + idx);
  }
  for (size_t idx = 0; idx < 8; idx++) {
    auxHk.devicesStatus[idx] = *(packet + 0x6c + idx);
  }

  auxHk.wdtCntGnd = as<uint32_t>(packet + 0x74);
  auxHk.wdtGndLeft = as<uint32_t>(packet + 0x78);
  coreHk.setValidity(true, true);
  auxHk.setValidity(true, true);
  return returnvalue::OK;
}

ReturnValue_t ACUHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
                                                  LocalDataPoolManager &poolManager) {
  using namespace ACU;
  localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNELS, new PoolEntry<int16_t>(6));
  localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNELS, new PoolEntry<uint16_t>(6));

  localDataPoolMap.emplace(pool::ACU_VCC, new PoolEntry<uint16_t>({0}));
  localDataPoolMap.emplace(pool::ACU_VBAT, new PoolEntry<uint16_t>({0}));

  localDataPoolMap.emplace(pool::ACU_TEMPERATURES, new PoolEntry<float>(3));

  localDataPoolMap.emplace(pool::ACU_MPPT_MODE, new PoolEntry<uint8_t>({0}));

  localDataPoolMap.emplace(pool::ACU_VBOOST_IN_CHANNELS, new PoolEntry<uint16_t>(6));
  localDataPoolMap.emplace(pool::ACU_POWER_IN_CHANNELS, new PoolEntry<uint16_t>(6));

  localDataPoolMap.emplace(pool::ACU_DAC_ENABLES, new PoolEntry<uint8_t>(3));
  localDataPoolMap.emplace(pool::ACU_DAC_RAW_CHANNELS, new PoolEntry<uint16_t>(6));

  localDataPoolMap.emplace(pool::ACU_BOOTCAUSE, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(pool::ACU_BOOTCNT, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(pool::ACU_UPTIME, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(pool::ACU_RESET_CAUSE, new PoolEntry<uint16_t>({0}));
  localDataPoolMap.emplace(pool::ACU_MPPT_TIME, new PoolEntry<uint16_t>({0}));
  localDataPoolMap.emplace(pool::ACU_MPPT_PERIOD, new PoolEntry<uint16_t>({0}));

  localDataPoolMap.emplace(pool::ACU_DEVICES, new PoolEntry<uint8_t>(8));
  localDataPoolMap.emplace(pool::ACU_DEVICES_STATUS, new PoolEntry<uint8_t>(8));

  localDataPoolMap.emplace(pool::ACU_WDT_CNT_GND, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(pool::ACU_WDT_GND_LEFT, new PoolEntry<uint32_t>({0}));

  poolManager.subscribeForDiagPeriodicPacket(
      subdp::DiagnosticsHkPeriodicParams(coreHk.getSid(), false, 10.0));
  poolManager.subscribeForRegularPeriodicPacket(
      subdp::RegularHkPeriodicParams(auxHk.getSid(), false, 30.0));
  return returnvalue::OK;
}

void ACUHandler::printChannelStats() {
  PoolReadGuard pg(&coreHk);
  sif::info << "ACU Info: Current [mA], Voltage [mV]" << std::endl;
  for (size_t idx = 0; idx < 6; idx++) {
    sif::info << std::setw(8) << std::left << "Channel " << idx << std::dec << "| "
              << static_cast<unsigned int>(coreHk.currentInChannels[idx]) << std::setw(15)
              << std::right << coreHk.voltageInChannels[idx] << std::endl;
  }
}

void ACUHandler::setDebugMode(bool enable) { this->debugMode = enable; }

ReturnValue_t ACUHandler::printStatus(DeviceCommandId_t cmd) {
  ReturnValue_t result = returnvalue::OK;
  switch (cmd) {
    case (GOMSPACE::PRINT_SWITCH_V_I): {
      PoolReadGuard pg(&coreHk);
      result = pg.getReadResult();
      if (result != returnvalue::OK) {
        break;
      }
      printChannelStats();
      break;
    }
    default: {
      return DeviceHandlerIF::COMMAND_NOT_SUPPORTED;
    }
  }
  if (result != returnvalue::OK) {
    sif::warning << "Reading PDU1 HK table failed!" << std::endl;
  }
  return result;
}