/*
 * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266
 */

#include "CC1101.h"

// default constructor
CC1101::CC1101(int8_t CSpin, int8_t MISOpin) : _CSpin(CSpin), _MISOpin(MISOpin)
{
  // SPI.begin(); // Done already by ESPEasy
  pinMode(_CSpin, OUTPUT);
} // CC1101

// default destructor
CC1101::~CC1101()
{} // ~CC1101

/***********************/

// SPI helper functions select() and deselect()
inline void CC1101::select(void) {
  digitalWrite(_CSpin, LOW);
}

inline void CC1101::deselect(void) {
  digitalWrite(_CSpin, HIGH);
}

void CC1101::spi_waitMiso()
{
  uint32_t maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds

  while (digitalRead(_MISOpin) == HIGH && millis() < maxWait) {
    yield();
  }
}

void CC1101::init()
{
  reset();
}

void CC1101::reset()
{
  deselect();
  delayMicroseconds(5);
  select();
  delayMicroseconds(10);
  deselect();
  delayMicroseconds(45);
  select();

  spi_waitMiso();
  SPI.transfer(CC1101_SRES);
  delay(10);
  spi_waitMiso();
  deselect();
}

uint8_t CC1101::writeCommand(uint8_t command)
{
  uint8_t result;

  select();
  spi_waitMiso();
  result = SPI.transfer(command);
  deselect();

  return result;
}

void CC1101::writeRegister(uint8_t address, uint8_t data)
{
  select();
  spi_waitMiso();
  SPI.transfer(address);
  SPI.transfer(data);
  deselect();
}

uint8_t CC1101::readRegister(uint8_t address)
{
  uint8_t val;

  select();
  spi_waitMiso();
  SPI.transfer(address);
  val = SPI.transfer(0);
  deselect();

  return val;
}

uint8_t CC1101::readRegisterMedian3(uint8_t address)
{
  uint8_t val, val1, val2, val3;

  select();
  spi_waitMiso();
  SPI.transfer(address);
  val1 = SPI.transfer(0);
  SPI.transfer(address);
  val2 = SPI.transfer(0);
  SPI.transfer(address);
  val3 = SPI.transfer(0);
  deselect();

  // reverse sort (largest in val1) because this is te expected order for TX_BUFFER
  if (val3 > val2) { val = val3; val3 = val2; val2 = val; } // Swap(val3,val2)

  if (val2 > val1) { val = val2; val2 = val1, val1 = val; } // Swap(val2,val1)

  if (val3 > val2) { val = val3; val3 = val2, val2 = val; } // Swap(val3,val2)

  return val2;
}

/* Known SPI/26MHz synchronization bug (see CC1101 errata)
   This issue affects the following registers: SPI status byte (fields STATE and FIFO_BYTES_AVAILABLE),
   FREQEST or RSSI while the receiver is active, MARCSTATE at any time other than an IDLE radio state,
   RXBYTES when receiving or TXBYTES when transmitting, and WORTIME1/WORTIME0 at any time.*/

// uint8_t CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType)
uint8_t /* ICACHE_RAM_ATTR */ CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType)
{
  uint8_t value1, value2;

  value1 = readRegister(address | registerType);

  // if two consecutive reads gives us the same result then we know we are ok
  do
  {
    value2 = value1;
    value1 = readRegister(address | registerType);
  }
  while (value1 != value2);

  return value1;
}

// registerType = CC1101_CONFIG_REGISTER or CC1101_STATUS_REGISTER
uint8_t CC1101::readRegister(uint8_t address, uint8_t registerType)
{
  switch (address)
  {
    case CC1101_FREQEST:
    case CC1101_MARCSTATE:
    case CC1101_RXBYTES:
    case CC1101_TXBYTES:
    case CC1101_WORTIME1:
    case CC1101_WORTIME0:
      return readRegisterWithSyncProblem(address, registerType);

    default:
      return readRegister(address | registerType);
  }
}

void CC1101::writeBurstRegister(uint8_t address, uint8_t *data, uint8_t length)
{
  uint8_t i;

  select();
  spi_waitMiso();
  SPI.transfer(address | CC1101_WRITE_BURST);

  for (i = 0; i < length; i++) {
    SPI.transfer(pgm_read_byte(&(data[i])));
  }
  deselect();
}

