#include "time.h"
#include "../ADI/rtc/adi_rtc.h"
#include <drivers/tmr/adi_tmr.h>

//=========================== defines =========================================

//=========================== typedef =========================================

typedef struct
{
    // FSM event
    time_event_cbt fsmEventCb;

    // publishing event
    time_event_cbt publishEventCb;
    uint32_t       publishPeriod; // pointer to flash memory

} time_vars_t;

//=========================== variables =======================================

//==== save callback to upper layers
time_vars_t         time_vars;

//==== RTC variables
static bool         RTC_configured = false;
uint8_t             RTC1Mem[ADI_RTC_MEMORY_SIZE];  // Memory required for RTC driver
ADI_RTC_HANDLE      hRTC1;                         // RTC1 device handle
static IRQn_Type    RTC1_IRQ = RTC1_EVT_IRQn;

//==== TMR variables
static IRQn_Type    TMR0_IRQ = TMR0_EVT_IRQn;


//=========================== prototypes ======================================

void                time_rtc1Alarm(void *pCBParam, uint32_t Event, void *pArg);
void                time_tmr0Alarm(void *pCBParam, uint32_t Event, void *pArg);

//=========================== public ==========================================

void     time_configureFsmEvent(uint32_t delay_ms, time_event_cbt fsmEvent)
{
    static bool firstRun = true;

    ADI_TMR_CONFIG tmr_cfg =
    {
        .bCountingUp  = false,
        .bPeriodic    = true,
        .ePrescaler   = ADI_TMR_PRESCALER_16,
        .eClockSource = ADI_TMR_CLOCK_LFOSC,
        .nLoad        = delay_ms << 1, // multiply 2x to get cca. milisecond
        .nAsyncLoad   = delay_ms << 1, // multiply 2x to get cca. milisecond
        .bReloading   = false,
        .bSyncBypass  = false,
    };

    // init timer if run for the first time
    if (firstRun)
    {
        // configure TMR0
        adi_tmr_Init(
            ADI_TMR_DEVICE_GP0,
            time_tmr0Alarm,
            &TMR0_IRQ,
            true
        );

        NVIC_SetPriority(TMR0_EVT_IRQn, 7);    // Low priority

        firstRun = false;
    }

    // store parameters, if any
    if (fsmEvent != NULL)
        time_vars.fsmEventCb = fsmEvent;

    // configure and run the timer
    adi_tmr_ConfigTimer(ADI_TMR_DEVICE_GP0, &tmr_cfg);
    adi_tmr_Enable(ADI_TMR_DEVICE_GP0, true);
}

void time_configurePublishEvent(uint32_t period, time_event_cbt publishEvent) // in 1/32 ms
{
    uint32_t    now;

    // Configure RTC handler, if not configured
    if (!RTC_configured)
    {
        adi_rtc_Open(
            1,
            RTC1Mem,
            ADI_RTC_MEMORY_SIZE,
            &hRTC1
        );
				
				adi_rtc_Enable(hRTC1, true);
        RTC_configured = true;
    }

    adi_rtc_RegisterCallback(hRTC1, time_rtc1Alarm, &RTC1_IRQ);
    NVIC_SetPriority(RTC1_EVT_IRQn, 6);    // Low priority

    // store parameters, if any
    if (publishEvent != NULL)
        time_vars.publishEventCb = publishEvent;

    // set up the alarm
    if (period != 0)
    {
        // store the period
        time_vars.publishPeriod = period;

        // get actual time in 32768Hz ticks
        adi_rtc_GetCount(hRTC1, &now);

        // set and enable alarm
        adi_rtc_SetAlarm(hRTC1, now + period);
        adi_rtc_EnableAlarm(hRTC1, true);
				adi_rtc_EnableInterrupts (hRTC1, ADI_RTC_ALARM_INT, true);
    }

    // turn off the alarm
    else
    {
        adi_rtc_EnableAlarm(hRTC1, false);
				adi_rtc_EnableInterrupts (hRTC1, ADI_RTC_ALARM_INT, false);
    }
}

void     time_configureSensorStrobe(uint16_t frequency_hz)
{
    uint16_t    halfPeriodTicks;

    // Configure RTC handler, if not configured
    if (!RTC_configured)
    {
        adi_rtc_Open(
            1,
            RTC1Mem,
            ADI_RTC_MEMORY_SIZE,
            &hRTC1
        );

				adi_rtc_Enable(hRTC1, true);
        RTC_configured = true;
    }

    if (frequency_hz != 0)
    {
        // calculate period in 32768Hz ticks 
        halfPeriodTicks = 32768/(frequency_hz << 1);  // 32768/(freq*2)

        adi_rtc_SetAutoReloadValue(hRTC1, ADI_RTC_SS_CHANNEL_2, halfPeriodTicks, halfPeriodTicks);
				adi_rtc_EnableAutoReload(hRTC1, ADI_RTC_SS_CHANNEL_2, true);
        adi_rtc_EnableSensorStrobeOutput (hRTC1, ADI_RTC_SS_CHANNEL_2, true);
    }
    else
    {
        adi_rtc_EnableSensorStrobeOutput (hRTC1, ADI_RTC_SS_CHANNEL_2, false);
    }
}


//=========================== private =========================================
//==== IRQ handlers

// Publishing and SensorStrobe timer
void time_rtc1Alarm(void *pCBParam, uint32_t Event, void *pArg)
{
    uint32_t now;

    // determine which interupt we must handle, and issue upper layer calls (if any)
    if (Event & ADI_RTC_ALARM_INT)
    {
        // get actual time in 32768Hz ticks
        adi_rtc_GetCount(hRTC1, &now);

        // set and enable alarm
        adi_rtc_SetAlarm(hRTC1, now + time_vars.publishPeriod);
        adi_rtc_EnableAlarm(hRTC1, true);

        // call upper layer, if set
        if (time_vars.publishEventCb != NULL)
            time_vars.publishEventCb();
    }
}

// FSM timer
void time_tmr0Alarm(void *pCBParam, uint32_t Event, void *pArg)
{
    // stop the timer
    adi_tmr_Enable(ADI_TMR_DEVICE_GP0, false);

    // check the event and call the upper layer
    if (Event == ADI_TMR_EVENT_TIMEOUT)
    {
        if (time_vars.fsmEventCb != NULL)
            time_vars.fsmEventCb();
    }
}

//=========================== helpers =========================================
