/**
 * @file sx1262.cpp
 * @brief implementation file for the SX1262 transceiver class.
 * @see sx1262.h
 * @see { @link https://www.semtech.fr/products/wireless-rf/lora-connect/sx1262 }
 * for detailed information about the SX1262.  
 */

#include "sx1262.h"
#include "logger.h"
#include "board.h"

extern logging::Logger logger;

/**
 * @brief Begin the SPI commmunication with the SX1262 transceiver.
 */
void Sx1262::beginSPI() const
{
  SPI.begin(Sx1262::getSclk(), Sx1262::getMiso(), Sx1262::getMosi(), Sx1262::getCs());
  pinMode(Sx1262::getCs(), OUTPUT);
  digitalWrite(Sx1262::getCs(), HIGH);
  pinMode(Sx1262::getBusy(), INPUT);
};

/**
 * @brief Get the reset pin.
 * @return The reset pin number.
 */
uint8_t Sx1262::getRst() const
{
  return _rst;
};

/**
 * @brief Get the specified DIO pin.
 * @param n The DIO pin index (1-3). Default is 1.
 * @return The DIO pin number.
 */
uint8_t Sx1262::getDio(uint8_t n) const
{ 
  uint8_t dioPin;
  switch(n)
  {
    case 1: dioPin = _dio1; break;
    case 2: dioPin = _dio2; break;
    case 3: dioPin = _dio3; break;
    default: dioPin = _dio1; break;
  }
  return dioPin;
};

/**
 * @brief Get the SPI clock pin.
 * @return The SPI clock pin number.
 */
uint8_t Sx1262::getSclk() const
{
  return _sclk;
};

/**
 * @brief Get the SPI MOSI pin.
 * @return The SPI MOSI pin number.
 */
uint8_t Sx1262::getMosi() const
{
  return _mosi;
};

/**
 * @brief Get the SPI MISO pin.
 * @return The SPI MISO pin number.
 */
uint8_t Sx1262::getMiso() const
{
  return _miso;
};

/**
 * @brief Get the chip select pin.
 * @return The chip select pin number.
 */
uint8_t Sx1262::getCs() const
{
  return _cs;
};

/**
 * @brief Get the busy pin.
 * @return The busy pin number.
 */
uint8_t Sx1262::getBusy() const
{
  return _busy;
};


/**
 * @brief Reset the Sx1262 device.
 */
void Sx1262::reset() const
{
  for (uint32_t delay = 0; delay < 0xFFF; delay++)
  {
    /// The reset pin operates with inverted logic
    digitalWrite(_rst, LOW);
  }
  for (uint32_t delay = 0; delay < 0xFFFFF; delay++)
  {
    digitalWrite(_rst, HIGH);
  }
};

/**
 * @brief Read a register value.
 * @param reg The register address.
 */
uint8_t Sx1262::regRead(uint16_t reg) const
{
  /**
   * Standard method to read data from a device at a specified address via SPI.
   * pareil, on a un adressage en 16 bits donc on split en 2 adresses sur 8 bits
   */
  uint8_t ret;
  digitalWrite(_cs, LOW);             ///< Lower Chip Select pin
  SPI.transfer(SX126X_READ_REGISTER); ///< Write the register address with a read-enabled flag
  SPI.transfer((reg >> 8) & 0xFF);    ///< Write the register address bit [15:8]
  SPI.transfer(reg & 0xFF);           ///< Write the register address bit [7:0]
  // read SPI.transfert until it equals 0x00
  if (SPI.transfer(0x00) == 0xAA) {
    ret = SPI.transfer(0x00); ///< Read the value at the register address
  }
  else {
    ret = 0x00;
  }
  digitalWrite(_cs, HIGH);            ///< Raise Chip Select pin
  return ret;
};

/**
 * @brief Write a value to a register.
 * @param reg The register address.
 * @param val The value to write.
 */ 
void Sx1262::regWrite(uint16_t reg, uint8_t val) const
{
  /**
   * Standard method to write data to a device at a specified address via SPI.
   * on a une adresse sur 16 bits, on va donc split en 2 adresses sur 8 bits
   */
  digitalWrite(_cs, LOW);   ///< Lower Chip Select pin
  SPI.transfer(SX126X_WRITE_REGISTER);
  SPI.transfer((reg >> 8) & 0xFF);  ///< Write the register address bit [15:8]
  SPI.transfer(reg & 0xFF);         ///< Write the register address bit [7:0]
  SPI.transfer(val);        ///< Write the value at the register address
  digitalWrite(_cs, HIGH);  ///< Raise Chip Select pin
};

