/**
 ******************************************************************************
 *
 * @file        Sample_RTC_PerpetualCalendar.c
 *
 * @brief       RTC sample code.
 *
 * @par         Project
 *              MG32
 * @version     V1.00
 * @date        2018/08/07
 * @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.
 ******************************************************************************
 ******************************************************************************
 */

#include "Sample.h"
 
#if Sample_Code_Macro_Select == Sample_Code_Macro_RTC && Sample_Code_RTC == Sample_Code_RTC_PerpetualCalendar 


#include "Sample_RTC_PerpetualCalendar.h"






/**
 * @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]   MG_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
    _PerpetualCalendarDef MG_Calendar;

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

    Sample_RTC_PerpetualCalendar_Init(&MG_Calendar);
 * @endcode
 *******************************************************************************
 */
DRV_Return Sample_RTC_PerpetualCalendar_Init ( _PerpetualCalendarDef *MG_Calendar )
{
    // month days
    const uint8_t MonthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    // ------------------------------------------------------------------------
    // check input parameter (month)
    if ( MG_Calendar->month > December )
        return DRV_Failure;

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

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


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

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

    // ------------------------------------------------------------------------
    // check input parameter (second)
    if ( MG_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_SetReladReg ( 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]   MG_Calendar: The perpetual calendar struct point.
 * @return      DRV_Return:
 *  @arg\b          DRV_Failure.
 *  @arg\b          DRV_Success.
 * @note
 * @par         Example
 * @code
    Sample_RTC_CaptureConvert(&MG_Calendar);

    printf ( "year:%d\n", MG_Calendar.year );
    printf ( "month:%d\n", MG_Calendar.month );
    printf ( "day:%d\n", MG_Calendar.day );
    printf ( "hour:%d\n", MG_Calendar.hour );
    printf ( "minute:%d\n", MG_Calendar.minute );
    printf ( "second:%d\n", MG_Calendar.second );
    printf ( "weekend:%d\n", MG_Calendar.weekend );
 * @endcode
 *******************************************************************************
 */
DRV_Return Sample_RTC_CaptureConvert ( _PerpetualCalendarDef *MG_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 ) ( MG_Calendar->hour * 3600 );
    TempC += ( uint64_t ) ( MG_Calendar->minute * 60 );
    TempC += ( uint64_t ) ( MG_Calendar->second );

    RTC_CNT += TempC;

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

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

    // ------------------------------------------------------------------------
    // Get Weekend
    getWeekday ( MG_Calendar );

    return DRV_Success;
}

/**
 *******************************************************************************
 * @brief       Is this year Leap Year?
 * @details     Check year depend forumla.
 * @param[in]   MG_Calendar: The perpetual calendar struct point.
 * @param[in]   days: Remain days.
 * @return      _isLeapYearDef:
 *  @arg\b          LeapYear.
 *  @arg\b          NotLeapYear.
 * @note
 * @par         Example
 * @code
    if ( isLeapYear ( &MG_Calendar ) == LeapYear )
    {
        // to do ...
    }
 * @endcode
 *******************************************************************************
 */
_isLeapYearDef isLeapYear ( _PerpetualCalendarDef *MG_Calendar )
{
    if ( ( MG_Calendar->year % 4 == 0 && MG_Calendar->year % 100 != 0 ) || ( MG_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]   MG_Calendar: The perpetual calendar struct point.
 * @param[in]   days: Remain days.
 * @return      none
 * @note
 * @par         Example
 * @code
    FillYMD(&MG_Calendar, 364);
 * @endcode
 *******************************************************************************
 */
void FillYMD ( _PerpetualCalendarDef *MG_Calendar, uint32_t days )
{
    // month days
    const uint8_t MonthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    uint8_t MaxDayOfMonth;


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

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

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

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

        MaxDayOfMonth = MonthDays[MG_Calendar->month];

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

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

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

            MG_Calendar->year ++;
        }

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

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

                break;

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

            default:
                break;
            }

            MG_Calendar->month ++;

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

            MaxDayOfMonth = MonthDays[MG_Calendar->month];

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

        }

        // ------------------------------------------------------------------------
        // remain days
        MG_Calendar->day = days;
    }
    else
    {
        MG_Calendar->day += days;

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

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

}


/**
 *******************************************************************************
 * @brief       Get weekend from calendar struct
 * @details     Calculate YY/MM/DD to get weekend
 * @param[in]   MG_Calendar: The perpetual calendar struct point.
 * @return      none
 * @note
 * @par         Example
 * @code
    FillYMD(&MG_Calendar);
 * @endcode
 *******************************************************************************
 */
void getWeekday ( _PerpetualCalendarDef *MG_Calendar )
{
    uint8_t C, Y, M, D;
    const uint8_t MonthWeekendTable[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};

    // ------------------------------------------------------------------------
    C = ( MG_Calendar->year / 100 );                // generate century factor
    Y = ( MG_Calendar->year % 100 );                // generate year factor
    M = MonthWeekendTable[MG_Calendar->month];      // calculate month factor
    D = ( MG_Calendar->day % 7 );                   // calculate day factor

    // calculate century factor
    C = 2 * ( 3 - ( C % 4 ) );

    // calculate year factor
    Y = ( ( Y % 28 ) + ( ( Y % 28 ) / 4 ) ) % 7;

    if ( isLeapYear ( MG_Calendar ) == LeapYear )
        if ( ( MG_Calendar->month == January ) || ( MG_Calendar->month == February ) )
            Y --;

    // calaulate weekend
    MG_Calendar->weekend = ( _WeekendDef ) ( ( C + Y + M + D ) % 7 );

}



/**
 *******************************************************************************
 * @brief       RTC PerpetualCalendar sample code
 * @details     
 * @param[in]   none
 * @return      none
 * @note
 * @par         Example
 * @code
 * @endcode
 *******************************************************************************
 */
void Sample_RTC_PerpetualCalendar(void)
{
    _PerpetualCalendarDef MG_Calendar;

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

    Sample_RTC_PerpetualCalendar_Init(&MG_Calendar);

    //----------------------------------------------
    //After a time to do  it
    Sample_RTC_CaptureConvert(&MG_Calendar);

    printf ( "year:%d\n", MG_Calendar.year );
    printf ( "month:%d\n", MG_Calendar.month );
    printf ( "day:%d\n", MG_Calendar.day );
    printf ( "hour:%d\n", MG_Calendar.hour );
    printf ( "minute:%d\n", MG_Calendar.minute );
    printf ( "second:%d\n", MG_Calendar.second );
    printf ( "weekend:%d\n", MG_Calendar.weekend );
}

///@}

#endif
