/**
 ******************************************************************************
 *
 * @file        Sample_RTC_ShowCalendar.c
 *
 * @brief       RTC sample code.
 *
 * @par         Project
 *              MG32
 * @version     V1.01
 * @date        2022/12/22
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2018 MegaWin Technology Co., Ltd.
 *              All rights reserved.
 *
 ******************************************************************************
 * @par         Disclaimer
 *      The Demo software is provided "AS IS"  without any warranty, either
 *      expressed or implied, including, but not limited to, the implied warranties
 *      of merchantability and fitness for a particular purpose.  The author will
 *      not be liable for any special, incidental, consequential or indirect
 *      damages due to loss of data or any other reason.
 *      These statements agree with the world wide and local dictated laws about
 *      authorship and violence against these laws.
 ******************************************************************************
 ******************************************************************************
 */

 

/* Includes ------------------------------------------------------------------*/
#include "Sample_RTC_ShowCalendar.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
uint8_t MG32_Calendar_Date_Ready = 0x00;
#pragma clang diagnostic pop
#else
uint8_t MG32_Calendar_Date_Ready = 0x00;
#endif

static uint32_t DATE_REG[4];
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/


/**
 * @name    initial RTC with 1sec period
 *
 */
///@{
/**
 *******************************************************************************
 * @brief       Initializes the base time and enable RTC
 * @details     Check input parameters and initial RTC
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 *  @arg\b      year : AD year.
 *  @arg\b      month : January, February, March ... December.
 *  @arg\b      day : 1~31.
 *  @arg\b      hour : 0~23.
 *  @arg\b      minute : 0~59.
 *  @arg\b      second : 0~59.
 * @return      DRV_Return:
 *  @arg\b          DRV_Failure.
 *  @arg\b          DRV_Success.
 * @note
 * @par         Example
 * @code
    SMP_PerpetualCalendarDef RTC_Calendar;

    RTC_Calendar.year = 2018;
    RTC_Calendar.month = August;
    RTC_Calendar.day = 8;
    RTC_Calendar.hour = 10;
    RTC_Calendar.minute = 43;
    RTC_Calendar.second = 30;

    Sample_RTC_PerpetualCalendar_Init(&RTC_Calendar);
 * @endcode
 *******************************************************************************
 */
