//----------------------------------------------------------------------------
///
/// \brief support for I2C-RTC MCP7940N
///
/// https://www.mouser.de/datasheet/2/268/25010A-71550.pdf
///
/// \author Maximilian Seesslen
///
//----------------------------------------------------------------------------


#include <stdio.h>
#include <time.h>       // time.h, localtime, strftime
#include <string.h>     // strncpy
#include <sys/time.h>   // settimeofday
#include <stdlib.h>      // atoi
#include <biwak/rtc.hpp>
#include <biwak/i2c_slave.h>
#include <lepto/log.h>

const char REG_RTCSEC     = 0x00;  // Register Address: Time Second
const char REG_RTCMIN     = 0x01;  // Register Address: Time Minute
const char REG_RTCHOUR    = 0x02;  // Register Address: Time Hour
const char REG_RTCWKDAY   = 0x03;  // Register Address: Date Day of Week
const char REG_RTCDATE    = 0x04;  // Register Address: Date Day
const char REG_RTCMTH     = 0x05;  // Register Address: Date Month
const char REG_RTCYEAR    = 0x06;  // Register Address: Date Year
const char REG_CONTROL    = 0x07;  // Register Address: RTC Feature Control
const char REG_OSCTRIM    = 0x08;  // Register Address: Oscillator Digital Trim
const char REG_PWRDNMIN   = 0x18;  // Register Address: Power Failure Time Minute
const char REG_PWRDNHR    = 0x19;  // Register Address: Power Failure Time Hour
const char REG_PWRUPMIN   = 0x1C;  // Register Address: Power Restore Time Minute
const char REG_PWRUPHR    = 0x1D;  // Register Address: Power Restore Time Hour

const char REG_YEARMSK    = 0xFF;
const char REG_MONTHMSK   = 0x1F;
const char REG_DAYMSK     = 0x3F;
const char REG_SECMSK     = 0x7F;
const char REG_MINMSK     = 0x7F;
const char REG_HOURMSK    = 0x3F;


class CMcp7940: public CI2cSlave
{
   private:
      CRtc &m_rtc;
      static constexpr int DEFAULT_ADDRESS=0x6F;

   public:
      CMcp7940( CI2c &i2c, CRtc &rtc)
         :CI2cSlave( i2c, DEFAULT_ADDRESS)
         ,m_rtc(rtc)
      {
         char start;
         int sta;
         sta=i2cReadData(0, &start, 1);
         lAssert( sta == 0 );

         if( ! (start&0x80) )
         {
            lWarning("RTC was stopped.");
         }
         else
         {
            hwToRtc();
         }
      }
      void enable() const
      {
         char    trimVal       = 0x45;    // The amount of clock cycles to add/subtract.  See datasheet
         char    twelveHour    = 0x00;    // 0 = 24 Hour Clock Mode     / 1 = 12 Hour Clock Mode
         char    crsTrim       = 0x00;    // 0 = Disable Course Trim    / 1 = Enable Course Trim
         char    batEnable     = 0x01;    // 0 = Disable Battery Backup / 1 = Enable Battery Backup
         char    startClock    = 0x01;    // 0 = Start Oscillator       / 1 = Stop Oscillator
         char    controlValue  = 0x80;
         int sta;
         
         // Ensure the Control register starts as 0x00. One device did not like
         // 0x00 (i2c timeout), but 0x80 was fine. Was it an MCP7940M? Device
         // was indeed dodgy
         sta=i2cWriteData(REG_CONTROL, &controlValue, 1);
         lAssert( sta == 0 );
         sta=i2cWriteData(REG_OSCTRIM, &trimVal, 1);    // Clock Cycle Trim Value - see datasheet
         lAssert( sta == 0 );
         modifyBit(REG_RTCHOUR,  twelveHour,  6);          // 12 Hour Clock:      0 = NO / 1 = YES
         modifyBit(REG_CONTROL,  crsTrim,     2);          // Course Trim Enable: 0 = NO / 1 = YES
         modifyBit(REG_RTCWKDAY, batEnable,   3);          // Battery Enable:     0 = NO / 1 = YES
         modifyBit(REG_RTCSEC,   startClock,  7);          // Start Oscillator:   0 = NO / 1 = YES
      }
      void modifyBit(int reg, char value, char bit) const
      {
         char data;
         int sta;
         sta=i2cReadData(reg, &data, 1);
         lAssert( sta == 0 );
         if(value)
         {
            data|=(1<<bit);
         }else
         {
            data&=~(1<<bit);
         }
         sta=i2cWriteData(reg,  &data, 1);
         lAssert( sta == 0 );
      }
      int getDate() const;
      static int bcd2dec(int val)
      {
         return( ( ( val >> 4) * 10) + (val & 0xF) );
      }
      static int dec2bcd(int val)
      {
         return( ( ( val / 10 ) << 4) + ( val % 10 ) );
      }

      int getTime(int &hour, int &minute, int &sec) const;
      int getDate(int &year, int &month, int &day) const;
      int setTime(int hour, int minute, int sec) const;
      int setDate(int year, int month, int day) const;
      void hwToRtc() const;
      void rtcToHw() const;
      int dump() const;
};


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