#ifndef _LEPTO_RING_H_
#define _LEPTO_RING_H_
//----------------------------------------------------------------------------
///
/// \file   ring.h
///
/// \brief  ring buffer
///
///         There must be only exist one producer and one consumer at the same
///         time
///
///         Empty:                        2 Entries:
///         [0 ] [1 ] [2 ] [3 ] [4 ]      [0 ] [1*] [2*] [3 ] [4 ]
///               |\--m_top                     |         \--m_top
///               \-- m_bottom                  \-- m_bottom
///
///         Full:
///         [0*] [1 ] [2*] [3*] [4*]      [0*] [1 ] [2 ] [3*] [4*]
///               |    \--m_bottom              \--m_top
///               \-- m_top                               \-- m_bottom
///                                        (5-3)+1 = 3
///
///         count:   top-bottom = count
///                  1-1=0
///                  3-2=2
///                  1-2=-1; 5-1=4;
///                  1-3=-2
///
/// \date   20141201
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


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


//---General--------------------------

#include <stdint.h>
#include <stdlib.h>        // malloc, free
#include <stdio.h>         // printf
#include <string.h>        // memset
#include <lepto/log.h>     //
#include <lepto/lepto.h>   // IS_ENABLED


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


#define CRING_DEFAULT_SIZE 0x0
#define LEPTO_RING_PUSH_CLEARANCE      1

#if ! defined(LEPTO_CONFIGURED)
   #error LEPTO_CONFIGURED not defined. The configuration header was probably not involved.
#endif


//---Definitions---------------------------------------------------------------


template <typename T>
class CRing
{
   private:
      T *m_buffers;
       
   protected:
      int m_bottomPos;
       
   private:
      int m_topPos;
      int m_maxEntries;
      #if IS_ENABLED( CONFIG_BIWAK_SUPPORT_VOLATILE_RING )
      bool m_volatile;
      #endif // ? BIWAK_SUPPORT_VOLATILE_RING
      
      //bool m_fatal;

   public:

      CRing(int maxEntries = CRING_DEFAULT_SIZE);
      ~CRing();
      void clear();
      bool isDataAvailable() const;
      T *bottomEntry() const;
      T *getEntry(int pos) const;
      T *topEntry() const;
      CRing<T>& operator << (const T value)
      {
         putEntry(value);
         return( *this );
      };
      bool putEntry(const T value);
      T popEntry();
      T &rawEntry(int pos) const
      {
         lAssert( pos <= m_maxEntries );
         return( m_buffers[pos] );
      }
      void dropEntry();
      const T *putString(const T *str);
      T crossfoot() const;

      #if IS_ENABLED( CONFIG_BIWAK_SUPPORT_VOLATILE_RING )
      /// \brief   set volatile attribute
      ///
      ///          If ringbuffer is volatile it will forgett older value when
      ///          new value is added.
      void setVolatile(bool _volatile)
      {
         m_volatile=_volatile;
      }
      #endif // ? BIWAK_SUPPORT_VOLATILE_RING
      
      /*
      void setFatal(bool fatal)
      {
         m_fatal=fatal;
      }
      */

      int getEntryCount() const
      {
         if( m_bottomPos == m_topPos )
         {
            return(0);
         }
         if( m_bottomPos < m_topPos )
         {
            return( m_topPos - m_bottomPos );
         }
         return ( ( m_maxEntries - m_bottomPos ) + m_topPos );
      }
      int getMaxEntries() const
      {
         return( m_maxEntries );
      }
      int getFreeCount()
      {
         return( getMaxEntries() - getEntryCount() );
      }
      void dump() const;
      void pushEntry()
      {
         int nextTop=( m_topPos + 1 ) % m_maxEntries;
         if( nextTop == m_bottomPos )
         {
            lFatal("-");
         }

         m_topPos = nextTop;
      }
      bool pushable()
      {
         int nextTop=( m_topPos + 1 ) % m_maxEntries;
         return ( nextTop != m_bottomPos );
      }
};


