/*******************************************************************************
* @file             main.c
*
* @version          1.0.0
*
* @brief            IntegrationAppRtc_Ms: blocking delay Rtc_DelayMilliseconds(ms)
*                  from RTC sub-second ticks. Uses alarm A one-shot sub-second
*                  compare with MASKSEL=15 (all SS[14:0] compared; same as
*                  ST-style RTC_ALARMSUBSECONDMASK_NONE). MASKSEL=0 masks all
*                  sub-seconds and breaks delay accuracy. Chained tick chunks;
*                  RTC alarm IRQ and NVIC for RTC stay off; no periodic alarm.
*                  Requires Rtc_PBcfg SynchPrediv+1 = 8192 (RTC_TICKS_PER_SEC).
*                  Avoid splitting 8192 ticks as 8191+1: a final 1-tick wait can
*                  mis-trigger on some silicon (~+1s error on DelayMs(1000)).
*
* Copyright (C) 2025 Geehy Semiconductor
*
* You may not use this file except in compliance with the GEEHY COPYRIGHT NOTICE
* (GEEHY SOFTWARE PACKAGE LICENSE).
*
* The program is only for reference, which is distributed in the hope that it
* will be useful and instructional for customers to develop their software.
* Unless required by applicable law or agreed to in writing, the program is
* distributed on an "AS IS" BASIS, WITHOUT ANY WARRANTY OR CONDITIONS OF ANY
* KIND, either express or implied. See the GEEHY SOFTWARE PACKAGE LICENSE for
* the governing permissions and limitations under the License.
*
*******************************************************************************/

