//----------------------------------------------------------------------------
//
// \brief   Hoperf RFM69CW radio transmit module
//
//          https://www.hoperf.com/api/downfile?title=UserGuide&uid=DDD3E650-1365-488A-9C01-54C068F6B890
//          https://www.hoperf.com/api/downfile?title=&uid=B0C71D2A-5616-4AC9-84ED-AD8BF6383A06
//          Registers:    p.60 - p.74
//          SPI-Protokol: p.44
//          Pinning:      p.09
//
// \date    20230628
// \author  Maximilian Seesslen <mes@seesslen.net>
//
//----------------------------------------------------------------------------


//---Includes-----------------------------------------------------------------


#include <stdint.h>
#include <biwak/rfm69cw.hpp>
#include <biwak/led.hpp>
#include <lepto/log.h>
#include <lepto/lepto.h>
#include <lepto/print.h>
#include <biwak/rfm69cw/regEntries.hpp>

#define LENGTH_INCLUDED    0
#define NO_DESC            1

enum ECommand
{
   eCommandLoad=    2,
   eCommandRead=     4,
   eCommandIncrease= 6,
   eCommandBeginProgramming= 8,
   eCommandEndProgramming= 0xE,
};


/*
enum EValues
{
   // eRegPacketConfig1
   eCrcAutoClearOff  = 0x8,
   eCrcOn            = 0x10,
   eDcFreeNone       = 0x00,
   eDcFreeManchester = 0x20,
   
   // eRegSyncConfig
   eSyncOn           = 0x80,
   eSyncSize4B       = 0x18,
   eSyncTolNone      = 0,
};
*/


//---Defines------------------------------------------------------------------



//---Implementation-----------------------------------------------------------


bool handleInterrupt=false;

//#define MD  usleep(100);
#define MD

CRfm69cw::CRfm69cw( CSpi &_spi, SRfmConfig config, CLed *_trafficLed)
   : m_spi( _spi )
   , m_outputCs( config.eGpioPeripheralCs, config.eGpioPinCs )
   , m_gpioDIO0( config.eGpioPeripheralDIO0, config.eGpioPinDIO0, config.configDataDIO0 )
   , m_gpioDIO1( config.eGpioPeripheralDIO1, config.eGpioPinDIO1, config.configDataDIO1 )
   , m_gpioDIO2( config.eGpioPeripheralDIO2, config.eGpioPinDIO2, config.configDataDIO2 )
   , m_gpioDIO3( config.eGpioPeripheralDIO3, config.eGpioPinDIO3, config.configDataDIO3 )
   , m_gpioDIO5( config.eGpioPeripheralDIO5, config.eGpioPinDIO5 )
   , m_outputReset( config.eGpioPeripheralReset, config.eGpioPinReset )
   , trafficLed( _trafficLed )
   , m_receiveBuffer( 4 )
   , m_transmitBuffer( 4 )
    , m_config(RFM69_CONFIG_SIZE)
{
   deactivateCs();
   m_spi.setDataSize(8);
   m_spi.writeData( (uint32_t)0 );

   data=NULL;
   
   m_gpioDIO0.enableInterrupt(true, true);
   
   #if defined ( BIWAK_USE_INPUT_SIGNAL )
   m_gpioDIO0.interruptSignal.connect(this, &CRfm69cw::int0);
   m_gpioDIO1.interruptSignal.connect(this, &CRfm69cw::int1);
   m_gpioDIO2.interruptSignal.connect(this, &CRfm69cw::int2);
   m_gpioDIO3.interruptSignal.connect(this, &CRfm69cw::int3);
   #else
   #endif
   
   m_outputReset.setValue( RFM69_RESET_ACTIVE );
   usleep( 5 * USECS_PER_MSEC );
   m_outputReset.setValue( RFM69_RESET_INACTIVE );
   usleep( 5 * USECS_PER_MSEC );
   
   //rawDump();
   readConfig();
   initConfig();
   state=eRfmStateNone;
   
   setRxMode();
   //setStbyMode();
}


