/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright (c) 2016-2021, NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "Defines.h"
#include "ComPortDriver.h"
#include "UserInterface.h"
#include "Timer.h"
#include "MeteringLPRT.h"
#include "Calibration3Ph.h"
#include "fsl_irtc.h"
#include "Application.h"
#include "RTCDriver.h"
#include "PowerModes.h"
#include "EEPROMDriver.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define RTCEventHandler         RTC_IRQHandler
/*******************************************************************************
* Prototypes
******************************************************************************/
void RTCModuleInit(void);
void RTCEventHandler(void);
void OneHzEventHandler(void);
void ActivateRTCClockOut(void);
void DeactivateRTCClockOut(void);

uint8 DaysInMonth[12] = 
{
  31,
  28,
  31,
  30,
  31,
  30,
  31,
  31,
  30,
  31,
  30,
  31
};

uint8  Min15Boundary;
uint16 EEPDefSign;
uint32 g_UTCTime;
irtc_config_t irtcConfig;
irtc_tamper_config_t copenTamperConfig, mopenTamperConfig, topenTamperConfig;
uint8 COpenCount = 0;
uint8 GSMRemoved = 0;
uint8 GSMOpenCount = 0;
uint8 CRemoved = 0;
#ifdef TOPEN
uint8 TRemoved = 0;
uint8 TOpenCount = 0;
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Initializes the RTC module in the MCU.
 */
void RTCModuleInit(void)
{
  irtcConfig.alrmMatch = kRTC_MatchSecMinHr;
  irtcConfig.timerStdMask = false;
  irtcConfig.wakeupSelect = false;
  CLOCK_EnableClock(kCLOCK_Rtcreg);
  if (IRTC_Init(RTC, &irtcConfig) == kStatus_Fail)
  {
    return;
  }
  
  IRTC_SetOscCapLoad(RTC, kIRTC_Capacitor2p|kIRTC_Capacitor4p|kIRTC_Capacitor16p); //4 PPM
  IRTC_SetWriteProtection(RTC, false);
  if(IRTC_MOPEN_FILTER_CFG_BIT==0x00)
  {
    /* default path after 1st power on of the meter */
    
    /* configure COPEN tamper */
    copenTamperConfig.filterClk = kIRTC_2;
    copenTamperConfig.filterDuration = 10;
    copenTamperConfig.pinPolarity = false; /* false is active high */
    IRTC_SetTamperParams(RTC, IRTC_COPEN_PIN, &copenTamperConfig);
    
    /* configure MOPEN tamper */
    mopenTamperConfig = copenTamperConfig;
    mopenTamperConfig.filterDuration = 5;
    mopenTamperConfig.pinPolarity = false; /* false is active high */
    IRTC_SetTamperParams(RTC, IRTC_MOPEN_PIN, &mopenTamperConfig);
#ifdef TOPEN
    topenTamperConfig = mopenTamperConfig;
    IRTC_SetTamperParams(RTC, IRTC_TOPEN_PIN, &topenTamperConfig);
#endif
    /* Enable RTC alarm interrupt */
    IRTC_EnableInterrupts(RTC, kIRTC_TamperInterruptEnable);

    GSMRemoved = 0;
#ifdef TOPEN
    TOpened = 0;
#endif
  }
  else
  {
    /* configure COPEN tamper */
    copenTamperConfig.filterClk = kIRTC_2;
    copenTamperConfig.filterDuration = 10;
    copenTamperConfig.pinPolarity = false; /* false is active high */
    IRTC_SetTamperParams(RTC, IRTC_COPEN_PIN, &copenTamperConfig);
    
    /* configure MOPEN tamper */
    mopenTamperConfig = copenTamperConfig;
    mopenTamperConfig.filterDuration = 5;
    mopenTamperConfig.pinPolarity = true; /* true is active low */
    IRTC_SetTamperParams(RTC, IRTC_MOPEN_PIN, &mopenTamperConfig);
    
#ifdef TOPEN
    topenTamperConfig = mopenTamperConfig;
    topenTamperConfig.pinPolarity = false;
    IRTC_SetTamperParams(RTC, IRTC_TOPEN_PIN, &topenTamperConfig);
#endif
    /* Enable RTC alarm interrupt */
    IRTC_EnableInterrupts(RTC, kIRTC_TamperInterruptEnable);
    
    GSMRemoved = 1;
#ifdef TOPEN
    TOpened = 1;
#endif
  }
  /* Enable at the NVIC */
  NVIC_SetPriority(RTC_IRQn, IRTC_INTERRUPT_PRIORITY);
  EnableIRQ(RTC_IRQn);
}