void Sx1262::setOpMode(uint8_t opcode, uint8_t *payload, uint8_t size) const
{
  digitalWrite(_cs, LOW);
  SPI.transfer(opcode);
  for (uint8_t i = 0; i < size; i++) {
    SPI.transfer(payload[i]);
  }
  digitalWrite(_cs, HIGH);

  // parce que pendant moins de 1us busy pin est à 0 après que le CS soit à 1
  // le while qui suit est donc inutile ... :)
  usleep(10);

  // on attend que le busy pin soit à 0, temps de traîtement tout ça
  // 8.3.1 BUSY Control Line in datasheet SX1261-2
  while (digitalRead(_busy) == HIGH) {
    usleep(10);
  }
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "setOpMode with opcode 0x%02X", opcode);
  for (uint8_t i=0; i < size; i++) {
    USBSerial.print("0x");
    USBSerial.println(payload[i], HEX);
    USBSerial.print(" ");
  }
};

/*
 * @brief Method to read the Status Command Communication
 * 11.5 Status Commands in datasheet SX1261-2
 * @param opcode The opcode of the status command
 * @return The status
 * 
 * on aura le Status byte en position 1 tout le temps pour chaque opcode
 * les autres bytes sont les réponses fonction de l'opcode
 * on va donc aprser le StatusByte pour vérifier que tout s'est bien passé
 * puis on va lire le reste de la réponse
 * 
 */
uint8_t Sx1262::getStatusCommand(uint8_t opcode) const
{
  digitalWrite(_cs, LOW);
  SPI.transfer(opcode);
  uint8_t statusByte = SPI.transfer(0x00);
  switch (opcode) {

    case (SX126X_GET_STATUS): {
      digitalWrite(_cs, HIGH);
      break;
    }
    
    case (SX126X_GET_RX_BUFFER_STATUS): {
      uint8_t rxBufferStatus = SPI.transfer(0x00);
      uint8_t rxBufferPayloadLenghtRx = SPI.transfer(0x00);
      uint8_t rxBufferRxStartBufferPointer = SPI.transfer(0x00);
      digitalWrite(_cs, HIGH);
      break;
    }

    case (SX126X_GET_PKT_STATUS): {
      uint8_t rxStatus = SPI.transfer(0x00);
      uint8_t rssiSync = SPI.transfer(0x00);
      uint8_t rssiAvg = SPI.transfer(0x00);
      digitalWrite(_cs, HIGH);
      break;
    }

    case (SX126X_GET_RSSI_INST): {
      uint8_t rssiValue = SPI.transfer(0x00);
      digitalWrite(_cs, HIGH);
      break;
    }

    case (SX126X_GET_STATS): {
      uint8_t statsPktReceived2 = SPI.transfer(0x00);
      uint8_t statsPktReceived1 = SPI.transfer(0x00);
      uint8_t statsPktError2 = SPI.transfer(0x00);
      uint8_t statsPktError1 = SPI.transfer(0x00);
      uint8_t NbPktLengthError2 = SPI.transfer(0x00);
      uint8_t NbPktLengthError1 = SPI.transfer(0x00);
      digitalWrite(_cs, HIGH);
      break;
    }
  }

  // Le 1er byte de réponse c'est le statusCode
  // on va parser tout ça
  uint8_t cmdStatus = 0;
  uint8_t cmdChipMode = 0;
  cmdStatus = (statusByte & 0x0E) >> 1;
  cmdChipMode = (statusByte & 0x70) >> 4;
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "cmdStatus : 0x%02X", cmdStatus);
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "cmdChipMode : 0x%02X", cmdChipMode);

  //on parse le status byte pour vérifier que tout s'est bien passé
  if (cmdStatus == 0x02) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "SX1262", "cmdStatus : Data is available to host");
  else if (cmdStatus == 0x03) logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, "SX1262", "cmdStatus : Command timeout");
  else if (cmdStatus == 0x04) logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "SX1262", "cmdStatus : Command processing error");
  else if (cmdStatus == 0x05) logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "SX1262", "cmdStatus : Failure to execute command");
  else if (cmdStatus == 0x06) logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "cmdStatus : Command TX done");
  else return -1; 
  
  // on parse le mode du chip pour vérifier que tout s'est bien passé
  if (cmdChipMode == 0x02) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "SX1262", "cmdChipMode : STBY_RC ");
  else if (cmdChipMode == 0x03) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "SX1262", "cmdChipMode : STBY_XOSC ");
  else if (cmdChipMode == 0x04) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "SX1262", "cmdChipMode : FS");
  else if (cmdChipMode == 0x05) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "SX1262", "cmdChipMode : RX");
  else if (cmdChipMode == 0x06) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "SX1262", "cmdChipMode : TX");
  else return -1;

  return 0;
};