void CRfm69cw::activateCs()
{
   m_outputCs.setValue( RFM69_CS_ACTIVE );
   MD;
}


void CRfm69cw::deactivateCs()
{
   m_outputCs.setValue( RFM69_CS_INACTIVE );
   MD;
}


void CRfm69cw::readConfig( )
{
   uint8_t wData[2];
   bool empty=true;

   activateCs();

   // Accessing the fifo does not automatically increase the address
   m_config.data()[RegFifo]=0;
   static_assert( RegFifo == 0, "Assumed fifo is at address 0");
   wData[0]=RFM69_READ_FLAG | ( RegFifo + 1 );

   m_spi.writeData(wData, 1);
   m_spi.readData(&m_config.data()[1], RFM69_CONFIG_SIZE-1 );
   
   for( int address=0; address < RFM69_CONFIG_SIZE; address++)
   {
      if(m_config.data()[address])
      {
         empty=false;
         break;
      }
   }

   if(empty)
   {
      lCritical("Mod dead");
      configValid=false;
      lFatal( "MD", -1);
   }
   else
   {
      configValid=true;
   }

   deactivateCs();
}


uint8_t CRfm69cw::readConfig( int address)
{
   uint8_t rData[0x2];
   uint8_t wData[0x2];
   int sta;

   activateCs();

   wData[0]= RFM69_READ_FLAG | address;
   wData[1]=0;
   lAssert( address < RFM69_CONFIG_SIZE );

   sta=m_spi.writeReadData(wData, rData, 2);
   lAssert( sta == 0 );
   m_config.data()[address]=rData[1];

   deactivateCs();
   
   return(rData[1]);
}


void CRfm69cw::writeConfig( int address, uint8_t value )
{
   uint8_t rData[0x2];
   uint8_t wData[0x2];
   int sta;
   
   activateCs();
   
   wData[0]= RFM69_WRITE_FLAG | address;
   wData[1]= value;
   
   lAssert( address < RFM69_CONFIG_SIZE );
   
   m_config.data()[address] = value;
   sta=m_spi.writeReadData(wData, rData, 2);
   
   lAssert( sta == 0 );
   
   deactivateCs();
   
   return;
}


int CRfm69cw::updatedGetConfig( const SRegEntry& regEntry )
{
   readConfig( regEntry.addr );
   return( m_config.getConfig( regEntry ) );
}


int CRfm69cw::getConfig( const SRegEntry& regEntry )
{
   return( m_config.getConfig( regEntry ) );
}


uint8_t CRfm69cw::setConfig( const SRegEntry& regEntry, uint8_t value )
{
   return( m_config.setConfig( regEntry, value ) );
}

void CRfm69cw::setWriteConfig( const SRegEntry& regEntry, uint8_t value )
{
   uint8_t wData[0x2];
   int address;
   
   activateCs();
   address=regEntry.addr ;
   wData[0] = address | RFM69_WRITE_FLAG;
   wData[1] = setConfig( regEntry, value );

   m_spi.writeData(wData, 2);
   deactivateCs();

   return;
}


void CRfm69cw::writeConfig( )
{
   uint8_t wData[0x2];

   activateCs();
   
   wData[0]= RFM69_WRITE_FLAG | ( RegFifo+1 );
   m_spi.writeData(wData, 1 );
   m_spi.writeData(&m_config.data()[RegFifo+1], RFM69_CONFIG_SIZE - ( RegFifo + 1 ) );
   
   deactivateCs();
}

