#ifndef _BIWAK_INPUT_DECODER_H_
#define _BIWAK_INPUT_DECODER_H_
//----------------------------------------------------------------------------
///
/// \brief  Just a simple Input
///
///         See implementation for further information.
///
/// \date   20121107
/// \author Maximilian Seesslen <development@seesslen.net>
///
//----------------------------------------------------------------------------


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


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

#include <biwak/input.hpp>
#include <lepto/log.h>
#include <lepto/lepto.h>            // MAX/MIN
#include <biwak/exception.h>

#define defaultProtocolTimeout   (25*USECS_PER_MSEC)
#define defaultPulseTolerance    (150)

#if ! defined ( HRTIMER_INVAL )
#define HRTIMER_INVAL   0
#endif

//---Declaration--------------------------------------------------------------


class CInputDecoderRecorder
      :public CInputDecoder
{
public:
   enum class EError: int
   {
         None=0,
         Decoding=3,
         Timeout=5,
         MaxSlopes=6,
         Unknown=100
   };

private:
   int maxSlopes=0;
   bool m_frameAvailable=false;
   int m_slopesPerFrame=0;
   bool errornous=false;
   EError lastErrorCode=EError::None;
   int lastErrorData=0;
   volatile hrtimer_t *slopeTimeStamp=nullptr;
   //hrtimer_t errorValue;
   hrtimer_t protocolTimeout=defaultProtocolTimeout;
   hrtimer_t pulseTolerance=defaultPulseTolerance;
   int totalFrameCount=0;

protected:
   int slopeCount=0;
   int errorFrameCount=0;

public:
   ~CInputDecoderRecorder()
   {
      if(slopeTimeStamp)
         delete[] slopeTimeStamp;
   }
   void setSlopesPerFrame(int slopesPerFrame);
   int getSlopesPerFrame()
   {
      return(m_slopesPerFrame);
   }
   void setPulseTolerance(hrtimer_t _pulseTolerance)
   {
      pulseTolerance=_pulseTolerance;
   }
   int getSlopeCount()
   {
      return(slopeCount);
   }

   void resetSlopes(bool error=false, EError errorCode=EError::Unknown, int errorData=0)
   {
      //lHint << "### Reset";
      //FREEZE;
     if( error && (!errornous) )
     {
        errorFrameCount++;
        lastErrorCode=errorCode;
        lastErrorData=errorData;
        errornous=true;
     }
     slopeCount=0;
     m_frameAvailable=false;
   }
   bool frameAvailable()
   {
      return(m_frameAvailable);
   }
   void resetErrornous()
   {
      errornous=false;
   }
   int getErrorFrameCount()
   {
      return(errorFrameCount);
   }
   hrtimer_t getSlopeTimeStamp(int index) const
   {
      return(slopeTimeStamp[index]);
   }
   virtual void slope(bool value) override
   {
      hrtimer_t hr;

#if defined ( BOOTLOADER )
   hr=0;
#else
    hr=getHrTimerValue();
#endif
    if(slopeCount)
   {
      if( 0 // ( hr - slopeTimeStamp[slopeCount-1] )
        || ( hrtValueToUsec( hr - slopeTimeStamp[slopeCount-1] ) >= protocolTimeout ) )
      {
         // It actually is an error becaouse previous frame was not decauded
         // since it was not complete
         //errorValue=hrtValueToUsec( hr - slopeTimeStamp[slopeCount-1] );
         resetSlopes(true, EError::Timeout);
         resetErrornous();
      }
      if( slopeCount>=maxSlopes )
      {
         resetSlopes(true, EError::MaxSlopes);
      }
   }

   if(maxSlopes)
   {
      slopeTimeStamp[slopeCount]=hr;
      slopeCount++;
   }

   if(slopeCount>=m_slopesPerFrame)
   {
      totalFrameCount++;
      //if( flags & EInputFlagDecodeFrame )
      {
            decodeFrame();
            resetSlopes(false);
            resetErrornous();
      }
      /*if( flags & EInputFlagSingleShotFrame )
      {
            setDecoder( nullptr );
            m_frameAvailable=true;
      }
      */
   }

   }
   virtual void decodeFrame()
   {
      // Do nothing by default
   }
   int getTotalFrameCount()
   {
      return(totalFrameCount);
   }
   int pulseWidth(int slope) const;
   int slopeValue(int slope) const;
   int decodePulsePauseWidth(int slope, hrtimer_t pulsePauseWidth[]);
   void setProtocolTimeout(hrtimer_t _protocolTimeout)
   {
      protocolTimeout=_protocolTimeout;
   }
};


