#include "AxiPtmeConfig.h"

#include <fsfw/ipc/MutexGuard.h>

#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw_hal/linux/uio/UioMapper.h"

AxiPtmeConfig::AxiPtmeConfig(object_id_t objectId, std::string axiUio, int mapNum)
    : SystemObject(objectId), axiUio(std::move(axiUio)), mapNum(mapNum) {
  mutex = MutexFactory::instance()->createMutex();
  if (mutex == nullptr) {
    sif::warning << "Failed to create mutex" << std::endl;
  }
}

AxiPtmeConfig::~AxiPtmeConfig() {}

ReturnValue_t AxiPtmeConfig::initialize() {
  UioMapper uioMapper(axiUio, mapNum);
  ReturnValue_t result =
      uioMapper.getMappedAdress(&baseAddress, UioMapper::Permissions::READ_WRITE);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t AxiPtmeConfig::writeCaduRateReg(uint8_t rateVal) {
  ReturnValue_t result = mutex->lockMutex(timeoutType, mutexTimeout);
  if (result != returnvalue::OK) {
    sif::warning << "AxiPtmeConfig::writeCaduRateReg: Failed to lock mutex" << std::endl;
    return returnvalue::FAILED;
  }
  *(baseAddress + CADU_BITRATE_REG) = static_cast<uint32_t>(rateVal);
  result = mutex->unlockMutex();
  if (result != returnvalue::OK) {
    sif::warning << "AxiPtmeConfig::writeCaduRateReg: Failed to unlock mutex" << std::endl;
    return returnvalue::FAILED;
  }
  return returnvalue::OK;
}

uint8_t AxiPtmeConfig::readCaduRateReg() {
  MutexGuard mg(mutex);
  return static_cast<uint8_t>(*(baseAddress + CADU_BITRATE_REG));
}

void AxiPtmeConfig::enableTxclockManipulator() {
  writeBit(COMMON_CONFIG_REG, true, BitPos::EN_TX_CLK_MANIPULATOR);
}

void AxiPtmeConfig::disableTxclockManipulator() {
  writeBit(COMMON_CONFIG_REG, false, BitPos::EN_TX_CLK_MANIPULATOR);
}

void AxiPtmeConfig::enableTxclockInversion() {
  writeBit(COMMON_CONFIG_REG, true, BitPos::INVERT_CLOCK);
}

void AxiPtmeConfig::disableTxclockInversion() {
  writeBit(COMMON_CONFIG_REG, false, BitPos::INVERT_CLOCK);
}

void AxiPtmeConfig::enableBatPriorityBit() {
  writeBit(COMMON_CONFIG_REG, true, BitPos::EN_BAT_PRIORITY);
}

void AxiPtmeConfig::disableBatPriorityBit() {
  writeBit(COMMON_CONFIG_REG, false, BitPos::EN_BAT_PRIORITY);
}

void AxiPtmeConfig::writeReg(uint32_t regOffset, uint32_t writeVal) {
  MutexGuard mg(mutex, timeoutType, mutexTimeout);
  *(baseAddress + regOffset / ADRESS_DIVIDER) = writeVal;
}

uint32_t AxiPtmeConfig::readReg(uint32_t regOffset) {
  MutexGuard mg(mutex, timeoutType, mutexTimeout);
  return *(baseAddress + regOffset / ADRESS_DIVIDER);
}

void AxiPtmeConfig::writePollThreshold(AxiPtmeConfig::IdlePollThreshold pollThreshold) {
  uint32_t regVal = readCommonCfgReg();
  // Clear bits first
  regVal &= ~(0b111 << 3);
  regVal |= (static_cast<uint8_t>(pollThreshold) << 3);
  writeCommonCfgReg(regVal);
}

AxiPtmeConfig::IdlePollThreshold AxiPtmeConfig::readPollThreshold() {
  uint32_t regVal = readCommonCfgReg();
  return static_cast<AxiPtmeConfig::IdlePollThreshold>((regVal >> 3) & 0b111);
}

void AxiPtmeConfig::writeCommonCfgReg(uint32_t value) { writeReg(COMMON_CONFIG_REG, value); }
uint32_t AxiPtmeConfig::readCommonCfgReg() { return readReg(COMMON_CONFIG_REG); }

void AxiPtmeConfig::writeBit(uint32_t regOffset, bool bitVal, BitPos bitPos) {
  uint32_t readVal = readReg(regOffset);
  uint32_t writeVal =
      (readVal & ~(1 << static_cast<uint32_t>(bitPos))) | bitVal << static_cast<uint32_t>(bitPos);
  writeReg(regOffset, writeVal);
}