/**
 ******************************************************************************
 *
 * @file        Sample_RTC_PerpetualCalendar.c
 *
 * @brief       RTC sample code.
 *
 * @par         Project
 *              MG32
 * @version     V1.15
 * @date        2023/03/16
 * @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_PerpetualCalendar.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void Sample_RTC_PerpetualCalendar(void);

/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/

/**
 * @brief   simple define
 *
 */

/**
 * @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_RTC_PerpetualCalendar_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 ( CAL_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;

    // ------------------------------------------------------------------------
    // 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

    // ------------------------------------------------------------------------
    // 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

    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;
    uint64_t TempC;

    // ------------------------------------------------------------------------
    // 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

    // ------------------------------------------------------------------------
    // 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;
    CAL_FillYMD ( RTC_Calendar, ( uint32_t ) TempC );

    // ------------------------------------------------------------------------
    // Get Weekend
    CAL_getWeekday ( RTC_Calendar );

    return DRV_Success;
}

/**
 *******************************************************************************
 * @brief       Is this year Leap Year?
 * @details     Check year depend forumla.
 * @param[in]   RTC_Calendar: The perpetual calendar struct point.
 * @return      SMP_isLeapYearDef:
 *  @arg\b          LeapYear.
 *  @arg\b          NotLeapYear.
 * @note
 * @par         Example
 * @code
    if ( CAL_isLeapYear ( &RTC_Calendar ) == LeapYear )
    {
        // to do ...
    }
 * @endcode
 *******************************************************************************
 */
SMP_isLeapYearDef CAL_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
    CAL_FillYMD(&RTC_Calendar, 364);
 * @endcode
 *******************************************************************************
 */
void CAL_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 ( CAL_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 ( CAL_isLeapYear ( RTC_Calendar ) == LeapYear )
                MaxDayOfMonth = 29;

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

            if ( CAL_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 ( CAL_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 ( CAL_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
    CAL_getWeekday(&RTC_Calendar);
 * @endcode
 *******************************************************************************
 */
void CAL_getWeekday ( SMP_PerpetualCalendarDef *RTC_Calendar )
{
    uint16_t Y = 0x0000;
    uint8_t M = 0x00,D = 0x00;
    
    // ------------------------------------------------------------------------
    
    // 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
 *******************************************************************************
 */
void Sample_RTC_PerpetualCalendar(void)
{
    SMP_PerpetualCalendarDef RTC_Calendar;

    RTC_Calendar.year = 2019;
    RTC_Calendar.month = January;
    RTC_Calendar.day = 30;
    RTC_Calendar.hour = 1;
    RTC_Calendar.minute = 1;
    RTC_Calendar.second = 1;

    Sample_RTC_PerpetualCalendar_Init(&RTC_Calendar);
    
    //----------------------------------------------
    //After a time to do  it
    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 ( "weekend:%d\n", RTC_Calendar.weekend );
    printf ( "hour:%d\n", RTC_Calendar.hour );
    printf ( "minute:%d\n", RTC_Calendar.minute );
    printf ( "second:%d\n", RTC_Calendar.second );
}

///@}