#ifdef __cplusplus
extern "C"{
#endif

#include <stdio.h>

#include "g32a1085.h"
#include "Port.h"
#include "CDD_Usart.h"
#include "Mcu.h"
#include "CDD_Rtc.h"
#include "Dio.h"
#include "Rtc_Cfg.h"
#include "Rtc_Reg.h"

/*******************************************************************************
**                     LOCAL MACROS / TYPES                                   **
*******************************************************************************/

/** Must match PBcfg (SynchPrediv + 1) */
#define RTC_TICKS_PER_SEC           (8192U)

#define RTC_SUBSEC_MASK             ((uint32)0x00007FFFU)
/** ALRMASS.MASKSEL=15: compare all SS[14:0] (ST RTC_ALARMSUBSECONDMASK_NONE); 0 masks all SS */
#define RTC_ALRMASS_MASKSEL_SS_ALL  ((uint8)15U)

/*******************************************************************************
**                     LOCAL FUNCTIONS                                        **
*******************************************************************************/

static void Rtc_DisableAlarmIrq(void)
{
    Rtc_DisableInterrupt((uint32)RTC_INT_ALR);
    NVIC_DisableIRQ(RTC_IRQn);
    NVIC_ClearPendingIRQ(RTC_IRQn);
}

/**
 * @brief Read time then sub-second until two consecutive SS reads match (RM flow).
 */
static void Rtc_ReadTimeSubSecondStable(Rtc_TimeType *t, uint32 *ssOut)
{
    uint32 ss1;
    uint32 ss2;

    Rtc_ReadTime(RTC_FORMAT_BIN, t);
    ss1 = ((uint32)Rtc_ReadSubSecond()) & RTC_SUBSEC_MASK;
    ss2 = ((uint32)Rtc_ReadSubSecond()) & RTC_SUBSEC_MASK;
    if (ss1 != ss2)
    {
        Rtc_ReadTime(RTC_FORMAT_BIN, t);
        ss2 = ((uint32)Rtc_ReadSubSecond()) & RTC_SUBSEC_MASK;
    }
    *ssOut = ss2;
}

/** Sub-second counter assumed to count down between second boundaries. */
static uint32 Rtc_CalcSubSecondCountdownTarget(uint32 ss, uint32 ticks)
{
    if (ticks >= RTC_TICKS_PER_SEC)
    {
        ticks = RTC_TICKS_PER_SEC - 1U;
    }
    if (ss >= ticks)
    {
        return (ss - ticks);
    }
    return (ss + RTC_TICKS_PER_SEC - ticks);
}

/**
 * @brief Wait for @p ticks sub-second steps (1..8191), poll ALRF; RTC NVIC off.
 */
static void Rtc_WaitSubSecondTicksAlarm(uint32 ticks)
{
    Rtc_TimeType tm;
    uint32 ss;
    uint32 target;

    if (ticks == 0U)
    {
        return;
    }
    if (ticks >= RTC_TICKS_PER_SEC)
    {
        ticks = RTC_TICKS_PER_SEC - 1U;
    }
    (void)Rtc_WaitForSynchro();
    Rtc_ReadTimeSubSecondStable(&tm, &ss);
    target = Rtc_CalcSubSecondCountdownTarget(ss, ticks);

    (void)Rtc_DisableAlarm();
    Rtc_ClearStatusFlag((uint32)RTC_FLAG_ALRF);
    Rtc_ConfigAlarmSubSecond(target, RTC_ALRMASS_MASKSEL_SS_ALL);
    Rtc_EnableAlarm();
    while (FALSE == Rtc_ReadStatusFlag(RTC_FLAG_ALRF))
    {
    }
    Rtc_ClearStatusFlag((uint32)RTC_FLAG_ALRF);
    (void)Rtc_DisableAlarm();
}

/**
 * @brief Blocking delay in ms (rounded to nearest sub-second tick). No periodic IRQ.
 */
static void Rtc_DelayMilliseconds(uint32 ms)
{
    uint32 total_ticks;
    uint32 chunk;

    if (ms == 0U)
    {
        return;
    }
    total_ticks = (ms * RTC_TICKS_PER_SEC + 500U) / 1000U;
    if (total_ticks == 0U)
    {
        total_ticks = 1U;
    }

    Rtc_DisableAlarmIrq();

    while (total_ticks > 0U)
    {
        chunk = total_ticks;
        if (chunk >= RTC_TICKS_PER_SEC)
        {
            chunk = RTC_TICKS_PER_SEC - 1U;
        }
        /* Do not split 8192 as 8191+1: trailing 1-tick wait can stall ~1 extra second */
        if ((chunk == (RTC_TICKS_PER_SEC - 1U)) && (total_ticks == RTC_TICKS_PER_SEC))
        {
            chunk = RTC_TICKS_PER_SEC - 2U;
        }
        Rtc_WaitSubSecondTicksAlarm(chunk);
        total_ticks -= chunk;
    }
}

/** Alarm A: mask calendar fields; sub-second compare only (time matches PBcfg). */
static void Rtc_InitAlarmMaskedForSubSecond(const Rtc_ConfigType *cfg)
{
    static Rtc_AlarmType s_alarm;
    Rtc_ConfigType app;

    s_alarm.time.hours   = cfg->TimeStruct->hours;
    s_alarm.time.minutes = cfg->TimeStruct->minutes;
    s_alarm.time.seconds = cfg->TimeStruct->seconds;
    s_alarm.time.H12     = cfg->TimeStruct->H12;
    s_alarm.AlarmMask    = (uint32)RTC_ALRMA_ALL_MASK;
    s_alarm.AlarmDateWeekDaySel = cfg->AlarmStruct->AlarmDateWeekDaySel;
    s_alarm.AlarmDateWeekDay    = cfg->AlarmStruct->AlarmDateWeekDay;

    app.Rtc_TimeInit = cfg->Rtc_TimeInit;
    app.TimeStruct   = cfg->TimeStruct;
    app.AlarmStruct  = &s_alarm;
    Rtc_AlarmsConfig(&app);

    Rtc_ClearStatusFlag((uint32)RTC_FLAG_ALRF);
    (void)Rtc_DisableAlarm();
    Rtc_DisableAlarmIrq();
}

int main(void)
{
    const Rtc_ConfigType *pBaseCfg = &g_rtcConfig;

    Mcu_Init(&g_mcuConfig);
    Mcu_InitClock(McuClockSettingConfig_0);
    Port_Init(&g_portConfig);
    Usart_Init(g_usartConfg);

    Rtc_Init(pBaseCfg);
    Rtc_InitAlarmMaskedForSubSecond(pBaseCfg);

    printf("\r\nIntegrationAppRtc_Ms: Rtc_DelayMilliseconds() only (RTC subsec one-shot, no IRQ)\r\n");
    printf("ticks/s=%u\r\n", (unsigned int)RTC_TICKS_PER_SEC);
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_HIGH);
    printf("\r\n200 ms ...\r\n");
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_LOW);
    Rtc_DelayMilliseconds(200U);
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_HIGH);
    printf("done\r\n");

    printf("500 ms ...\r\n");
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_LOW);
    Rtc_DelayMilliseconds(500U);
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_HIGH);
    printf("done\r\n");

    printf("1500 ms ...\r\n");
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_LOW);
    Rtc_DelayMilliseconds(1500U);
    Dio_WriteChannel(DIO_PORT_C_PIN_10, DIO_STD_HIGH);
    printf("done\r\n");

    /* Interval between consecutive flips = delay; same-polarity edge period = 2 * delay */
    printf("\r\nLoop: Rtc_DelayMilliseconds(1000) + Flip, 1s between flips\r\n");
    while (1)
    {
        Rtc_DelayMilliseconds(1000U);
        Dio_FlipChannel(DIO_PORT_C_PIN_10);
    }
}

#if defined (__GNUC__) || (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*/
int fputc(int ch, FILE* f)
{
    int reVal;
    (void)f;
    reVal = Usart_PutRedefinition(USART1_ID, &ch);

    return reVal;
}


/* If configuring the full library using versions after IAR 9.30,
   it is necessary to implement the _write function for printf redirection.*/
size_t __write(int handle, const unsigned char *buf, size_t bufSize)
{
    size_t i;
    (void)handle;
    for (i = 0U; i < bufSize; i++)
    {
        int32 ch = (int32)buf[i];
        (void)Usart_PutRedefinition(USART1_ID, &ch);
    }
    return bufSize;
}

/* For Arm Cross GCC: Eclipse */
int _write(int fd, const unsigned char *buf, int len)
{
    int i;
    (void)fd;
    for (i = 0; i < len; i++)
    {
        int32 ch = (int32)buf[i];
        (void)Usart_PutRedefinition(USART1_ID, &ch);
    }
    return len;
}

#endif

#ifdef __cplusplus
}
#endif
