#include "I2cTestClass.h"

#include <errno.h>
#include <fsfw_hal/linux/UnixFileGuard.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/serviceinterface.h"

I2cTestClass::I2cTestClass(object_id_t objectId, std::string i2cdev)
    : TestTask(objectId), i2cdev(i2cdev) {
  mode = TestModes::BPX_BATTERY;
}

ReturnValue_t I2cTestClass::initialize() {
  if (mode == TestModes::BPX_BATTERY) {
    battInit();
  }
  return HasReturnvaluesIF::RETURN_OK;
}

ReturnValue_t I2cTestClass::performPeriodicAction() {
  if (mode == TestModes::BPX_BATTERY) {
    battPeriodic();
  }
  return HasReturnvaluesIF::RETURN_OK;
}

void I2cTestClass::battInit() {
  sif::info << "I2cTestClass: BPX Initialization" << std::endl;
  UnixFileGuard fileHelper(i2cdev, &bpxInfo.fd, O_RDWR, "I2cTestClass::sendMessage");
  if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
    sif::error << "Opening I2C device" << i2cdev << " failed" << std::endl;
    return;
  }
  if (ioctl(bpxInfo.fd, I2C_SLAVE, bpxInfo.addr) < 0) {
    sif::error << "Failed to acquire bus access and/or talk to slave" << std::endl;
  }
  cmdBuf[0] = BpxBattery::PORT_PING;
  cmdBuf[1] = 0x42;
  sendLen = 2;
  ReturnValue_t result = i2cWrite(bpxInfo.fd, cmdBuf.data(), sendLen);
  if (result != HasReturnvaluesIF::RETURN_OK) {
    return;
  }
  // Receive back port, error byte and ping reply
  recvLen = 3;
  result = i2cRead(bpxInfo.fd, replyBuf.data(), recvLen);
  if (result != HasReturnvaluesIF::RETURN_OK) {
    return;
  }
  sif::info << "Ping reply:" << std::endl;
  arrayprinter::print(replyBuf.data(), recvLen);
  if (replyBuf[2] != 0x42) {
    sif::warning << "Received ping reply not expected value 0x42" << std::endl;
  }
}

void I2cTestClass::battPeriodic() {
  UnixFileGuard fileHelper(i2cdev, &bpxInfo.fd, O_RDWR, "I2cTestClass::sendMessage");
  if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
    sif::error << "Opening I2C device" << i2cdev << " failed" << std::endl;
    return;
  }
  if (ioctl(bpxInfo.fd, I2C_SLAVE, bpxInfo.addr) < 0) {
    sif::error << "Failed to acquire bus access and/or talk to slave" << std::endl;
  }
  cmdBuf[0] = BpxBattery::PORT_GET_HK;
  sendLen = 1;
  ReturnValue_t result = i2cWrite(bpxInfo.fd, cmdBuf.data(), sendLen);
  if (result != HasReturnvaluesIF::RETURN_OK) {
    return;
  }
  // Receive back HK set
  recvLen = 23;
  result = i2cRead(bpxInfo.fd, replyBuf.data(), recvLen);
  if (result != HasReturnvaluesIF::RETURN_OK) {
    return;
  }
  sif::info << "HK reply:" << std::endl;
  arrayprinter::print(replyBuf.data(), recvLen);
}

ReturnValue_t I2cTestClass::i2cWrite(int fd, uint8_t* data, size_t len) {
  if (write(fd, data, len) != static_cast<ssize_t>(len)) {
    sif::error << "Failed to write to I2C bus" << std::endl;
    sif::error << "Error " << errno << ": " << strerror(errno) << std::endl;
    return HasReturnvaluesIF::RETURN_FAILED;
  }
  return HasReturnvaluesIF::RETURN_OK;
}

ReturnValue_t I2cTestClass::i2cRead(int fd, uint8_t* data, size_t len) {
  if (read(fd, data, len) != static_cast<ssize_t>(len)) {
    sif::error << "Failed to read from I2C bus" << std::endl;
    sif::error << "Error " << errno << ": " << strerror(errno) << std::endl;
    return HasReturnvaluesIF::RETURN_FAILED;
  }
  return HasReturnvaluesIF::RETURN_OK;
}