//----------------------------------------------------------------------------
///
/// \brief  an RFID chip reader
///
///         Decoding is done with prefetched slopes so driver is interrupt
///         driven.
///
///         Software was tested with EM4095 chip on Mikroelektronika board.
///
///         VCC: 5V
///         MOD: NC
///         RDY/CLK: NC
///         SHD: GND
///         OUT: CRfid GPIO
///
/// \author Maximilian Seesslen <mes@seesslen.net>
/// \date   [20151221] file created
///
//----------------------------------------------------------------------------


//---Documentation-------------------------------------------------------------



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


#define RFID_SLOPE_TIMEOUT        (0x160)       // tSlope was constantly 0x16
#define RFID_SLOPES_PER_FRAME     ((64*2) +3)   // 64 Bits, wors case 2 slopes,
                                                // 3 times to have one complete one


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


//---Own------------------------------

#include <biwak/gpio.h>
#include <biwak/uart.h>
#include <biwak/biwak.h>         // exception();
#include <biwak/lltools.h>       // setExti()
#include <biwak/rfid.h>          // getHRTimer()
#include <lepto/log.h>
#include <stdlib.h>

//#include <biwak/platform.h>


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


/// \brief  Le contructeur
///
///         Just for CInput()
CRfid::CRfid(EGpioPeripheral _eGpioPeripheral, EGpioPin _eGpioPin)
   :CInput(_eGpioPeripheral, _eGpioPin, gpioModeInput)
{

}


/// \brief  Le destructeur
CRfid::~CRfid()
{
   // Nothing to do here
   return;
}

CRfidDecoder::CRfidDecoder()
{
   setSlopesPerFrame(RFID_SLOPES_PER_FRAME);
   setProtocolTimeout( 1500 );      // ca 1/2 Frame
   //setPulseTolerance(125/8);   // 15usec, SuccessPercent 0x5d
   //setPulseTolerance(125/5);   // 25usec, high error qhote
   //setPulseTolerance(128/8);   // 16usec
   setPulseTolerance( (32/2) +1) ;
}

void CRfidDecoder::dumpSlopes()
{
   int slopeCount=getSlopeCount();

   for(int i1=0; i1 < slopeCount-1; i1++)
   {
      lHint << "#" << i1 << "/" << slopeCount << " pulseWidth: " << (long int)pulseWidth(i1);
      //delay(10);
      //pf.uart[0]->flushOutput();
   }

   return;
}

enum ESlopeDist
{
   eSlopeDistNone,
   eSlopeDistLarge,
   eSlopeDistSmall
};

enum ERfidState
{
   eRfidStateNone,
   eRfidStateHeaderStarted,
   eRfidStateDataStarted,
   eRfidStateDataExpectSmall,
   eRfidStateError,
   eRfidStateFinished,
};


void CRfidDecoder::decodeFrame() /*[virtual]*/
{
   // lHint << "RFID decode, " << slopeCount << " slopes.";
   int minDiff=0x1000, maxDiff=0;
   int tolerance;
   int cont=0;
   char data[11];
   int dataPos=0;
   int bitPos=0;
   static int decodeError=0;
   bool parity[11];

   for(int i1=0; i1 < slopeCount-1; i1++)
   {
      if(pulseWidth(i1) < minDiff)
         minDiff=pulseWidth(i1);
      if(pulseWidth(i1) > maxDiff)
         maxDiff=pulseWidth(i1);
   }
   tolerance=(minDiff * 2 ) / 3;

   ESlopeDist eSlopeDist=eSlopeDistNone;
   ERfidState eRfidState=eRfidStateNone;
   bool transitState=false;

   for(int i1=0; i1 < slopeCount-1; i1++)
   {
      if( abs ( pulseWidth(i1) - minDiff ) < tolerance )
         eSlopeDist=eSlopeDistSmall;
      else if( abs ( pulseWidth(i1) - maxDiff ) < tolerance )
         eSlopeDist=eSlopeDistLarge;
      else
      {
         #if 0
         lHint << "ERR tolerance in " << i1;
         lHint << "minDiff:" << minDiff;
         lHint << "maxDiff: " << maxDiff;
         lHint << "tolerance: " << tolerance;
         #endif
         eRfidState=eRfidStateError;
      }

      switch(eRfidState)
      {
         case eRfidStateNone:
            cont=0;
            if( eSlopeDist == eSlopeDistLarge )
               if( slopeValue(i1) )
                  eRfidState=eRfidStateHeaderStarted;
            break;

         case eRfidStateHeaderStarted:
            if( eSlopeDist == eSlopeDistSmall )
               cont++;
            else
               eRfidState=eRfidStateNone;
            if(cont>=16)
            {
               eRfidState=eRfidStateDataStarted;
               bitPos=0;
               dataPos=0;
               memset(data, 0, sizeof(data));
               transitState=true;
            }
            break;

         case eRfidStateDataStarted:
            switch( eSlopeDist )
            {
               case ( eSlopeDistSmall ):
                  eRfidState=eRfidStateDataExpectSmall;
                  break;
               case ( eSlopeDistLarge ):
                  transitState = !transitState;
                  break;
               default:
                  break;
            }
            if(bitPos==4)
            {
               parity[dataPos]=transitState;
               dataPos++;
               bitPos=0;
               if(dataPos==11)
                  eRfidState=eRfidStateFinished;
            }
            else
               data[dataPos]|=(transitState)<<bitPos++;
            break;
         case eRfidStateDataExpectSmall:
            if(eSlopeDist!=eSlopeDistSmall)
               eRfidState=eRfidStateError;

            eRfidState=eRfidStateDataStarted;
            break;
         case eRfidStateError:
            break;

         case eRfidStateFinished:
            break;

         default:
            exception("Unknown state", eRfidState);
            break;
      }

      if ( (eRfidState==eRfidStateError) || (eRfidState==eRfidStateFinished) )
         break;
   }

   if(eRfidState==eRfidStateFinished)
   {
      bool checked=true;
      for(int i1=0; i1<10; i1++)
      {
          char cross=0;
         for(int i2=0; i2<4; i2++)
            cross+=( data[i1]>>i2 ) & 1;
         if( (cross & 1) != parity[i1])
         {
            lHint << "E: " << i1;
            checked=false;
         }
      }

      for(int i2=0; i2<4; i2++)
      {
         char cross=0;
         for(int i1=0; i1<10; i1++)
         {
            cross+=( data[i1]>>i2 ) & 1;
         };
         if( (cross & 1) != ( (data[10]>>i2) & 1 ) )
         {
            lHint << "+E: " << i2 << " C=" << (int)cross << " VALUE=" << (int)data[10];
            lHint << " " << (int)(cross&1) << " != " << (int)((data[10]>>i2)&1);
            checked=false;
         }
      }

      if(parity[10])
         checked=false;

      if(!checked)
         lHint << "Parity Error!";

      version=data[0] | (data[1] << 4);
      id=0;
      for(int i1=2; i1<10; i1++)
         id|=(data[i1] << (i1*4) );

      decodeError=0;
      lHint << "Version: " << (int) version << "; Id: " << id;
   }
   else
   {
      decodeError++;
      //lHint << "DE " << eRfidState;
#if 0
      if(decodeError>(0x10/2))
      {
         version=0;
         id=0;
         lHint << "Version: " << (int) version << "; Id: " << id;
         decodeError=0;
      }
#endif
   }
   return;
}


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