#include "Xadc.h"

#include <fcntl.h>
#include <unistd.h>

#include <sstream>

#include "fsfw/serviceinterface/ServiceInterfaceStream.h"

Xadc::Xadc() {}

Xadc::~Xadc() {}

ReturnValue_t Xadc::getTemperature(float& temperature) {
  ReturnValue_t result = returnvalue::OK;
  int raw = 0;
  int offset = 0;
  float scale = 0;
  result = readValFromFile<int>(xadc::file::tempRaw.c_str(), raw);
  if (result != returnvalue::OK) {
    return result;
  }
  result = readValFromFile<int>(xadc::file::tempOffset.c_str(), offset);
  if (result != returnvalue::OK) {
    return result;
  }
  result = readValFromFile<float>(xadc::file::tempScale.c_str(), scale);
  if (result != returnvalue::OK) {
    return result;
  }
  temperature = (raw + offset) * scale / 1000;
  return result;
}

ReturnValue_t Xadc::getVccPint(float& vccPint) {
  ReturnValue_t result =
      readVoltageFromSysfs(xadc::file::vccpintRaw, xadc::file::vccpintScale, vccPint);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVccPaux(float& vccPaux) {
  ReturnValue_t result =
      readVoltageFromSysfs(xadc::file::vccpauxRaw, xadc::file::vccpauxScale, vccPaux);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVccInt(float& vccInt) {
  ReturnValue_t result =
      readVoltageFromSysfs(xadc::file::vccintRaw, xadc::file::vccintScale, vccInt);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVccAux(float& vccAux) {
  ReturnValue_t result =
      readVoltageFromSysfs(xadc::file::vccauxRaw, xadc::file::vccauxScale, vccAux);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVccBram(float& vccBram) {
  ReturnValue_t result =
      readVoltageFromSysfs(xadc::file::vccbramRaw, xadc::file::vccbramScale, vccBram);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVccOddr(float& vccOddr) {
  ReturnValue_t result =
      readVoltageFromSysfs(xadc::file::vccoddrRaw, xadc::file::vccoddrScale, vccOddr);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVrefp(float& vrefp) {
  ReturnValue_t result = readVoltageFromSysfs(xadc::file::vrefpRaw, xadc::file::vrefpScale, vrefp);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::getVrefn(float& vrefn) {
  ReturnValue_t result = readVoltageFromSysfs(xadc::file::vrefnRaw, xadc::file::vrefnScale, vrefn);
  if (result != returnvalue::OK) {
    return result;
  }
  return returnvalue::OK;
}

ReturnValue_t Xadc::readVoltageFromSysfs(std::string rawFile, std::string scaleFile,
                                         float& voltage) {
  ReturnValue_t result = returnvalue::OK;
  float raw = 0;
  float scale = 0;
  result = readValFromFile(rawFile.c_str(), raw);
  if (result != returnvalue::OK) {
    return result;
  }
  result = readValFromFile(scaleFile.c_str(), scale);
  if (result != returnvalue::OK) {
    return result;
  }
  voltage = calculateVoltage(raw, scale);
  return result;
}

float Xadc::calculateVoltage(int raw, float scale) { return static_cast<float>(raw * scale); }

template <typename T>
ReturnValue_t Xadc::readValFromFile(const char* filename, T& val) {
  FILE* fp;
  fp = fopen(filename, "r");
  if (fp == nullptr) {
    sif::warning << "Xadc::readValFromFile: Failed to open file " << filename << std::endl;
    return returnvalue::FAILED;
  }
  char valstring[MAX_STR_LENGTH] = "";
  char* returnVal = fgets(valstring, MAX_STR_LENGTH, fp);
  if (returnVal == nullptr) {
    sif::warning << "Xadc::readValFromFile: Failed to read string from file " << filename
                 << std::endl;
    fclose(fp);
    return returnvalue::FAILED;
  }
  std::istringstream valSstream(valstring);
  valSstream >> val;
  fclose(fp);
  return returnvalue::OK;
}