void CRfm69cw::writeConfig( ERegs reg )
{
   uint8_t wData[0x2];
   
   activateCs();
   
   wData[0]= RFM69_WRITE_FLAG | reg;
   m_spi.writeData(wData, 1 );
   m_spi.writeData(&m_config.data()[reg], 1 );
   
   deactivateCs();
   
}
/*
int CRfm69cw::setConfig( int address, int bitPos, int bitSize, int value)
{
   uint32_t mask=(((uint32_t)-1)>>( (sizeof(uint32_t)*8) - bitSize)) << bitPos;
   config[address] = ( config[address] & (~mask)) | (value << bitPos);
   return( config[address] );
}


int CRfm69cw::getConfig( int address, int bitPos, int bitSize)
{
   uint32_t mask=(((uint32_t)-1)>>( (sizeof(uint32_t)*8) - bitSize)) << bitPos;
   
   static_assert( (((uint32_t)-1)>>( (sizeof(uint32_t)*8) - 8)) << 0 == 0xFF );
   
   uint32_t value=( config[address] & mask) >> bitPos;
   return( value );
}
*/

void CRfm69cw::dumpConfig( )
{
   if(!configValid)
   {
      lWarning("CNF INV");
      //return;
      readConfig();
   }
#if 0
   lInfo("Dumping config:");
   unsigned int eBitField=0;
   for( eBitField=0; eBitField<eBitFieldEnd; eBitField++ ){
      /*
      lInfo("   %s (reg 0x%X): %d (raw reg 0x%X)", bitFieldDescriptors[eBitField].name
               , bitFieldDescriptors[eBitField].addr
               , getConfig((EBitField)eBitField)
               , config[ bitFieldDescriptors[eBitField].addr ] );
      */
      #if ! defined ( NO_DESC )
         lInfo("   %s (reg 0x%X): %d (raw reg 0x%X)", bitFieldDescriptors[eBitField].name
      #else
         lInfo("   %s (reg 0x%X): %d (raw reg 0x%X)", "?"
      #endif
            , bitFieldDescriptors[eBitField].addr
            , getConfig((EBitField)eBitField)
            , config[ bitFieldDescriptors[eBitField].addr ] );
      biwakEventLoop();
   }
#endif
}

void CRfm69cw::dumpStaticConfig( )
{
   printf( "   constexpr uint8_t staticConfig[]= {" );
   for(int i1=0; i1< RFM69_CONFIG_SIZE ; i1++)
   {
      if( ( i1 % 0x10 ) == 0 )
      {
         printf("\n       ");
      }
      printf("0x%02X, ", m_config.data()[i1]);
   }
   printf("\n   };\n");
}

#if 0
void CRfm69cw::rawDump( )
{
   char cdata;
   
   readConfig();
   printf("Dump:\n");
   printf("    | -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F\n");
   printf("----|------------------------------------------------\n");
   for(int i1=0; i1< RFM69_CONFIG_SIZE >> 4; i1++)
   {
      printf(" %X- | ", i1);
      for(int i2=0; i2<0x10; i2++)
      {
         int address=(i1*0x10)+i2;
         if(address)
         {
            cdata=readConfig( (i1*0x10)+i2 );
         }
         else
         {
            cdata=0;
         }
         printf("%02X ", cdata);
         //printf("0x%02X, ", cdata);
      }
      printf("\n");
   }
   return;
}
#endif

int CRfm69cw::initConfig()
{
   setStbyMode();
#if ! CONFIG_RFM69CW_USE_STATIC_DATA
   m_config.setConfig( RESequencerOff, ESequencerOff::SequencerOn);
   m_config.setConfig( REListenOn, EListenOn::ListenOff);
   m_config.setConfig( REMode, EMode::Standby);
   
   m_config.setConfig( REDataMode, EDataMode::PacketMode);
   m_config.setConfig( REModulationType, EModulationType::FSK);
   m_config.setConfig( REModulationShaping, EModulationShaping::None);
   // 150kbps still has a lot of retranmissionf in canbridge
   m_config.setConfig( REBitRateMsb, EBitRateMsb::_115kbps);
   m_config.setConfig( REBitRateLsb, EBitRateLsb::_115kbps);
   
   
   m_config.setConfig( REFdevMsb, EFdevMsb::_5000Hz );
   m_config.setConfig( REFdevLsb, EFdevLsb::_5000Hz );
   m_config.setConfig( REFrfMsb, EFrfMsb::_868Mhz );
   m_config.setConfig( REFrfMid, EFrfMid::_868Mhz );
   m_config.setConfig( REFrfLsb, EFrfLsb::_868Mhz );
   m_config.setConfig( REDccFreq, 0b010 );
   
   m_config.setConfig( RERxBwMant, ERxBwMant::M16);
   m_config.setConfig( RERxBwExp, 2);
   m_config.setConfig( REDio0Mapping, 0);
   m_config.setConfig( REClkOut, EClkOut::Off);
   
   m_config.setConfig( REFifoOverrun, 1);       // writing to this bit ensures that the FIFO & status flags are reset
   m_config.setConfig( RERssiThreshold, 220);

   m_config.setConfig( RESyncOn, ESyncOn::SyncOn);
   m_config.setConfig( REFifoFillCondition, EFifoFillCondition::Auto);
   m_config.setConfig( RESyncSize, ESyncSize::_2Bytes);
   m_config.setConfig( RESyncTol, 0);
   m_config.setConfig( RESyncValue1, 0x2D);
   m_config.setConfig( RESyncValue2, 0x3C);
   
   m_config.setConfig( REPacketFormat, EPacketFormat::Variable);
   //m_config.setConfig( REDcFree, EDcFree::Manchaster);
   m_config.setConfig( REDcFree, EDcFree::None);
   m_config.setConfig( RECrcOn, ECrcOn::CrcOn);
   m_config.setConfig( RECrcAutoClearOff, ECrcAutoClearOff::AutoClearOn);
   m_config.setConfig( REAddressFiltering, EAddressFiltering::AddressBroadCast);
   m_config.setConfig( REPayloadLength, 66);
   m_config.setConfig( RETxStartCondition, ETxStartCondition::FifoNotEmpty);
   m_config.setConfig( REFifoThreshold, 0x04);
   m_config.setConfig( REInterPacketRxDelay, EInterPacketRxDelay::Delay2Bits);
   m_config.setConfig( REAutoRxRestartOn, EAutoRxRestartOn::RestartOn);
   m_config.setConfig( REAesOn, EAesOn::AesOff);
   m_config.setConfig( REContinuousDagc, EContinuousDagc::ImprovedMargin_LowBeta0);
#else
   constexpr uint8_t staticConfig[]= {
       0x00, 0x10, 0x00, 0x01, 0x16, 0x03, 0x33, 0xD9, 0x00, 0x00, 0x01, 0x00, 0x02, 0x92, 0xF5, 0x20, 
       0x24, 0x9F, 0x09, 0x1A, 0x40, 0xB0, 0x7B, 0x9B, 0x08, 0x42, 0x8A, 0x40, 0x80, 0x06, 0x31, 0x00, 
       0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x07, 0x00, 0x10, 0xDC, 0x00, 0x00, 0x00, 0x03, 0x88, 0x2D, 
       0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x42, 0x00, 0x00, 0x00, 0x84, 0x12, 0x00, 0x00, 
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 
       0x00, 0x01, 0x88, 0x08, 0x00, 0x00, 0x01, 0x00, 0x1B, 0x09, 0x55, 0x80, 0x70, 0x33, 0xCA, 0x08, 
       0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x70, 0x00, 0x13, 0x16, 0x19, 0x1B, 0x00, 0x04, 0x0C, 0x30, 
   };
   memcpy( m_config.data(), staticConfig, 0x70);
   
#endif
   writeConfig( );
   
   setWriteConfig( REAfcStart, 1 );
   setWriteConfig( REFeiStart, 1 );
   setWriteConfig( RERssiStart, 1 );

   return(0);
}

void CRfm69cw::setAddress(int address)
{
   setWriteConfig( RENodeAddress, address );
}

void CRfm69cw::setTxMode()
{
   setWriteConfig( REMode, EMode::Transmit);
   state=eRfmStateTransmiting;
}


void CRfm69cw::setRxMode()
{
   setWriteConfig( REMode, EMode::Receive );
   state=eRfmStateReceiving;
}


void CRfm69cw::setStbyMode()
{
   setWriteConfig( REMode, EMode::Standby);
   state=eRfmStateStandby;
}

void CRfm69cw::listenMode()
{
   m_config.setConfig( REListenAbort, 1 );
   m_config.setConfig( REListenOn, EListenOn::ListenOff);
   writeConfig( ERegs::RegOpMode );
   usleep(1000);
   m_config.setConfig( REListenAbort, 0 );
   m_config.setConfig( REListenOn, EListenOn::ListenOn);
   writeConfig( ERegs::RegOpMode );
   
   setStbyMode();
}

void CRfm69cw::leaveListenMode()
{
   m_config.setConfig( REListenAbort, 1 );
   m_config.setConfig( REListenOn, EListenOn::ListenOff);
   writeConfig( ERegs::RegOpMode );
   usleep(1000);
   m_config.setConfig( REListenAbort, 0 );
   writeConfig( ERegs::RegOpMode );
}

int CRfm69cw::sendPackage(int targetId, /*EPacketType type,*/ int len , const void *data)
{
   static SRawPacket packet;
   static  uint32_t sendCounter=0;
   uint8_t address;
   static_assert( offsetof(struct SRawPacket, data) == 0, "Fooo" );
   
   leaveListenMode();
   
   // Add the magic/biwak stuff
   uint8_t _len=len+offsetof(struct SRawPacket, data);
   // Address-Byte + premagic + postmagic
   _len+= 1 + sizeof(PRE_MAGIC) + sizeof(POST_MAGIC);
   
   uint8_t target=targetId;

   uint8_t *wData=(uint8_t *)&packet;
   
   if ( getState() == eRfmStateTransmiting )
   {
      lFatal("Already transmitting");
      return(0);
   }

   setStbyMode();
   
   setConfig( RERestartRx,  1); // EBitField::restartRx, EBitFieldValues::eRestartRx);
   setWriteConfig(REAutoRxRestartOn, EAutoRxRestartOn::RestartOn);
   
   //lAssert( (unsigned)len < sizeof(packet.data), "package too big" );
   //memset(packet.data, 0x55, sizeof(packet.data) );
   memcpy(packet.data, data, MIN( (uint32_t)len, RFM69CW_MAX_PAYLOAD ) );

   address = RegFifo | RFM69_WRITE_FLAG;
   //packet.counter=sendCounter;
   //packet.nodeId=nodeId;
   //packet.targetNetId=0;
   //packet.type = type;
   //_len=MAX(16, _len);

   activateCs();
   m_spi.writeData( &address, 0x1 );
   #if LENGTH_INCLUDED
      _len++;
   #endif

   m_spi.writeData( &_len, 0x1 );
   m_spi.writeData( &target, 0x1 );
   m_spi.writeData( &PRE_MAGIC, sizeof(PRE_MAGIC) );
   #if LENGTH_INCLUDED
         _len--;
   #endif
         
   // Address-Byte + premagic + postmagic
   _len-= 1 + sizeof(PRE_MAGIC) + sizeof(POST_MAGIC);
   
   #if 1
      // MAX: depends on "fifoThresh"
      for(int i1=0; i1 < MAX(4, _len); i1++)
      {
         m_spi.writeData((uint8_t*)&wData[i1], 0x1);
      }
   #else
      m_spi.writeData((uint8_t*)&wData[0], PAYLOAD_SIZE);
   #endif
      m_spi.writeData( &POST_MAGIC, sizeof(POST_MAGIC) );
   deactivateCs();
      
   sendCounter++;
   m_hrSenStartTime=hrNow();
   setTxMode();
   
   return(0);
}


int CRfm69cw::receivePackage()
{
   static SRawPacket packet;
   
   uint8_t *rData=(uint8_t *)&packet;
   uint8_t addr= RegFifo | RFM69_READ_FLAG;
   uint8_t length;
   uint8_t target;
   magic_t postMagic;
   magic_t preMagic;

   if( updatedGetConfig( RECrcOk ) == false )
   {
      // don't warn via heartbeat led
      lInfo("CRC err-flag set");
   }
   
   activateCs();
   m_spi.writeData((uint8_t *)&addr, 0x1);
   m_spi.readData(&length, 0x1);
   m_spi.readData(&target, 0x1);
   m_spi.readData( (uint8_t*)&preMagic, sizeof(preMagic));

   #if LENGTH_INCLUDED
      length--;
   #endif
   // Address-Byte + Post-magic + Pre-magic
   length-= 1 + sizeof(postMagic) + sizeof(preMagic);

   //lAssert(length<=sizeof(packet));
   memset(rData, 0xFF, sizeof(packet));
   
   #if 1
      for(int i1=0; i1 < MIN(length,(int)sizeof(packet)) ; i1++)
      {
         m_spi.readData((uint8_t *)&rData[i1], 0x1);
      }
      m_spi.readData((uint8_t *)&postMagic, sizeof(postMagic) );
   #else
      m_spi.readData((uint8_t *)&rData[0], PAYLOAD_SIZE);
   #endif
      
   packet.payloadLength=length - offsetof(struct SRawPacket, data);
   packet.target=target;
   deactivateCs();
   if( ( preMagic == PRE_MAGIC ) && ( postMagic == POST_MAGIC ) )
   {
      if(m_receiveBuffer.pushable())
      {
         m_receiveBuffer.putEntry( packet );
      }
      else
      {
         lWarning("RFM BOF (%d)", m_receiveBuffer.isDataAvailable());
      }
   }
   else
   {
      if( preMagic != PRE_MAGIC )
      {
         lWarning("Mgk wrg: 0x%X; len: %d", preMagic, length);
      }
      else
      {
         lWarning("Postfix wrg: 0x%X; len: %d", postMagic, length);
      }
   }
   
   //setRxMode();
   m_receiveTimer=lrNow();
   
   return(0);
}


void CRfm69cw::int0( bool value )
{
   //lInfo("INT 0: %d", value);
   switch( getState() )
   {
      case eRfmStateReceiving:
      case eRfmStateStandby:
      {
         if(value==1)
         {
            receivePackage();
            //m_receiveInterrupt = true;
            listenMode();
         }
         break;
      }
      case eRfmStateTransmiting:
      {
         if(value==1)
         {
            listenMode();
         }
         break;
      }
      default:
      {
         lWarning("UH STA: %d", getState() );
      }
   }
}


void CRfm69cw::int1( bool value )
{
   // This is also called
   //lInfo("INT 1: %d", value);
}


void CRfm69cw::int2( bool value )
{
   //lInfo("INT 2: %d", value);
}


void CRfm69cw::int3( bool value )
{
   lInfo("INT 3: %d", value);
}


void CRfm69cw::int4( bool value )
{
   lFatal("Int 4");
}


void CRfm69cw::int5( bool value )
{
   lFatal("Int 5");
}


void CRfm69cw::eventLoop()
{
   if( receiveInterrupt() )
   {
      receivePackage();
      m_receiveInterrupt = false;
   }

   /*
   if( ( state == eRfmStateReceiving ) && ( lrElapsedSeconds(m_receiveTimer) >= 2 ) )
   {
         if( updatedGetConfig( EBitField::payloadReady ) )
         {
            lWarning("Payload present; missed IRQ");
            receivePackage();
         }
   }
   */
}


bool CRfm69cw::getPllLock()
{
   return( updatedGetConfig( REPllLock ) );
}


bool CRfm69cw::canSend() const
{
   return ( ( getState() != eRfmStateTransmiting ) && ( m_gpioDIO2.getValue() == 0 ) );
}

//---fin----------------------------------------------------------------------