void RTCEventHandler(void)
{
  volatile register uint16    reg_ier, reg_tamper_scr, reg_isr;
  uint8  source = 0;
  
  /* read RTC registers and store their values in variables - we're loading   */
  /* variables by content of registers to minimize number of read operations  */
  reg_ier = RTC->IER; reg_tamper_scr = RTC->TAMPER_SCR; reg_isr = RTC->ISR;
  
  IRTC_SetWriteProtection(RTC, false);
  /* TAMPER pin callback processing                                           */
  if(reg_ier & RTC_IER_TAMPER_IE_MASK)
  {
    /* TAMPER0 pin                                                            */
    if((reg_tamper_scr & RTC_TAMPER_SCR_TMPR_EN (TAMPER0_BIT)) &&
       (reg_tamper_scr & RTC_TAMPER_SCR_TMPR_STS(TAMPER0_BIT)))
    {
      RTC->TAMPER_SCR = RTC_TAMPER_SCR_TMPR_STS(TAMPER0_BIT)|(reg_tamper_scr&0xf);
      source |= TAMPER0_BIT;
    }
    /* TAMPER1 pin                                                            */
    if((reg_tamper_scr & RTC_TAMPER_SCR_TMPR_EN (TAMPER1_BIT)) &&
       (reg_tamper_scr & RTC_TAMPER_SCR_TMPR_STS(TAMPER1_BIT)))
    {
      RTC->TAMPER_SCR = RTC_TAMPER_SCR_TMPR_STS(TAMPER1_BIT)|(reg_tamper_scr&0xf);
      source |= TAMPER1_BIT;
    }
    /* TAMPER2 pin                                                            */
    if((reg_tamper_scr & RTC_TAMPER_SCR_TMPR_EN (TAMPER2_BIT)) &&
       (reg_tamper_scr & RTC_TAMPER_SCR_TMPR_STS(TAMPER2_BIT)))
    {
      RTC->TAMPER_SCR = RTC_TAMPER_SCR_TMPR_STS(TAMPER2_BIT)|(reg_tamper_scr&0xf);
      source |= TAMPER2_BIT;
    }
  }
  
  if (source & (1<<IRTC_COPEN_PIN))
  {    
    IRTC_SetWriteProtection(RTC, false);
    if(IRTC_COPEN_FILTER_CFG_BIT == 0x00)
    {
      COpenCount++;
      copenTamperConfig.pinPolarity = true;     /* ture is low polarity */
      IRTC_SetTamperParams(RTC, IRTC_COPEN_PIN, &copenTamperConfig);
      CRemoved = 1;
    }
    else
    {
      copenTamperConfig.pinPolarity = false;    /* false is high polarity */
      IRTC_SetTamperParams(RTC, IRTC_COPEN_PIN, &copenTamperConfig);
      CRemoved = 0;
    }
  }
  
  if (source & (1<<IRTC_MOPEN_PIN))
  {
    IRTC_SetWriteProtection(RTC, false);
    if(IRTC_MOPEN_FILTER_CFG_BIT == 0x00)
    {
      GSMOpenCount++;
      mopenTamperConfig.pinPolarity = true;     /* ture is low polarity */
      IRTC_SetTamperParams(RTC, IRTC_MOPEN_PIN, &mopenTamperConfig);
      GSMRemoved = 1;
    }
    else
    {
      mopenTamperConfig.pinPolarity = false;    /* false is high polarity */
      IRTC_SetTamperParams(RTC, IRTC_MOPEN_PIN, &mopenTamperConfig);
      GSMRemoved = 0;
    }
  }
  
#ifdef TOPEN
  if (source & (1<<IRTC_TOPEN_PIN))
  {
    if(IRTC_TOPEN_FILTER_CFG_BIT == 0x00)
    {
      TOpenCount++;
      topenTamperConfig.pinPolarity = true;         /* ture is low polarity */
      IRTC_SetTamperParams(RTC, IRTC_TOPEN_PIN, &topenTamperConfig);
      TRemoved = 1;      
    }
    else
    {
      topenTamperConfig.pinPolarity = false;        /* false is high polarity */
      IRTC_SetTamperParams(RTC, IRTC_TOPEN_PIN, &topenTamperConfig);
      TRemoved = 0;
    }
  }
#endif

  SDK_ISR_EXIT_BARRIER;
  return;
}

/*!
 * @brief Reads date-time from RTC to global data structures, variables in system RAM.
 */