template <typename T>
CRing<T>::CRing(int maxEntries)
   :m_bottomPos(0)
   ,m_topPos(0)
   ,m_maxEntries(maxEntries)
   #if IS_ENABLED( CONFIG_BIWAK_SUPPORT_VOLATILE_RING )
   ,m_volatile(false)
   #endif
{
   m_buffers=new T[m_maxEntries];
   lAssert( m_buffers != nullptr );

   return;
};


template <typename T>
CRing<T>::~CRing()
{
   if( m_buffers )
   {
      delete[] m_buffers;
   }

   m_buffers=nullptr;

   return;
};


template <typename T>
void CRing<T>::clear()
{
   m_bottomPos=m_topPos=0;
}


template <typename T>
T CRing<T>::popEntry()
{
   T value{};

   if( isDataAvailable() )
   {
      value=m_buffers[ m_bottomPos ];
      dropEntry();
   }

   return(value);
};


template <typename T>
T *CRing<T>::bottomEntry() const
{
   T *value=nullptr;

   if(isDataAvailable())
   {
      value=&m_buffers[m_bottomPos];
   }

   return(value);
};


template <typename T>
T *CRing<T>::topEntry() const
{
   T *value=nullptr;

   //if( m_bottomPos != m_topPos )
   {
      value=&m_buffers[ m_topPos ];
   }

   return(value);
};


template <typename T>
void CRing<T>::dropEntry()
{
   if(isDataAvailable())
   {
      m_bottomPos = ( m_bottomPos + 1 ) % m_maxEntries;
   }
   else
   {
      lWarning("NE");
   }

   return;
};


template <typename T>
bool CRing<T>::putEntry(const T value)
{
   int nextTopPos= ( m_topPos + 1 ) % m_maxEntries;

   #if IS_ENABLED( CONFIG_BIWAK_SUPPORT_VOLATILE_RING )
   if( ( nextTopPos == m_bottomPos ) && m_volatile )
   {
      dropEntry();
   }
   #endif // ? #if BIWAK_SUPPORT_VOLATILE_RING

   if( nextTopPos != m_bottomPos )
   {
      m_buffers[ m_topPos ] = value;
      m_topPos = nextTopPos;
   }
   else
   {
      /*
      if(m_fatal)
      {
         lFatal("BOF");
      }
      */

      lCritical("BOF");
      return(false);
   }

   return(true);
};


template <typename T>
T CRing<T>::crossfoot() const
{
   T value=0.0;
   int pos=0;
   int count;
   if(! ( count = getEntryCount() ) )
   {
      return(value);
   }

   for(int cnt=0; cnt< count; cnt++)
   {
      value+=*getEntry(pos);
      pos++;
   }

   return( value / count );
}


template <typename T>
bool CRing<T>::isDataAvailable() const
{
   return( m_topPos != m_bottomPos );
}


template <typename T>
T *CRing<T>::getEntry(int pos) const
{
   T *value=nullptr;

   if( pos < getEntryCount() )
   {
      // Pos is relative to start
      pos= ( pos + m_bottomPos ) % m_maxEntries;
      value=&m_buffers[pos];
   }

   return(value);
}


template <typename T>
const T *CRing<T>::putString(const T *str)
{
   for(int i1=0; str[i1]; i1++)
      putValue(str[i1]);

   return(str);
}


template <typename T>
void CRing<T>::dump() const
{
   printf("m_bottomPos: %d\n", m_bottomPos);
   printf("m_topPos: %d\n", m_topPos);
   printf("m_maxEntries: %d\n", m_maxEntries);
   for(int i1=0; i1<m_maxEntries; i1++)
   {
      printf( "   [%02X]: %d\n", i1, m_buffers[i1]);
   }
}


//---fin----------------------------------------------------------------------
#endif // ? ! _LEPTO_RING_H_