class CInputDecoderSlopeCounter
      :public CInputDecoder
{
private:
   lrtimer_t   m_startTimer;
#if 1
   hrtimer_t   m_previousTimer;
   int         m_maxHz;
   int         m_minHz;
#endif
   int         m_counter;
   int         m_nowHz;
#if 0
   int         m_sumHz=0;
   int         m_convergenceValue;
#endif
public:

   void reset()
   {
      m_startTimer=lrNow();
      m_previousTimer=0; // HRTIMER_INVAL;
      m_counter=0;

#if 0
      m_maxHz=0;
      m_minHz=0;

      m_nowHz=0;
#endif
      //m_sumHz=0;
      //m_convergenceValue=0;
   }
   virtual void slope(bool value) override
   {
      hrtimer_t now=hrNow();

      //static bool lastValue=false;

      //if( (!value) || ( value && lastValue ) )
      if( !value )
      {
         return;
      }

      //lastValue=value;
#if 0
      if( /*( m_previousTimer != HRTIMER_INVAL ) && */ value )
      {
         int nowValue=USECS_PER_SEC/hrtValueToUsec(now-m_previousTimer);
#if 0
         if( ! ( (nowValue>1000) && (nowValue<30000) ) )
         {
            // unplausible value
            // Bad idea beacuse an long pause would freeze the system until
            // reset is called
            return;
         }
#endif
#if 0
         m_nowHz=nowValue;
         if(m_nowHz>0)
         {
            m_maxHz=(m_maxHz==0)?m_nowHz:MAX(m_maxHz, m_nowHz);
            m_minHz=(m_minHz==0)?m_nowHz:MIN(m_minHz, m_nowHz);
         }
         else
         {
            //lHint << "Error: unplausible timer value: " << nowHz;
         }
#endif
      }
#endif

      if(value)
      {
         m_counter++;
         if(!m_counter)
         {
            // "Overrun"
            exception( );
         }
      }
      else
      {
         //lHint << "Haehh???";
         //m_counter=0;
      }
      /*
      if( (m_nowHz>1000) && (m_nowHz<30000))
      {
            if(!m_convergenceValue)
            {
               m_convergenceValue=m_nowHz;
            }
            else
            {
               m_convergenceValue=(m_nowHz+m_convergenceValue) / 2;
            }
      }

      m_sumHz+=m_nowHz;
      */

      m_previousTimer=now;
      return;
   }
   int getHz( )
   {
      int hz=(m_counter*MSECS_PER_SEC)/lrElapsedMSeconds(m_startTimer);
      //int hz=m_counter/(lrElapsedMSeconds(m_startTimer)/MSECS_PER_SEC);
      return(hz);
   }
   int getNow( )
   {
      return(m_nowHz);
   }
#if 1
   int getMax()
   {
      return(m_maxHz);
   }
   int getMin()
   {
      return(m_minHz);
   }
#endif
#if 0
   int getMid()
   {
      return( ( m_minHz + m_maxHz ) / 2 );
   }
   int getRMid()
   {
      return( m_sumHz / m_counter );
   }
   int getDerivation()
   {
      return( m_maxHz - m_minHz );
   }
   int getConvergence()
   {
      return( m_convergenceValue );
   }
#endif
   int getCounter()
   {
      return(m_counter);
   }
};

class CInputDecoderTrivialSlopeCounter
      :public CInputDecoder
{
private:
   lrtimer_t   m_startTimer;
   int         m_counter;
public:

   void reset()
   {
      m_counter=0;
   }
   virtual void slope(bool value) override
   {
      if( !value )
      {
         return;
      }

      if( !m_counter )
      {
         m_startTimer=lrNow();
      }

      m_counter++;

      return;
   }
   int getHz( bool doReset = false )
   {
      int hz=(m_counter*MSECS_PER_SEC)/lrElapsedMSeconds(m_startTimer);

      if(doReset)
         reset();

      return(hz);
   }
   int getCounter()
   {
      return(m_counter);
   }
};