/**
 * @brief Set the transceiver in SLEEP mode
 * Table 13-2: Sleep Mode Definition in datasheet
 */
void Sx1262::SetSleep() const
{
  uint8_t *buffer = new uint8_t[1];
  buffer[0] = 0x00;
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetSleep with opcode 0x%02X and parameter 0x%02X", SX126X_SET_SLEEP, buffer[0]);
  setOpMode(SX126X_SET_SLEEP, buffer, sizeof(buffer));
};

/**
 * @brief Set the transceiver in STANDBY_RC mode
 * Table 13-3: Standby_RC Mode Definition in datasheet
 */
void Sx1262::SetStandby() const
{
  uint8_t *buffer = new uint8_t[1];
  buffer[0] = 0x00;
  setOpMode(SX126X_SET_STANDBY, buffer , 1);
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetStandby with opcode 0x%02X and parameter 0x%02X", SX126X_SET_STANDBY, buffer[0]);  
  getStatusCommand(SX126X_GET_STATUS);

};

void Sx1262::SetFS() const
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetFS with opcode 0x%02X", SX126X_SET_FS);
  uint8_t *buffer = new uint8_t[1];
  setOpMode(SX126X_SET_FS, buffer, 0);
};

/**
 * @brief Set the Packet Type of the transceiver
 * Table 13-4-2: Packet Type Definition in datasheet
 * 
 */
void Sx1262::SetPacketType(uint8_t type) const
{
  uint8_t *parameter = new uint8_t[1];
  parameter[0] = type;
  setOpMode(SX126X_SET_PKT_TYPE, parameter, 1);
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetPacketType with opcode 0x%02X and parameter 0x%02X", SX126X_SET_PKT_TYPE, parameter[0]);
};

/*
 * @brief Get the Packet Type of the transceiver
 * Table 13-4-3: Packet Type Definition in datashee
 */
void Sx1262::GetPacketType() const
{
  // define an array of uint8_t of 2 bytes
  uint8_t *parameter = new uint8_t[2];
  parameter[0] = 0x00;
  parameter[1] = 0x00;
  setOpMode(SX126X_GET_PKT_TYPE, parameter, 2);
};


/**
 * @brief Set frequency
 * Table 13-36: SetRfFrequency SPI Transaction in datasheet
*/
void Sx1262::SetRfFrequency(uint32_t freq) const
{
  uint8_t *buffer = new uint8_t[4];
  buffer[0] = (freq >> 24) & 0xFF;
  buffer[1] = (freq >> 16) & 0xFF;
  buffer[2] = (freq >> 8) & 0xFF;
  buffer[3] = freq & 0xFF;
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetRfFrequency with opcode 0x%02X and parameters :", SX126X_SET_RF_FREQUENCY);
  USBSerial.print("0x");
  USBSerial.print(buffer[0], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[1], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[2], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[3], HEX);
  setOpMode(SX126X_SET_RF_FREQUENCY, buffer, 4);
}

/**
 * @brief Set PaConfig
 * Table 13-20: SetPaConfig SPI Transaction in datasheet
 */
void Sx1262::SetPaConfig(uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut) const
{
  uint8_t *buffer = new uint8_t[4];
  buffer[0] = paDutyCycle;
  buffer[1] = hpMax;
  buffer[2] = deviceSel;
  buffer[3] = paLut;
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetPaConfig with opcode 0x%02X and parameters :", SX126X_SET_PA_CFG);
  USBSerial.print("0x");
  USBSerial.print(buffer[0], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[1], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[2], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[3], HEX);
  USBSerial.println();
  setOpMode(SX126X_SET_PA_CFG, buffer, 4);
}

