//-----------------------------------------------------------------------------
///
/// \brief  nec infrared input
///
///         http://www.mikrocontroller.net/articles/IRMP#NEC_.2B_extended_NEC
///         Reference IR Control was "Vivanco UR 89", code "165" (Toshiba TV)
///         All keys used, address is 0x40;
///         CoCo: code "10508" (Toshiba TV), address is 0x40
///
/// \date   20141102
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


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


//---Protocol NEC---------------------
//
// 1 Start-Bit + 32 Daten-Bits + 1 Stop-Bit
//
// normal: 8 Adress-Bits + 8 invertierte Adress-Bits + 8 Kommando-Bits + 8 invertierte Kommando-Bits
// extend: 16 Adress-Bits + 8 Kommando-Bits + 8 invertierte Kommando-Bits
//
// Start-Bit 	9000µs Puls, 4500µs Pause
// 0-Bit 	560µs Puls, 560µs Pause
// 1-Bit 	560µs Puls, 1690µs Pause
// Stop-Bit 	560µs Puls
// Tasten-Wiederholung 	9000µs Puls, 2250µs Pause, 560µs Puls, ~100ms Pause
//
// Tolerance: 150µs
// Timeout  : 100ms->25ms


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


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

#include <biwak/ir_input.h>
//#include <biwak/debug.h>
#include <lepto/log.h>
#include <lepto/time.h>          // USEC_PER_SEC


//---Defines / Declarations----------------------------------------------------

// Start + 32Data + Stop
#define NEEDED_SLOPES_PRESS      (34*2)
#define NEEDED_SLOPES_REPEAT     (2*2)
#define TIMEOUT                  (25*USEC_PER_MSEC)
#define PULSE_TOLERANCE          (560/2)

enum EIrSlope
{
   eIrSlopeStartBit,
   eIrSlopeStartPause,
   eIrSlopeStartPauseRepeat,
   eIrSlopeBitPulse,
   eIrSlopeBit0Pause=eIrSlopeBitPulse,
   eIrSlopeBit1Pause,
   eIrSlopeInvalid,
};


hrtimer_t irPulseWidth[]=
{
   [eIrSlopeStartBit]=9000,
   [eIrSlopeStartPause]=4500,
   [eIrSlopeStartPauseRepeat]=2250,
   [eIrSlopeBitPulse]=560,
   //[eIrSlopeBit0Pause]=560,
   [eIrSlopeBit1Pause]=1690,
   [eIrSlopeInvalid]=0
};


enum EIrDecoderState
{
   eIrDecoderStateInvalid,
   eIrDecoderStateStarted,
   eIrDecoderStateIterate,
};


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


CIrDecoder::CIrDecoder()
{
   eKeyMode=eKeyModeinvalid;

   setSlopesPerFrame(NEEDED_SLOPES_PRESS+1);
   setPulseTolerance(PULSE_TOLERANCE);
   setProtocolTimeout(TIMEOUT);
}


/// \brief  Slope interrupt
///
///         ...
//void CIrDecoder::decodeFrame() /*[virtual override]*/
void CIrDecoder::slope(bool value)
{
   CInputDecoderRecorder::slope(value);
   //EIrDecoderState irDecoderState=eIrDecoderStateInvalid;

   if( slopeCount>NEEDED_SLOPES_PRESS )
   {
      resetSlopes(true, EError::MaxSlopes);
   }

   switch(slopeCount)
   {
      case 2: // start bit
         if( decodePulsePauseWidth(0, irPulseWidth)  != eIrSlopeStartBit )
         {
            resetSlopes(true,EError::Decoding, 0);
         }
         break;

      case 3: // check for repeat
         if( decodePulsePauseWidth(1, irPulseWidth)  == eIrSlopeStartPauseRepeat )
            eKeyMode=eKeyModeRepeat;
         else
         if( decodePulsePauseWidth(1, irPulseWidth) == eIrSlopeStartPause )
            eKeyMode=eKeyModePress;
         else
         {
            //hrtimer_t value=decodePulsePauseWidth(1, irPulseWidth);
            resetSlopes(true, EError::Decoding, 0);
         }
         break;
   }

   if( (eKeyMode==eKeyModePress) && (slopeCount==NEEDED_SLOPES_PRESS) )
   {
      decodeKey();
      resetSlopes(false);
   }
   else if( (eKeyMode==eKeyModeRepeat) && (slopeCount==NEEDED_SLOPES_REPEAT) )
   {
      decodeKey();
      resetSlopes(false);
   }

   return;
}


/// \brief  Le contructeur
///
CIrInput::CIrInput(EGpioPeripheral _eGpioPeripheral, EGpioPin _eGpioPin)
   :CInput(_eGpioPeripheral, _eGpioPin, gpioModeInputInterrupt)
   ,CRing<TIrKey>(5)
{
   setVolatile(true);

   //setDecoding(EDecodeMode::Record);
   setDecoder( this );

}


CIrInput::~CIrInput()
{

}


//
int CIrDecoder::decodeData(int startSlope, int bites, int &data)
{
   int slope1;
   int slope2;
   int bitValue;

   data=0;

   for(int i1=0; i1<bites; i1++)
   {
      slope1=decodePulsePauseWidth(startSlope++, irPulseWidth);
      slope2=decodePulsePauseWidth(startSlope++, irPulseWidth);
      bitValue=0;

      if(slope1!=eIrSlopeBitPulse)
         return(-1);
      switch(slope2)
      {
         case eIrSlopeBit0Pause: bitValue=0;break;
         case eIrSlopeBit1Pause: bitValue=1;break;
         default: return(-1);break;
      }
      // LSB
      //data|= bitValue << ( (bites-1)-i1);
      data|= bitValue << i1;
   };

   return(0);
}


int CIrDecoder::decodeStop(int startSlope)
{
   int slope;
   int sta=0;

   slope=decodePulsePauseWidth(startSlope++, irPulseWidth);
   if(slope!=eIrSlopeBitPulse)
      sta=-1;

   return(sta);
}


void CIrDecoder::decodeKey()
{
   int sta=0;
   int data;
   uint8_t address;
   uint8_t addressInv;
   uint8_t command;
   uint8_t commandInv;

   switch(eKeyMode)
   {
      case eKeyModeRepeat:
         // lHint << "decode Repeat";
         break;
      case eKeyModePress:
         // lHint << "decode Press";
         sta=decodeData( (1)*2, 32, data);
         if(sta>=0)
            ;//lHint << "Data: " << sta;
         else
            lHint << "Data: Error";

         sta=decodeStop((1+32)*2);
         if(sta<0)
            lHint << "Data: Error";
         address=(data>>0)&0xFF;
         addressInv=(data>>8)&0xFF;
         if ( ( addressInv ^ address ) != 0xFF )
            lHint << "Logical: A Error " << address << ";" << addressInv;
         command=(data>>16)&0xFF;
         commandInv=(data>>24)&0xFF;
         if ( ( commandInv ^ command ) != 0xFF)
            lHint << "Logical: C Error " << command << ";" << commandInv;

         //lHint << "Address: " << address << "; Command: " << command;
         if(!sta)
         {
            //putEntry( TIrKey{address, command} );
            sink(address, command);
         }
         break;
      default:
         lHint << "decode err";
         break;
   }
}


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