class CInputDecoderUptime
      :public CInputDecoder
{
private:
   hrtimer_t   m_risingTime=0; //HRTIMER_INVAL;
   hrtimer_t   m_startTime=0; //HRTIMER_INVAL;
   hrtimer_t   m_endTime=0; //HRTIMER_INVAL;
   int m_value=0;
   int m_minValue=0;
   int m_maxValue=0;
   int m_valueSum=0;
   int m_sumCounter=0;

public:
   virtual void slope(bool value) override
   {
      hrtimer_t stamp=getHrTimerValue();

      if(value)
      {
         m_risingTime=stamp;
         return;
      }
      if(m_risingTime!=HRTIMER_INVAL)
      {
         m_endTime=stamp;
         //m_value=elapsedUSeconds(m_risingTime, m_endTime);

         m_value=m_endTime-m_risingTime;

         //lAssert( (m_value>5) && (m_value<30000) );
         m_maxValue=(m_maxValue==0)?m_value:MAX(m_maxValue, m_value);
         m_minValue=(m_minValue==0)?m_value:MIN(m_minValue, m_value);
         m_valueSum+=m_value;
         m_sumCounter++;
      }
      //m_risingTime=hrNow();
      return;
   }

   void reset(bool value=false)
   {
      if(value)
      {
         m_risingTime=hrNow();
      }
      else
      {
         m_risingTime=HRTIMER_INVAL;
      }
      m_startTime=hrNow();
      m_value=0;
      m_maxValue=0;
      m_minValue=0;
      m_sumCounter=0;
      m_valueSum=0;
      return;
   }

   int getRoundupValue()
   {
      return(m_valueSum/(m_sumCounter/1));
   }

   /* \brief   Get round up value of an single low pulse
    *
    *          This value is not as accurate as getRoundupValue() because the
    *          acutal slope-down-time is not measured but calculated. Errors
    *          at start and end are not eliminated.
    */
   int getLowValue()
   {
      hrtimer_t diff=hrNow()-m_startTime;
      return( (diff-m_valueSum)/(m_sumCounter/1));
   }
   int getLastValue()
   {
      return(m_value);
   }
   int getTotalUptimeValue()
   {
      return(m_valueSum);
   }
   int getTotalValue()
   {
      return( getHrTimerValue()-m_startTime );
   }
   int getMin()
   {
      return(m_minValue);
   }
   int getMax()
   {
      return(m_maxValue);
   }
};


class CInputDecoderPeriod
      :public CInputDecoder
{
private:
   hrtimer_t   m_risingTime=HRTIMER_INVAL;
   hrtimer_t   m_startTime=HRTIMER_INVAL;
   hrtimer_t   m_endTime=HRTIMER_INVAL;
   int m_value=0;
#if 0
   int m_minValue=0;
   int m_maxValue=0;
#endif
   int m_valueSum=0;
   int m_sumCounter=0;

public:
   virtual void slope(bool value) override
   {
      hrtimer_t stamp=getHrTimerValue();

      // Only evaluate rising slopes
      if(!value)
      {
         return;
      }

      if(m_risingTime!=HRTIMER_INVAL)
      {
         m_endTime=stamp;
         m_value=m_endTime-m_risingTime;
#if 0
         m_maxValue=(m_maxValue==0)?m_value:MAX(m_maxValue, m_value);
         m_minValue=(m_minValue==0)?m_value:MIN(m_minValue, m_value);
#endif
         m_valueSum+=m_value;
         m_sumCounter++;
      }
      m_risingTime=stamp;
      return;
   }

   void reset()
   {
      m_risingTime=HRTIMER_INVAL;
      m_startTime=hrNow();
      m_value=0;
#if 0
      m_maxValue=0;
      m_minValue=0;
#endif
      m_sumCounter=0;
      m_valueSum=0;
      return;
   }

   int getRoundupValue____()
   {
      return(m_valueSum/(m_sumCounter/1));
   }

   int getRoundupUsec()
   {
      return( hrtValueToUsec ( m_valueSum/(m_sumCounter/1)) );
   }

   int getLastValue()
   {
      return(m_value);
   }
   int getTotalUptimeValue()
   {
      return(m_valueSum);
   }
   int getTotalValue()
   {
      return( getHrTimerValue()-m_startTime );
   }
#if 0
   int getMin()
   {
      return(m_minValue);
   }
   int getMax()
   {
      return(m_maxValue);
   }
#endif
};


class CInputDecoderTrivialPeriod
      :public CInputDecoder
{
private:
   hrtimer_t   m_startTime=HRTIMER_INVAL;
   hrtimer_t   m_endTime=HRTIMER_INVAL;
   int m_sumCounter;

public:
   virtual void slope(bool value) override
   {
      // Only evaluate rising slopes
      if(value)
      {
         m_sumCounter++;
      }
      else
      {
         //lHint << "-";
      }
      return;
   }

   void reset()
   {
      m_startTime=hrNow();
      m_sumCounter=0;
      return;
   }
   int getPeriod()
   {
      hrtimer_t start = m_startTime;
      hrtimer_t stop = hrNow();
      hrtimer_t period = ( stop - start ) / m_sumCounter;
      //return( m_sumCounter );
      if(!m_sumCounter)
      {
         return(1);
      }
      return( hrtValueToUsec( period ) );
      return( period );
   }
};


//---fin----------------------------------------------------------------------
#endif // ? ! _BIWAK_INPUT_DECODER_H_