void CC1101::readBurstRegister(uint8_t *buffer, uint8_t address, uint8_t length)
{
  uint8_t i;

  select();
  spi_waitMiso();
  SPI.transfer(address | CC1101_READ_BURST);

  for (i = 0; i < length; i++) {
    buffer[i] = SPI.transfer(0x00);
  }

  deselect();
}

// wait for fixed length in rx fifo
uint8_t CC1101::receiveData(CC1101Packet *packet, uint8_t length)
{
  uint8_t rxBytes = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_STATUS_REGISTER);

  rxBytes = rxBytes & CC1101_BITS_RX_BYTES_IN_FIFO;

  // check for rx fifo overflow
  if ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE) == CC1101_MARCSTATE_RXFIFO_OVERFLOW)
  {
    writeCommand(CC1101_SIDLE); // idle
    writeCommand(CC1101_SFRX);  // flush RX buffer
    writeCommand(CC1101_SRX);   // switch to RX state
  }
  else if (rxBytes == length)
  {
    readBurstRegister(packet->data, CC1101_RXFIFO, rxBytes);

    // continue RX
    writeCommand(CC1101_SIDLE); // idle
    writeCommand(CC1101_SFRX);  // flush RX buffer
    writeCommand(CC1101_SRX);   // switch to RX state

    packet->length = rxBytes;
  }
  else
  {
    // empty fifo
    packet->length = 0;
    writeCommand(CC1101_SIDLE); // idle
    writeCommand(CC1101_SFRX);  // flush RX buffer
    writeCommand(CC1101_SRX);   // switch to RX state
  }

  return packet->length;
}

// This function is able to send packets bigger then the FIFO size.
void CC1101::sendData(CC1101Packet *packet)
{
  uint8_t index = 0;
  uint8_t txStatus, MarcState;
  uint8_t length;

  writeCommand(CC1101_SIDLE); // idle

  txStatus = readRegisterWithSyncProblem(CC1101_TXBYTES, CC1101_STATUS_REGISTER);

  // clear TX fifo if needed
  if (txStatus & CC1101_BITS_TX_FIFO_UNDERFLOW)
  {
    writeCommand(CC1101_SIDLE); // idle
    writeCommand(CC1101_SFTX);  // flush TX buffer
  }

  writeCommand(CC1101_SIDLE);   // idle

  // determine how many bytes to send
  length = (packet->length <= CC1101_DATA_LEN ? packet->length : CC1101_DATA_LEN);

  writeBurstRegister(CC1101_TXFIFO, packet->data, length);

  writeCommand(CC1101_SIDLE);

  // start sending packet
  writeCommand(CC1101_STX);

  // continue sending when packet is bigger than 64 bytes
  if (packet->length > CC1101_DATA_LEN)
  {
    index += length;

    // loop until all bytes are transmitted
    while (index < packet->length)
    {
      // check if there is free space in the fifo
      uint32_t maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds

      while ((txStatus = (readRegisterMedian3(CC1101_TXBYTES | CC1101_STATUS_REGISTER) & CC1101_BITS_RX_BYTES_IN_FIFO)) >
             (CC1101_DATA_LEN - 2) &&
             millis() < maxWait) {
      }

      // calculate how many bytes we can send
      length = (CC1101_DATA_LEN - txStatus);
      length = ((packet->length - index) < length ? (packet->length - index) : length);

      // send some more bytes
      for (int i = 0; i < length; i++) {
        writeRegister(CC1101_TXFIFO, packet->data[index + i]);
      }

      index += length;
    }
  }

  // wait until transmission is finished (TXOFF_MODE is expected to be set to 0/IDLE or TXFIFO_UNDERFLOW)
  uint32_t maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds

  do
  {
    MarcState = (readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE);

    //		if (MarcState == CC1101_MARCSTATE_TXFIFO_UNDERFLOW) Serial.print(F("TXFIFO_UNDERFLOW occured in sendData() \n"));
  }
  while ((MarcState != CC1101_MARCSTATE_IDLE) && (MarcState != CC1101_MARCSTATE_TXFIFO_UNDERFLOW) &&
         millis() < maxWait);
}