/**
 * @brief Set TxParams
 * Table 13-40: SetTxParams SPI Transaction
 */
void Sx1262::SetTxParams(uint8_t power, uint8_t rampTime) const
{
  uint8_t *buffer = new uint8_t[2];
  buffer[0] = power;
  buffer[1] = rampTime;
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetTxParams with opcode 0x%02X and parameters :", SX126X_SET_TX_PARAMS);
  USBSerial.print("0x");
  USBSerial.print(buffer[0], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[1], HEX);
  USBSerial.println();
  setOpMode(SX126X_SET_TX_PARAMS, buffer, 2);
}

/*
 * @brief Set BufferBaseAddress
  * Table 13-4-8: SetBufferBaseAddress SPI Transaction
  */
void Sx1262::SetBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) const
{
  uint8_t *buffer = new uint8_t[2];
  buffer[0] = txBaseAddress;
  buffer[1] = rxBaseAddress;
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "SetBufferBaseAddress with opcode 0x%02X and parameters :", SX126X_SET_BUFFER_BASE_ADDRESS);
  USBSerial.print("0x");
  USBSerial.print(buffer[0], HEX);
  USBSerial.print(" 0x");
  USBSerial.print(buffer[1], HEX);
  USBSerial.println();
  setOpMode(SX126X_SET_BUFFER_BASE_ADDRESS, buffer, 2);
}

/**
 * @brief Set WriteBuffer
 * Table 13-26: WriteBuffer SPI Transaction in datasheee
 */
void Sx1262::WriteBuffer(uint8_t *buffer, uint8_t size) const
{
  setOpMode(SX126X_WRITE_BUFFER, buffer, size);
}

/*
 * @brief Set ModulationParams
 */
void Sx1262::SetModulationParams(uint32_t bitrate, uint8_t pulseShape, uint8_t bandwidht, uint32_t fdev) const
{
  uint8_t *buffer = new uint8_t[8];
  buffer[0] = (bitrate >> 16) & 0xFF;
  buffer[1] = (bitrate >> 8) & 0xFF;
  buffer[2] = bitrate & 0xFF;
  buffer[3] = pulseShape;
  buffer[4] = bandwidht;
  buffer[5] = (fdev >> 16) & 0xFF;
  buffer[6] = (fdev >> 8) & 0xFF;
  buffer[7] = fdev & 0xFF;
  setOpMode(SX126X_SET_MODULATION_PARAMS, buffer, 8);
}

/*
 * @brief SetPacketParams
*/
void Sx1262::SetPacketParams(uint16_t preambleLength, uint8_t PreambleDetectorLength, uint8_t SyncWordLength, uint8_t AddrComp, uint8_t PacketType, uint8_t PayloadLength, uint8_t CRCType) const
{
  uint8_t *buffer = new uint8_t[8];
  buffer[0] = (preambleLength >> 8) & 0xFF;
  buffer[1] = preambleLength & 0xFF;
  buffer[2] = PreambleDetectorLength;
  buffer[3] = SyncWordLength;
  buffer[4] = AddrComp;
  buffer[5] = PacketType;
  buffer[6] = PayloadLength;
  buffer[7] = CRCType;
  setOpMode(SX126X_SET_PKT_PARAMS, buffer, 8);
}

/*
 * @brief SetDIO3AsTCXOCtrl
 */
void Sx1262::SetDIO3AsTCXOCtrl(uint8_t tcxoVoltage, uint32_t timeout) const
{
  uint8_t *buffer = new uint8_t[4];
  buffer[0] = tcxoVoltage;
  buffer[1] = (timeout >> 16) & 0xFF;
  buffer[2] = (timeout >> 8) & 0xFF;
  buffer[3] = timeout & 0xFF;
  setOpMode(SX126X_SET_DIO3_AS_TCXO_CTRL, buffer, 4);
}

/*
 * @brief SetTx
 */
void Sx1262::SetTx() const
{
  uint8_t *buffer = new uint8_t[0];
  setOpMode(SX126X_SET_TX, buffer, 0);
}