#include <fsfw/datapool/PoolReadGuard.h>
#include <mission/power/GomSpacePackets.h>
#include <mission/power/Pdu1Handler.h>

#include "devices/powerSwitcherList.h"

Pdu1Handler::Pdu1Handler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie,
                         FailureIsolationBase *customFdir, bool enableHkSets)
    : GomspaceDeviceHandler(objectId, comIF, comCookie, cfg, customFdir, enableHkSets),
      coreHk(this),
      auxHk(this) {
  initPduConfigTable();
}

Pdu1Handler::~Pdu1Handler() {}

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

void Pdu1Handler::letChildHandleHkReply(DeviceCommandId_t id, const uint8_t *packet) {
  parseHkTableReply(packet);
}

void Pdu1Handler::assignChannelHookFunction(GOMSPACE::ChannelSwitchHook hook, void *args) {
  this->channelSwitchHook = hook;
  this->hookArgs = args;
}

ReturnValue_t Pdu1Handler::setParamCallback(SetParamMessageUnpacker &unpacker,
                                            bool afterExecution) {
  using namespace PDU1;
  GOMSPACE::Pdu pdu = GOMSPACE::Pdu::PDU1;
  if (not afterExecution) {
    return returnvalue::OK;
  }
  if (channelSwitchHook != nullptr and unpacker.getParameterSize() == 1) {
    switch (unpacker.getAddress()) {
      case (CONFIG_ADDRESS_OUT_EN_TCS_BOARD_3V3): {
        channelSwitchHook(pdu, 0, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_SYRLINKS): {
        channelSwitchHook(pdu, 1, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_STAR_TRACKER): {
        channelSwitchHook(pdu, 2, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_MGT): {
        channelSwitchHook(pdu, 3, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_SUS_NOMINAL): {
        channelSwitchHook(pdu, 4, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_SOLAR_CELL_EXP): {
        channelSwitchHook(pdu, 5, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_PLOC): {
        channelSwitchHook(pdu, 6, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_ACS_BOARD_SIDE_A): {
        channelSwitchHook(pdu, 7, unpacker.getParameter()[0], hookArgs);
        break;
      }
      case (CONFIG_ADDRESS_OUT_EN_CHANNEL8): {
        channelSwitchHook(pdu, 8, unpacker.getParameter()[0], hookArgs);
        break;
      }
    }
  }
  return returnvalue::OK;
}

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

void Pdu1Handler::parseHkTableReply(const uint8_t *packet) {
  GomspaceDeviceHandler::parsePduHkTable(coreHk, auxHk, packet);
}

ReturnValue_t Pdu1Handler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
                                                   LocalDataPoolManager &poolManager) {
  initializePduPool(localDataPoolMap, poolManager, pcdu::INIT_SWITCHES_PDU1);
  poolManager.subscribeForDiagPeriodicPacket(
      subdp::DiagnosticsHkPeriodicParams(coreHk.getSid(), enableHkSets, 30.0));
  poolManager.subscribeForRegularPeriodicPacket(
      subdp::RegularHkPeriodicParams(auxHk.getSid(), enableHkSets, 6000.0));
  return returnvalue::OK;
}

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

ReturnValue_t Pdu1Handler::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;
      }
      printHkTableSwitchVI();
      break;
    }
    case (GOMSPACE::PRINT_LATCHUPS): {
      PoolReadGuard pg(&auxHk);
      result = pg.getReadResult();
      if (result != returnvalue::OK) {
        break;
      }
      printHkTableLatchups();
      break;
    }
    default: {
      return DeviceHandlerIF::COMMAND_NOT_SUPPORTED;
    }
  }
  if (result != returnvalue::OK) {
    sif::warning << "Reading PDU1 HK table failed!" << std::endl;
  }
  return result;
}

void Pdu1Handler::printHkTableSwitchVI() {
  using namespace PDU1;
  sif::info << "PDU1 Info: " << std::endl;
  sif::info << "Boot Cause: " << auxHk.bootcause << " | Boot Count: " << std::setw(4) << std::right
            << coreHk.bootcount << std::endl;
  sif::info << "Reset Cause: " << auxHk.resetcause
            << " | Battery Mode: " << static_cast<int>(coreHk.battMode.value) << std::endl;
  sif::info << "SwitchState, Currents [mA], Voltages [mV]:" << std::endl;
  auto printerHelper = [&](std::string channelStr, Channels idx) {
    sif::info << std::setw(30) << std::left << channelStr << std::dec << "| "
              << unsigned(coreHk.outputEnables[idx]) << ", " << std::setw(4) << std::right
              << coreHk.currents[idx] << ", " << std::setw(4) << coreHk.voltages[idx] << std::endl;
  };

  printerHelper("TCS Board", Channels::TCS_BOARD_3V3);
  printerHelper("Syrlinks", Channels::SYRLINKS);
  printerHelper("Star Tracker", Channels::STR);
  printerHelper("MGT", Channels::MGT);
  printerHelper("SUS Nominal", Channels::SUS_NOMINAL);
  printerHelper("SCEX", Channels::SOL_CELL_EXPERIMENT);
  printerHelper("PLOC", Channels::PLOC);
  printerHelper("ACS Board A Side", Channels::ACS_A_SIDE);
  printerHelper("Channel 8", Channels::UNUSED);
  printerHelper("Syrlinks", Channels::SYRLINKS);
}

void Pdu1Handler::printHkTableLatchups() {
  using namespace PDU1;
  sif::info << "PDU1 Latchup Information" << std::endl;
  auto printerHelper = [&](std::string channelStr, Channels idx) {
    sif::info << std::setw(MAX_CHANNEL_STR_WIDTH) << std::left << "TCS Board" << std::dec << "| "
              << std::setw(4) << std::right << auxHk.latchups[idx] << std::endl;
  };
  printerHelper("TCS Board", Channels::TCS_BOARD_3V3);
  printerHelper("Syrlinks", Channels::SYRLINKS);
  printerHelper("Star Tracker", Channels::STR);
  printerHelper("MGT", Channels::MGT);
  printerHelper("SUS Nominal", Channels::SUS_NOMINAL);
  printerHelper("SCEX", Channels::SOL_CELL_EXPERIMENT);
  printerHelper("PLOC", Channels::PLOC);
  printerHelper("ACS Board A Side", Channels::ACS_A_SIDE);
  printerHelper("Channel 8", Channels::UNUSED);
  printerHelper("Syrlinks", Channels::SYRLINKS);
}