void ReadDateAndTime(void)
{
  uint32 UTCTime;
  stDate Date;
  stTime Time;
  irtc_datetime_t Clock;

  IRTC_GetDatetime(RTC, &Clock);
  if ((Clock.year < 2050) && (Clock.month < 13) && (Clock.day <= 31) && 
      (Clock.hour < 24) && (Clock.minute < 60))
  {  
    Time.m_ucHr    = Clock.hour;
    Time.m_ucMin   = Clock.minute;
    Time.m_ucSec   = Clock.second;
    Date.m_ucDay   = Clock.day;
    Date.m_ucMonth = Clock.month;
    Date.m_ucYear  = Clock.year;
    
    RTCToUTC(Date, Time, &UTCTime);
    if (UTCTime != 0xFFFFFFFF)
    {
      //if ((g_UTCTime == 0) || ((UTCTime >= g_UTCTime)))
      if ((g_UTCTime == 0) || ((UTCTime >= g_UTCTime) && ((UTCTime - g_UTCTime) < 15)))
      {
        g_stTime.m_ucHr    = Clock.hour;
        g_stTime.m_ucMin   = Clock.minute;
        g_stTime.m_ucSec   = Clock.second;
        g_stDate.m_ucDay   = Clock.day;
        g_stDate.m_ucMonth = Clock.month;
        g_stDate.m_ucYear  = Clock.year;
        g_UTCTime = UTCTime;
      }
    }
  }
  return;
}

/*!
 * @brief Sets date-time to RTC as per called data.
 */
void SetDateAndTime(stDate Date,stTime Time)
{
  irtc_datetime_t Clock;
  
 if((Date.m_ucDay>31)||(Date.m_ucMonth>12))
   return;

  if((Date.m_ucMonth==4)||(Date.m_ucMonth==6)||(Date.m_ucMonth==9)||(Date.m_ucMonth==11))
 {
   if(Date.m_ucDay>30)
    return;
  
 }
 if((Date.m_ucYear)%4!=0)
 {
   if(Date.m_ucMonth==2)
     if(Date.m_ucDay>28)
       return;
 }
 else
 {
   if(Date.m_ucMonth==2)
    if(Date.m_ucDay>29)
      return;
 }
 
 if((Time.m_ucSec>59)||(Time.m_ucMin>59)||(Time.m_ucHr>23))
 {
   return;
 }

  Clock.year=Date.m_ucYear;
  Clock.month=Date.m_ucMonth;
  Clock.day=Date.m_ucDay;
  Clock.weekDay = 0;

  Clock.second=Time.m_ucSec;
  Clock.minute=Time.m_ucMin;
  Clock.hour=Time.m_ucHr;

  IRTC_SetWriteProtection(RTC, false);
  IRTC_SetDatetime(RTC, &Clock);
  g_UTCTime = 0;
  ReadDateAndTime( );
  Min15Boundary = TRUE;
}

/*!
 * @brief Computes RTC compensation value.
 */
void ComputeRTCCompensation(void)
{
  volatile float Difference = 0.0;
  float Drift32768Clocks;
  int8 ErrorSign;
  
  if ((mlib3phdata.Frequency > 49.8) && (mlib3phdata.Frequency < 50.2))
  {
    if (CalibStruct3Ph.FrequencyCoeff > 1)
    {
      ErrorSign = -1;
      Difference = CalibStruct3Ph.FrequencyCoeff - 1;
    }
    else
    {
      ErrorSign = 1;
      Difference = 1.0 - CalibStruct3Ph.FrequencyCoeff;
    }
    
    /* then, calculate the frequency with difference = No of 32K clocks drift per sec*/
    Drift32768Clocks = Difference * 32768.0;

    CalculateCoarseCompensation(ErrorSign, Drift32768Clocks, &(CalibStruct3Ph.RTCCompInterval), &(CalibStruct3Ph.RTCCompValue));
  }
}

/*!
 * @brief Computes RTC coarse compensation value.
 */
void CalculateCoarseCompensation(int8 errorSign, float drift32768Clocks, uint8 *RTCCompInterval, int8 *RTCCompValue)
{
  if(drift32768Clocks >= 1)
  {
    /* drift is few clocks */
    *RTCCompInterval = 1;
    if (drift32768Clocks > 127.0)
    {
      *RTCCompValue = 127;
    }
    else
    {
      *RTCCompValue = (int8)((drift32768Clocks) + 0.5); // + 0.5 for rounding off
    }
    *RTCCompValue *= errorSign;
  }
  else
  {      
    /* drift is fraction of a clock */
    if((1.0/drift32768Clocks) > 255.0)
    {
      /* very small fraction of a clock */
      *RTCCompInterval = 255;
      *RTCCompValue = (int8)errorSign * 1;
    }
    else
    {
      if((1.0/drift32768Clocks) < 2.55)
      {
        /* maximum might fall here */
        *RTCCompInterval = (uint8)(100.0/drift32768Clocks);
        *RTCCompValue = (int8)(errorSign * 100);
      }
      else if((1.0/drift32768Clocks) < 25.5)
      {
        *RTCCompInterval = (uint8)(10.0/drift32768Clocks);
        *RTCCompValue = (int8)(errorSign * 10);
      }
      else
      {
        *RTCCompInterval = (uint8)(1.0/drift32768Clocks);
        *RTCCompValue = (int8)(errorSign * 1);
      } 
    }
  }
}

