#include "MBPeripherals.h"

void defaultExtIntIsr(void){}

MBPeripherals::MBPeripherals(const BoardConfig &boardConfig, void (*extIntIsr)(void)): _boardConfig(boardConfig)
{
  if(!extIntIsr)
    _extIntIsr = &(defaultExtIntIsr); //We provide a default ISR
}

uint8_t MBPeripherals::init()
{
  uint8_t toReturn(0);
  //We initialize needed IOs :
  pinMode(_boardConfig.ExternalInt, INPUT);
  pinMode(_boardConfig.LDOEnable, OUTPUT);
  _3V3PowerRail(OFF);
  pinMode(_boardConfig.SwitchVSensEnable, INPUT_PULLUP);
  pinMode(_boardConfig.BattVSensEnable, OUTPUT);
  digitalWrite(_boardConfig.BattVSensEnable, LOW);
  
  //Unused pins are set as inputs with internal pullup enabled to reduce power consumption during sleep
  pinMode(0,INPUT_PULLUP);
  //pinMode(1,INPUT_PULLUP); TX pin for serial
  pinMode(3, INPUT_PULLUP);
  // Set to input pull up because it is its rest state
  pinMode(_boardConfig.SwitchVSensEnable, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);

  //RFM95W control pins, these are set in the deinitExternalPeripherals method.
  //pinMode(6, INPUT_PULLUP);
  //pinMode(8, INPUT_PULLUP);
  //pinMode(10, INPUT_PULLUP);
  //pinMode(11, INPUT_PULLUP);
  //pinMode(12, INPUT_PULLUP);
  //pinMode(13, INPUT_PULLUP);

  //These pins are all unused
  pinMode(A2, INPUT_PULLUP);
  pinMode(A3, INPUT_PULLUP);
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);
  pinMode(A6, INPUT_PULLUP);
  pinMode(A7, INPUT_PULLUP);

  //Defines the RFM95W IOs for the class (does not physically set pin states)
  _RFM95W.setPins(_boardConfig.RFMCs, _boardConfig.RFMRst, _boardConfig.RFMDI0);

  //We check that every external devices are responding
  _3V3PowerRail(ON);
  toReturn |= initExternalPeripherals();
  deinitExternalPeripherals();
  _3V3PowerRail(OFF);

  attachInterrupt(digitalPinToInterrupt(_boardConfig.ExternalInt), _extIntIsr, FALLING);

  return toReturn;
}

uint8_t MBPeripherals::initExternalPeripherals(void)
{
  uint8_t toReturn(0);
  
  toReturn |= _RFM95W.begin(LORA_BAND);
  
  return toReturn;
}

void MBPeripherals::deinitExternalPeripherals(void)
{
  //Let's properly turn the deinitialize the RFM95W module
  _RFM95W.end();
  //Don't forget to set associated radio IO's to input WITHOUT PULLUPS resistors (current leak when the module is not powered...)
  pinMode(_boardConfig.RFMRst, INPUT);
  pinMode(_boardConfig.RFMDI0, INPUT);
  pinMode(_boardConfig.RFMCs, INPUT);
  pinMode(_boardConfig.MOSI, INPUT);
  pinMode(_boardConfig.MISO, INPUT);
  pinMode(_boardConfig.SCK, INPUT);
}

float MBPeripherals::batteryVoltage(void)
{
  //We close the voltage divider bridge and we do the measurement
  digitalWrite(_boardConfig.BattVSensEnable, HIGH);
  int rawBatteryValue = analogRead(_boardConfig.BattAnalogVSens);
  digitalWrite(_boardConfig.BattVSensEnable, LOW);
  
  return float(rawBatteryValue) * ADC_QUANTUM * VOLTAGE_DIV_COEFF; 
}

void MBPeripherals::_3V3PowerRail(State state)
{
  digitalWrite(_boardConfig.LDOEnable, state);
  if(state) //We let some time for the voltage to stabilize on the rail. 
    delay(10);
}

LoRaClass &MBPeripherals::getRadio(void) {return _RFM95W;}

void MBPeripherals::applyRadioConfig(int spreadingFactor, bool enableCRC, int txPower)
{
  _RFM95W.setSpreadingFactor(spreadingFactor);
  enableCRC ? _RFM95W.enableCrc() : _RFM95W.disableCrc();
  _RFM95W.setTxPower(txPower);
}

MAILBOX_EVENT_e MBPeripherals::readMailboxEvent(void)
{
  pinMode(_boardConfig.SwitchVSensEnable, OUTPUT);
  digitalWrite(_boardConfig.SwitchVSensEnable, HIGH);
  int analogValue = analogRead(_boardConfig.SwitchAnalogVSens);
  digitalWrite(_boardConfig.SwitchVSensEnable, LOW);
  pinMode(_boardConfig.SwitchVSensEnable, INPUT_PULLUP);

  if(analogValue < 526)
    return MAILBOX_EVENT_e::MAILBOX_LETTER;
  else if(analogValue >= 526 && analogValue < 711)
    return MAILBOX_EVENT_e::MAILBOX_PACKAGE;
  //The collected event is not used for the moment.
  else if(analogValue >= 711 && analogValue < 1000)
    return MAILBOX_EVENT_e::MAILBOX_TEST;
  else return MAILBOX_EVENT_e::MAILBOX_UNKNOWN;
}

void MBPeripherals::maskExtInt(void)
{
  detachInterrupt(digitalPinToInterrupt(_boardConfig.ExternalInt));
}

void MBPeripherals::unmaskExtInt(void)
{
  attachInterrupt(digitalPinToInterrupt(_boardConfig.ExternalInt), _extIntIsr, FALLING);
}
