#include <stdio.h>
#include <string.h>
#include "bsp/bsp_driver.h"

#define IS_LEAP_YEAR(year)      ((year % 4 == 0) && ( year % 100 != 0) || (year % 400 == 0))

//static uint32_t time_ticks = 0;
uint32_t time_ticks = 0;
uint32_t rtc_second = 0;
#define POWER_FIRSTFLAG_REG BKP_DATA_0
#define POWER_FIRSTFLAG_VALUE 0x5AA5



unsigned char check_rtc_time(void)
{
	int rtcTime = rtc_counter_get();

	if(rtcTime > YEAR_20171201_UTS  && rtcTime < YEAR_20571201_UTS)
		return 1;
	else
		return 0;
}


unsigned char check_utc_stamp_valid(uint32_t utc)
{
	if(utc > YEAR_20171201_UTS  && utc < YEAR_20571201_UTS)
		return 1;
	else
		return 0;
}

/* get utc timestamp --> second */
uint32_t rtc_get_timestamp(void){
	return rtc_counter_get();
}


/* get utc timestamp --> macro second */
uint64_t rtc_get_timestamp_ms(void){
	return (uint64_t)rtc_counter_get() * 1000 ;
}

void rtc_set_time_with_utc_second(uint32_t utc_s){
    /* wait until last write operation on RTC registers has finished */
    rtc_lwoff_wait();
    /* change the current time */
    rtc_counter_set(utc_s);
    rtc_lwoff_wait();
}

static void gd32_rtc_first_init(void){
	//keep rtc running and inited while chip reset
	if (gd32_bkp_first_startup()){
		/* enable PMU and BKPI clocks */
		rcu_periph_clock_enable(RCU_BKPI);
		rcu_periph_clock_enable(RCU_PMU);
		/* allow access to BKP domain */
		pmu_backup_write_enable();

		/* reset backup domain */
		bkp_deinit();
		rcu_osci_on(RCU_IRC40K);
		rcu_osci_stab_wait(RCU_IRC40K);

		/* select RCU_RTCSRC_IRC40K as RTC clock source */
		rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);

		/* enable RTC Clock */
		rcu_periph_clock_enable(RCU_RTC);

		/* wait for RTC registers synchronization */
		rtc_register_sync_wait();

		/* wait until last write operation on RTC registers has finished */
		rtc_lwoff_wait();

		rtc_interrupt_enable(RTC_INT_SECOND);
		/* wait until last write operation on RTC registers has finished */
		rtc_lwoff_wait();

		//set 1s a tick
		rtc_prescaler_set(40000-1);

		/* wait until last write operation on RTC registers has finished */
		rtc_lwoff_wait();

		bkp_write_data(POWER_FIRSTFLAG_REG, POWER_FIRSTFLAG_VALUE);

	}else{
        /* allow access to BKP domain */
        rcu_periph_clock_enable(RCU_PMU);
        pmu_backup_write_enable();

        rcu_osci_on(RCU_IRC40K);
        rcu_osci_stab_wait(RCU_IRC40K);
        
        /* select RCU_RTCSRC_IRC40K as RTC clock source */
        rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);
        
        /* enable RTC Clock */
        rcu_periph_clock_enable(RCU_RTC);
		
        /* wait for RTC registers synchronization */
        rtc_register_sync_wait();
        rtc_lwoff_wait();
        /* enable the RTC second and alarm interrupt*/
        rtc_interrupt_enable(RTC_INT_SECOND);
		rtc_lwoff_wait();
	}
}

static void gd32_rtc_enable_second_irq(int enable){
	if (enable){
		nvic_irq_enable(RTC_IRQn, RTC_IRQ_PRIORITY, 0U);
	}else{
		nvic_irq_disable(RTC_IRQn);
	}
}

void gd32_rtc_init(void){
	gd32_rtc_first_init();

	//3?ˇ§o?ˇ§o?¨¤1??¨¤?RTC alarmˇ§o?3?
	gd32_rtc_stop_alarm();
#ifdef GD32F30X_CL
	nvic_irq_disable(RTC_ALARM_IRQn);
#else
	nvic_irq_disable(RTC_Alarm_IRQn);
#endif
	exti_init(EXTI_17, EXTI_INTERRUPT, EXTI_TRIG_RISING);
	exti_flag_clear(EXTI_17);
	exti_interrupt_enable(EXTI_17);

	gd32_rtc_enable_second_irq(1);
}


int rtc_lwoff_wait_timeout(void)
{
	int wait_count = 0xffff;
    /* loop until LWOFF flag is set */
    while(RESET == (RTC_CTL & RTC_CTL_LWOFF)){
		if (wait_count -- <= 0){
			return -1;
		}
    }
	return 0;
}


static int set_rtc_alarm(uint32_t sencod){
	uint32_t current_count=0;
	do {
		current_count = rtc_counter_get();
	}while(current_count != rtc_counter_get());
	
	rtc_alarm_config(current_count + sencod);
	return rtc_lwoff_wait_timeout();
}


int gd32_rtc_start_alarm(uint32_t second){
#ifdef GD32F30X_CL
	nvic_irq_enable(RTC_ALARM_IRQn, RTC_IRQ_PRIORITY, 0U);
#else
    nvic_irq_enable(RTC_Alarm_IRQn, RTC_IRQ_PRIORITY, 0U);
#endif
    exti_flag_clear(EXTI_17);
    exti_interrupt_enable(EXTI_17);
	rtc_register_sync_wait();
    rtc_flag_clear(RTC_FLAG_ALARM);
	rtc_lwoff_wait();
	rtc_interrupt_enable(RTC_INT_ALARM);// second means a shot here
	rtc_lwoff_wait();
	return set_rtc_alarm(second);
}


void gd32_rtc_stop_alarm(void){
    exti_interrupt_disable(EXTI_17);
    rtc_flag_clear(RTC_FLAG_ALARM);	
	rtc_lwoff_wait();
	rtc_interrupt_disable(RTC_INT_ALARM);
	rtc_lwoff_wait();
}


int gd32_rtc_update_alarm(uint32_t sencod){
	return set_rtc_alarm(sencod);
}

uint32_t rtc_irq_times = 0;
static alarm_handler s_handler = NULL;

void gd32_rtc_set_alarm_handler(alarm_handler handler){
	s_handler = handler;
}
void RTC_Alarm_IRQHandler(void)
{
    if (rtc_flag_get(RTC_FLAG_ALARM) != RESET)
	{
       /* clear the RTC second interrupt flag*/
      	rtc_flag_clear(RTC_FLAG_ALARM);
		if (s_handler != NULL){
			s_handler();
		}
		rtc_irq_times++;
    }
	exti_interrupt_flag_clear(EXTI_17);
}


void RTC_IRQHandler(void){
	if (rtc_flag_get(RTC_FLAG_OVERFLOW /*RTC_INT_FLAG_OVERFLOW*/) != RESET)
	{
		/* clear the RTC overflow interrupt flag*/
		rtc_flag_clear(RTC_FLAG_OVERFLOW);
	}

	if (rtc_flag_get(RTC_FLAG_SECOND) != RESET)
	{
		rtc_second++;
		/* clear the RTC second interrupt flag*/
		rtc_flag_clear(RTC_FLAG_SECOND);
	}
}