DRV_Return Sample_Calendar_Init(SMP_PerpetualCalendarDef *RTC_Calendar)
{
    // month days
    const uint8_t MonthDays[] = {31,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    // ------------------------------------------------------------------------
    // check input parameter (month)
    if ( RTC_Calendar->month > December )
        return DRV_Failure;

    // ------------------------------------------------------------------------
    // check input parameter (day)
    if ( RTC_Calendar->day == 0 )
        return DRV_Failure;

    if ( RTC_Calendar->month == February )
    {
        // leap year ?
        if ( isLeapYear ( RTC_Calendar ) == LeapYear )
        {
            if ( RTC_Calendar->day > 29 )
                return DRV_Failure;
        }
        else if ( RTC_Calendar->day > 28 )
            return DRV_Failure;
    }
    else if ( RTC_Calendar->day > MonthDays[RTC_Calendar->month] )
        return DRV_Failure;


    // ------------------------------------------------------------------------
    // check input parameter (hour)
    if ( RTC_Calendar->hour > 23 )
        return DRV_Failure;

    // ------------------------------------------------------------------------
    // check input parameter (minute)
    if ( RTC_Calendar->minute > 59 )
        return DRV_Failure;

    // ------------------------------------------------------------------------
    // check input parameter (second)
    if ( RTC_Calendar->second > 59 )
        return DRV_Failure;

    

    return DRV_Success;
}


void Sample_RTC_Init(void)
{
    // ------------------------------------------------------------------------
    // Enable CSC to RTC clock
    UnProtectModuleReg ( CSCprotect );
    CSC_PeriphOnModeClock_Config ( CSC_ON_RTC, ENABLE );
    ProtectModuleReg ( CSCprotect );
    
    // ------------------------------------------------------------------------
    // Configure RTC clock
    UnProtectModuleReg ( RTCprotect );                              // Unprotect RTC module
    RTC_CLK_Select ( RTC_CK_LS );                                   // RTC clock source = CK_LS
    RTC_PreDivider_Select ( RTC_PDIV_4096 );                        // PDIV output = RTC clock / 4096
    RTC_Divider_Select ( RTC_DIV_8 );                               // DIV output = (RTC clock / 4096) / 8
    
    // ------------------------------------------------------------------------
    // RTC Iterrupt Initialize Configure.
    RTC_IT_Config(RTC_INT_PC,ENABLE);
    RTC_ITEA_Cmd(ENABLE);
    SYS_ITEA_Cmd(ENABLE);
    NVIC_EnableIRQ(SYS_IRQn);
    
    // ------------------------------------------------------------------------
    // Set RTC timer value
    RTC_RCR_Mode_Select ( RTC_RCR_MOD_ForceReload );                // RTC switch to reload mode
    RTC_SetReloadReg ( 0 );                                          // Set reload data
    RTC_TriggerStamp_SW();                                          // Trigger reload data update to RTC timer
    while ( RTC_GetSingleFlagStatus ( RTC_RCRF ) == DRV_UnHappened ); // Waiting reload complete

    RTC_ClearFlag ( RTC_ALLF );                                     // Clear flag

    // ------------------------------------------------------------------------
    // Enable RTC module
    RTC_Cmd ( ENABLE );                                             // Enable RTC module
}

/**
 *******************************************************************************
 * @brief       Convert RTC to perpetual calendar
 * @details     Get RTC value & convert to Y/M/D hh:mm:ss with weekend
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 * @return      DRV_Return:
 *  @arg\b          DRV_Failure.
 *  @arg\b          DRV_Success.
 * @note
 * @par         Example
 * @code
    Sample_RTC_CaptureConvert(&RTC_Calendar);

    printf ( "year:%d\n", RTC_Calendar.year );
    printf ( "month:%d\n", RTC_Calendar.month );
    printf ( "day:%d\n", RTC_Calendar.day );
    printf ( "hour:%d\n", RTC_Calendar.hour );
    printf ( "minute:%d\n", RTC_Calendar.minute );
    printf ( "second:%d\n", RTC_Calendar.second );
    printf ( "weekend:%d\n", RTC_Calendar.weekend );
 * @endcode
 *******************************************************************************
 */
DRV_Return Sample_Calendar_Colculate(SMP_PerpetualCalendarDef *RTC_Calendar, uint64_t RTC_CNT)
{
    uint64_t TempC;
    
    // ------------------------------------------------------------------------
    // addition base time
    TempC = ( uint64_t ) ( RTC_Calendar->hour * 3600 );
    TempC += ( uint64_t ) ( RTC_Calendar->minute * 60 );
    TempC += ( uint64_t ) ( RTC_Calendar->second );

    RTC_CNT += TempC;

    // ------------------------------------------------------------------------
    // convert second, mintu, hour
    RTC_Calendar->second = RTC_CNT % 60;
    RTC_Calendar->minute = ( RTC_CNT / 60 ) % 60;
    RTC_Calendar->hour = ( RTC_CNT / 3600 ) % 24;

    // ------------------------------------------------------------------------
    // Fill days, month, year
    TempC = RTC_CNT / 86400;
    FillYMD ( RTC_Calendar, ( uint32_t ) TempC );

    // ------------------------------------------------------------------------
    // Get Weekend
    getWeekday ( RTC_Calendar );
    
    return DRV_Success;
}

/**
 *******************************************************************************
 * @brief       Convert RTC to perpetual calendar
 * @details     Get RTC value & convert to Y/M/D hh:mm:ss with weekend
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 * @return      DRV_Return:
 *  @arg\b          DRV_Failure.
 *  @arg\b          DRV_Success.
 * @note
 * @par         Example
 * @code
    Sample_RTC_CaptureConvert(&RTC_Calendar);

    printf ( "year:%d\n", RTC_Calendar.year );
    printf ( "month:%d\n", RTC_Calendar.month );
    printf ( "day:%d\n", RTC_Calendar.day );
    printf ( "hour:%d\n", RTC_Calendar.hour );
    printf ( "minute:%d\n", RTC_Calendar.minute );
    printf ( "second:%d\n", RTC_Calendar.second );
    printf ( "weekend:%d\n", RTC_Calendar.weekend );
 * @endcode
 *******************************************************************************
 */
DRV_Return Sample_RTC_CaptureConvert (SMP_PerpetualCalendarDef *RTC_Calendar)
{
    uint64_t RTC_CNT;
    

    // ------------------------------------------------------------------------
    // Get RTC timer value
    RTC_RCR_Mode_Select ( RTC_RCR_MOD_DirectlyCapture );            // RTC switch to delay capture mode
    RTC_ClearFlag ( RTC_PCF | RTC_RCRF );                           // Clear flag PCF and RCRF
    RTC_TriggerStamp_SW();                                          // Trigger RTC capture timer data

    while ( RTC_GetSingleFlagStatus ( RTC_RCRF ) == DRV_UnHappened ); // Waiting capture complete

    RTC_CNT = ( uint64_t ) RTC_GetCaptureReg();                     // Get capture register data
    
    Sample_Calendar_Colculate(RTC_Calendar, RTC_CNT);
    
    return DRV_Success;
}

/**
 *******************************************************************************
 * @brief       Is this year Leap Year?
 * @details     Check year depend forumla.
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 * @return      _isLeapYearDef:
 *  @arg\b          LeapYear.
 *  @arg\b          NotLeapYear.
 * @note
 * @par         Example
 * @code
    if ( isLeapYear ( &RTC_Calendar ) == LeapYear )
    {
        // to do ...
    }
 * @endcode
 *******************************************************************************
 */
SMP_isLeapYearDef isLeapYear ( SMP_PerpetualCalendarDef *RTC_Calendar )
{
    if ( ( RTC_Calendar->year % 4 == 0 && RTC_Calendar->year % 100 != 0 ) || ( RTC_Calendar->year % 400 == 0 ) )
        return LeapYear;

    return NotLeapYear;
}

/**
 *******************************************************************************
 * @brief       Convert remain days to YY/MM/DD
 * @details     Convert days to YY/MM/DD
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 * @param[in]   days: Remain days.
 * @return      none
 * @note
 * @par         Example
 * @code
    FillYMD(&RTC_Calendar, 364);
 * @endcode
 *******************************************************************************
 */
void FillYMD ( SMP_PerpetualCalendarDef *RTC_Calendar, uint32_t days )
{
    // month days
    const uint8_t MonthDays[] = {31 ,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    uint8_t MaxDayOfMonth;


    // ------------------------------------------------------------------------
    MaxDayOfMonth = MonthDays[RTC_Calendar->month];

    if ( RTC_Calendar->month == February )
        if ( isLeapYear ( RTC_Calendar ) == LeapYear )
            MaxDayOfMonth = 29;

    // ------------------------------------------------------------------------
    if ( days > MaxDayOfMonth )
    {
        //
        days -= ( MaxDayOfMonth - RTC_Calendar->day );
        RTC_Calendar->month ++;

        if ( RTC_Calendar->month > December )
        {
            RTC_Calendar->month = January;
            RTC_Calendar->year ++;
        }

        MaxDayOfMonth = MonthDays[RTC_Calendar->month];

        if ( RTC_Calendar->month == February )
            if ( isLeapYear ( RTC_Calendar ) == LeapYear )
                MaxDayOfMonth = 29;

        // year
        while ( 1 )
        {
            if ( days < 365 )
                break;

            if ( isLeapYear ( RTC_Calendar ) == LeapYear )
                days -= 366;
            else
                days -= 365;

            RTC_Calendar->year ++;
        }

        // month
        while ( days > MaxDayOfMonth )
        {
            // check leap year
            // February has 29 days
            switch ( RTC_Calendar->month )
            {
                case January:
                case March:
                case May:
                case July:
                case August:
                case October:
                case December:
                                    days -= 31;
                                    break;

                case February:
                                    if ( isLeapYear ( RTC_Calendar ) == LeapYear )
                                        days -= 29;
                                    else
                                        days -= 28;

                    break;

                case April:
                case June:
                case September:
                case November:
                                    days -= 30;
                                    break;
            }

            RTC_Calendar->month ++;

            if ( RTC_Calendar->month > December )
            {
                RTC_Calendar->month = January;
                RTC_Calendar->year ++;
            }

            MaxDayOfMonth = MonthDays[RTC_Calendar->month];

            if ( RTC_Calendar->month == February )
                if ( isLeapYear ( RTC_Calendar ) == LeapYear )
                    MaxDayOfMonth = 29;

        }

        // ------------------------------------------------------------------------
        // remain days
        RTC_Calendar->day = (uint8_t)days;
    }
    else
    {
        RTC_Calendar->day += days;

        if ( RTC_Calendar->day > MaxDayOfMonth )
        {
            RTC_Calendar->day -= MaxDayOfMonth;
            RTC_Calendar->month ++;

            if ( RTC_Calendar->month > December )
            {
                RTC_Calendar->month = January;
                RTC_Calendar->year ++;
            }
        }
    }

}


/**
 *******************************************************************************
 * @brief       Get weekend from calendar struct
 * @details     Calculate YY/MM/DD to get weekend
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 * @return      none
 * @note
 * @par         Example
 * @code
    FillYMD(&RTC_Calendar);
 * @endcode
 *******************************************************************************
 */
void getWeekday ( SMP_PerpetualCalendarDef *RTC_Calendar )
{
    uint16_t Y = 0x0000;
    uint8_t M = 0x00,D = 0x00;

        
    // Kim Larsen formula
    
    // Weekend Calculate
    Y = (uint16_t)RTC_Calendar->year;
    M = (uint8_t) RTC_Calendar->month;
    D = (uint8_t) RTC_Calendar->day;
    
    switch(RTC_Calendar->month)
    {
        case January:
                        Y = Y - 1;
                        M = 13;
                        break;
        case February:
                        Y = Y - 1;
                        M = 14;
                        break;
        case March:
        case April:
        case May:
        case June:      
        case July:      
        case August:    
        case September: 
        case October:   
        case November:  
        case December:  
                        break;
    }
    
    RTC_Calendar->weekend = ( SMP_WeekendDef ) ( ( D+1+2*M+3*(M+1)/5+Y+Y/4-Y/100+Y/400 ) % 7 );             // Used "Kim larsen" calculation formula
}

/**
 *******************************************************************************
 * @brief       RTC PerpetualCalendar sample code
 * @details     
 * @return      none
 * @note
 * @par         Example
 * @code
 * @endcode
 * @example
 *              RTC_Calendar.year = 2022;
 *              RTC_Calendar.month = May;
 *              RTC_Calendar.day = 2;
 *              RTC_Calendar.hour = 10;
 *              RTC_Calendar.minute = 30;
 *              RTC_Calendar.second = 20;
 *              Sample_RTC_ShowCalendar(&RTC_Calendar);
 *              
 *              
 *              while(1)
 *              {
 *                  //To do......
 *                  if(MG32_Calendar_Date_Ready==1)
 *                  {
 *                      Sample_RTC_CaptureConvert(&RTC_Calendar);
 *                      
 *                      printf("\n%d/%d/%d %d:%d.%d\n", RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day, RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
 *                      printf("Weekend:%d\n",RTC_Calendar.weekend);
 *                      MG32_Calendar_Date_Ready = 0;
 *                  }
 *                  
 *              }
 *******************************************************************************
 */
uint32_t Sample_RTC_ShowCalendar(SMP_PerpetualCalendarDef *RTC_Calendar)
{
    uint8_t m = 0x00, d = 0x00, w = 0x00,db = 0;
    
    DATE_REG[0] = (uint32_t)RTC_Calendar->year;
    DATE_REG[1] = (uint8_t)RTC_Calendar->month;
    DATE_REG[2] = (uint8_t)RTC_Calendar->day;
    DATE_REG[3] = (uint8_t)RTC_Calendar->weekend;
    
    // Show Calendar
    printf ( "\n\nyear:%d \n", RTC_Calendar->year );
    
    for(m=0x00 ; m<12 ; m++)
    {
        RTC_Calendar->month = m+1;
        
        printf ( "\nmonth:%d \n", RTC_Calendar->month );
        
        printf("Sun   Mon   Tue   Wed   Thu   Fri   Sat\n");
        
        
        for(d = 0x00 ; d<31 ; d++)
        {
            RTC_Calendar->day = d+1;
            if(Sample_Calendar_Init(RTC_Calendar)==DRV_Failure)
                break;
            
            Sample_Calendar_Colculate(RTC_Calendar, 0);
            
            if(w==RTC_Calendar->weekend)    // Weekend Double Define Check
                db = 1;
            
            if(RTC_Calendar->day == 1)
            {
                if(RTC_Calendar->weekend!=Sunday)
                {
                    w = RTC_Calendar->weekend;
                    for(;w!=0;w--)
                    {
                        printf("      ");
                    }
                }
            }
            
            w = RTC_Calendar->weekend;
            
            if(RTC_Calendar->weekend==0)
            {
                if(RTC_Calendar->day != Monday)
                    printf("\n");
            }
            // Number interval
            // Number align
            
            if(db!=1)
            {
                if(d<9)             // When date is padding number.
                    printf("0");
                
                printf ( "%d    ", RTC_Calendar->day );
                
            }
            
            else
            {
                db = 0;             // Clear Double Define State.
            }
        }
        
    }
    
    RTC_Calendar->year = (uint32_t)DATE_REG[0];
    RTC_Calendar->month = (uint8_t)DATE_REG[1];
    RTC_Calendar->day = (uint8_t)DATE_REG[2];
    RTC_Calendar->weekend = (uint8_t)DATE_REG[3];
    
    // Check Calnedar Value.
    if(Sample_Calendar_Init(RTC_Calendar) != DRV_Success)
    {
        printf("Canlendar date is illegal date, recheck date please./n ");
        return DRV_Failure;
    }
    else
    {
        Sample_RTC_Init();
    }
    
    
    return DRV_Success;
}

/**
 *******************************************************************************
 * @brief       SYS: An interrupt, or IRQ, is an exception signalled by a
 *              peripheral, or generated by a software request. 
 * @details     WWDT interrupt source.
 * @return      No
 *******************************************************************************
 */
void SYS_IRQHandler(void)
{
    RTC_IRQ();
}

/**
 *******************************************************************************
 * @brief       RTC module interrupt service routine.
 * @details     RTC PC interrupt set RTC data ready flag and clear flag.
 * @return      No
 *******************************************************************************
 */
void RTC_IRQ(void)
{
    // Clear RTC Flag
    RTC_ClearFlag(RTC_PCF);
    
    MG32_Calendar_Date_Ready = 1;
}


///@}

