#ifndef _BIWAK_RETAIN_H_
#define _BIWAK_RETAIN_H_
//----------------------------------------------------------------------------
///
/// \brief  Retain data
///
///         See implementation for further information.
///
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


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


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

#include <lepto/lepto.h>      // IS_ENABLED( )
#if IS_ENABLED( CONFIG_BIWAK_I2C_RETAIN_ONLY )
   #include <biwak/flash_i2c.hpp>
#else
   #include <biwak/flash.h>
#endif

#include <stdint.h>


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


#define RETAIN_HEADER_ID      ( 0x7265746E )     // 'retn'
#define RETAIN_HEADER_SIZE    ( 0xC )


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

typedef int8_t c_counter_t;

typedef struct
{
   char           magic;         // 0x00
   uint8_t        versionMajor;  // 0x01
   uint8_t        versionMinor;  // 0x02
   c_counter_t    counter;       // 0x03
   uint32_t       len;           // 0x04
   uint32_t       dataCrc32;     // 0x08
                                 // 0x0C
} SRetainHeader;

static_assert( sizeof(SRetainHeader) == 0xC );

enum ERetainError : int
{
   noValidCounter = -100,
};

#if IS_ENABLED( CONFIG_BIWAK_I2C_RETAIN_ONLY )
   #define CFlashX CFlashI2c
#else
   #define CFlashX CFlash
#endif

/*int operator( const ERetainError &value)
{
   return( static_cast<int>(value) );
}
*/

class CRetain
{
   private:
      #if IS_ENABLED( CONFIG_BIWAK_I2C_RETAIN_ONLY )
      CFlashX&        m_flash;
      #else
      CFlash&        m_flash;
      #endif
      SRetainHeader  header;
      int            m_dataSize;
      // dataSize + sizeof(header) is not neccessarily total-size due to
      // erase page size.
      int            m_totalSize;
      void*          m_pData;
      void*          m_pMirroredData;
      int            m_containerCount;
      int            m_counter;
      address_t      m_containerAddress[2]{};
      c_counter_t    m_containerCounter[2]{};
      uint8_t        m_versionMajor;
      uint8_t        m_versionMinor;

   public:
      CRetain(CFlashX &_flash, int size=0, int _containerCount=2 );
      ~CRetain();

      void acquireMemory();
      int findNewest();
      int findOldest();
      void setVersion(int versionMajor, int versionMinor);
      int getVersion() const;
      int updateCycleTable();

      void initHeader();
      void updateHeader();

      CFlashX &getFlash(){ return(m_flash); }


      void setData(void *_data, int _dataSize, int layoutVersionMajor, int layoutVersionMinor);

      int store();
      int restore();
      int restore(int container);
#if 0
      int getPageCount() const
      {
         return(m_pages);
      }
#endif
      int containerCount() const
      {
         return(m_containerCount);
      }
      int writeCounter() const
      {
         return(m_counter);
      }
      void *getMemoryAddress() const
      {
         return(m_pData);
      }
      void *getFlashAddress(int index) const
      {
         return((void *)m_containerAddress[index]);
      }
      int getSize() const
      {
         return(m_dataSize);
      }
      int getCounter()
      {
         return(header.counter);
      }
      int destroyAll();
      int getTotalSize() const
      {
         return(m_totalSize);
      }
      static bool isNewer(c_counter_t what, c_counter_t than)
      {
         if(what==0)
            return(false);
         if(than==0)
            return(true);
         return( ( (c_counter_t) ( what - than ) ) > 0 );
      }
      static bool isOlder(c_counter_t what, c_counter_t than)
      {
         if(what==0)
            return(true);
         if(than==0)
            return(false);
         return( ( (c_counter_t) ( than- what ) ) > 0 );
      }
      int spare() const
      {
         return( m_totalSize - m_dataSize - sizeof(header) );
      }
      // Just for unit tests
      void setCounter(int counter)
      {
         m_counter=counter;
      }
      // Just for unit tests
      int getCounter(int container)
      {
         return( m_containerCounter[container] );
      }
};


//---fin----------------------------------------------------------------------
#endif // ? ! _BIWAK_RETAIN_H_