/*!
 * @brief Appllies RTC compensation values to RTC module.
 */
void ApplyRTCCompensation(uint8 compInterval, int8 compValue)
{
  IRTC_SetWriteProtection(RTC, false);
  IRTC_SetCoarseCompensation(RTC, compValue, compInterval);
  RTC->CTRL &= ~(RTC_CTRL_FINEEN_MASK);
  RTC->CTRL |= (RTC_CTRL_COMP_EN_MASK);
}

/*!
 * @brief Initializes applicaiton time variables from RTC.
 */
void InitTime(void)
{
  uint8 i;
  for (i = 0; i < 3; i++)
  {
    /* Try 3 times */
    ReadDateAndTime( );

    if (g_UTCTime > SECS_IN_16_YRS)
    {
      // All's well
      return;
    }
    else
    {
      g_UTCTime = 0;
    }
  }
}

/*!
 * @brief Converts RTC date-time format to UTC format.
 */
void RTCToUTC(stDate Date,stTime Time, uint32 *UtcTime)
{
  uint8  i;
  uint8  j;
  
  /* Past years */
  if ((Date.m_ucYear >= 2050)||(Date.m_ucYear < 2013)||(Date.m_ucMonth>12)||(Date.m_ucDay>31)
      || (Time.m_ucHr>24)||(Time.m_ucMin>60))
  {
    *UtcTime = 0xffffffff;
    return;
  }
  i = Date.m_ucYear - 2000;
  j = i;
  i /= 4;
  j = j - (i*4);
  *UtcTime = i * DAYS_IN_4_YRS;
  if (j != 0)
  {
    (*UtcTime) += (j * DAYS_IN_1_YR);
    (*UtcTime)++; // Add the leap year
  }
  else
  {
    // This is leap year
    if (Date.m_ucMonth > 2)
    {
      // Are we past Feb already??
      (*UtcTime)++;
    }
  }
  
  /* Now this year */
  j = Date.m_ucMonth - 1;
  for (i = 0; i < j; i++)
  {
    (*UtcTime) += DaysInMonth[i];
  }
  (*UtcTime) += Date.m_ucDay;
  (*UtcTime)--;
  (*UtcTime) *= SECS_IN_DAY;
  (*UtcTime) += (Time.m_ucHr  * 3600);
  (*UtcTime) += (Time.m_ucMin * 60);
  (*UtcTime) += (Time.m_ucSec);
  
  return;
}

/*!
 * @brief Converts UTC for to RTC date-time format.
 */
void UTCToRTC(stDate *Date, stTime *Time, uint32 UTCTime)
{
  uint8  i;
  uint8  Year;
  uint16 Days;
  uint32 Seconds;
  
  Days = UTCTime/SECS_IN_DAY;
  Seconds = UTCTime % SECS_IN_DAY; // Use it later
  
  Year = Days/DAYS_IN_4_YRS;
  Year *= 4;
  
  // Now within this 4 year slot
  Days %= DAYS_IN_4_YRS;
  if (Days >= DAYS_IN_1LP_YR)
  {
    Year++;
    Days -= DAYS_IN_1LP_YR;
    if (Days >= DAYS_IN_1_YR)
    {
      Year++;
      Days -= DAYS_IN_1_YR;
      if (Days >= DAYS_IN_1_YR)
      {
        Year++;
        Days -= DAYS_IN_1_YR;
      }
    }
  }
  Date->m_ucYear = Year + 2000;
  if ((Year & 0x03) == 0)
  {
    DaysInMonth[1] = 29;
  }
  i = 0;
  while (Days >= DaysInMonth[i])
  {
    Days = Days - DaysInMonth[i];
    i++;
  }
  Date->m_ucMonth = i+1;
  Date->m_ucDay = Days+1;
  
  Time->m_ucHr = Seconds/3600;
  Seconds %= 3600;
  Time->m_ucMin = Seconds/60;
  Time->m_ucSec = Seconds % 60;
  
  DaysInMonth[1] = 28;
  return;
}
