#include "sr_rtc.h"

#include "sr_log.h"

typedef enum
{
    RTC_CLK_SRC_TYPE_HSE_DIV32 = 0x01,
    RTC_CLK_SRC_TYPE_LSE = 0x02,
    RTC_CLK_SRC_TYPE_LSI = 0x03,
} RTC_CLK_SRC_TYPE;


static BSP_RTC_INFO_T _rtcInfo[] =
{
  {
          .name = "rtc0",
          .srId = SR_RTC0,

  },

};

static SR_RTC_DEV_T gs_rtcDevice[sizeof(_rtcInfo) / sizeof(BSP_RTC_INFO_T)] = {0};

static uint32_t SynchPrediv, AsynchPrediv;

/**
 * @brief  Configures the RTC Source Clock Type.
 * @param Clk_Src_Type specifies RTC Source Clock Type.
 *   This parameter can be on of the following values:
 *     @arg RTC_CLK_SRC_TYPE_HSE128
 *     @arg RTC_CLK_SRC_TYPE_LSE
 *     @arg RTC_CLK_SRC_TYPE_LSI
 * @param Is_First_Cfg_RCC specifies Is First Config RCC Module.
 *   This parameter can be on of the following values:
 *     @arg true
 *     @arg false
 * @param Is_Rst_Bkp specifies Whether Reset The Backup Area
 *   This parameter can be on of the following values:
 *     @arg true
 *     @arg false
 */
static void RTC_CLKSourceConfig(RTC_CLK_SRC_TYPE Clk_Src_Type, bool Is_First_Cfg_RCC, bool Is_Rst_Bkp)
{
    assert_param(IS_CLKSRC_VALUE(ClkSrc));
    assert_param(IS_FLCFG_VALUE(FirstLastCfg));
    /* Enable the PWR clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
    /* Allow access to RTC */
    PWR_BackupAccessEnable(ENABLE);
    /* Disable RTC clock */
    RCC_EnableRtcClk(DISABLE);
    if (RTC_CLK_SRC_TYPE_HSE_DIV32 == Clk_Src_Type)
    {
        // log_info("\r\n RTC_ClkSrc Is Set HSE/32! \r\n");
        if (true == Is_First_Cfg_RCC)
        {
            /* Enable HSE */
            RCC_EnableLsi(DISABLE);
            RCC_ConfigHse(RCC_HSE_ENABLE);
            while (RCC_WaitHseStable() == ERROR)
            {
            }
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_HSE_DIV32);
        }
        else
        {
            RCC_EnableLsi(DISABLE);
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_HSE_DIV32);
            /* Enable HSE */
            RCC_ConfigHse(RCC_HSE_ENABLE);
            while (RCC_WaitHseStable() == ERROR)
            {
            }
        }
        SynchPrediv = 0x7A0; // 8M/32 = 250KHz
        AsynchPrediv = 0x7F; // value range: 0-7F
    }
    else if (RTC_CLK_SRC_TYPE_LSE == Clk_Src_Type)
    {
        // log_info("\r\n RTC_ClkSrc Is Set LSE! \r\n");
        if (true == Is_First_Cfg_RCC)
        {
            /* Enable the LSE OSC32_IN PC14 */
            RCC_EnableLsi(DISABLE); // LSI is turned off here to ensure that only one clock is turned on
#if (_TEST_LSE_BYPASS_)
            RCC_ConfigLse(RCC_LSE_BYPASS);
#else
            RCC_ConfigLse(RCC_LSE_ENABLE);
#endif
            while (RCC_GetFlagStatus(RCC_LDCTRL_FLAG_LSERD) == RESET)
            {
            }
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSE);
        }
        else
        {
            /* Enable the LSE OSC32_IN PC14 */
            RCC_EnableLsi(DISABLE);
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSE);
#if (_TEST_LSE_BYPASS_)
            RCC_ConfigLse(RCC_LSE_BYPASS);
#else
            RCC_ConfigLse(RCC_LSE_ENABLE);
#endif
            while (RCC_GetFlagStatus(RCC_LDCTRL_FLAG_LSERD) == RESET)
            {
            }
        }
        SynchPrediv = 0xFF;  // 32.768KHz
        AsynchPrediv = 0x7F; // value range: 0-7F
    }
    else if (RTC_CLK_SRC_TYPE_LSI == Clk_Src_Type)
    {
        // log_info("\r\n RTC_ClkSrc Is Set LSI! \r\n");
        if (true == Is_First_Cfg_RCC)
        {
            /* Enable the LSI OSC */
            RCC_EnableLsi(ENABLE);
            while (RCC_GetFlagStatus(RCC_CTRLSTS_FLAG_LSIRD) == RESET)
            {
            }
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSI);
        }
        else
        {
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSI);
            /* Enable the LSI OSC */
            RCC_EnableLsi(ENABLE);
            while (RCC_GetFlagStatus(RCC_CTRLSTS_FLAG_LSIRD) == RESET)
            {
            }
        }
        SynchPrediv = 0x14A; // 41828Hz
        AsynchPrediv = 0x7F; // value range: 0-7F
    }
    else
    {
        SR_LOG(ERR," RTC_ClkSrc Value is error! ");
    }
    /* Enable the RTC Clock */
    RCC_EnableRtcClk(ENABLE);
    RTC_WaitForSynchro();
}

static void Calendar_Init(void)
{
    RTC_DateType RTC_DateStructure;
    RTC_TimeType RTC_TimeStructure;
    RTC_InitType RTC_InitStructure;

    /* Configure the RTC data register and RTC prescaler */
    RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;
    RTC_InitStructure.RTC_SynchPrediv = SynchPrediv;
    RTC_InitStructure.RTC_HourFormat = RTC_24HOUR_FORMAT;
    /* Check on RTC init */
    if (RTC_Init(&RTC_InitStructure) == ERROR)
    {
        SR_LOG(ERR,"//******* RTC Prescaler Config failed **********//");
    }

    // Date
    RTC_DateStructure.WeekDay = 0;
    RTC_DateStructure.Date = 1;
    RTC_DateStructure.Month = 1;
    RTC_DateStructure.Year = 23;
    // Time
    RTC_TimeStructure.H12 = RTC_AM_H12;   //24小时制
    RTC_TimeStructure.Hours = 23;
    RTC_TimeStructure.Minutes = 00;
    RTC_TimeStructure.Seconds = 00;
    if (RTC_SetDate(RTC_FORMAT_BIN, &RTC_DateStructure) == ERROR)
    {
        SR_LOG(ERR, ">> !! RTC Set Date failed. !! <<");
        return;
    }
    if (RTC_ConfigTime(RTC_FORMAT_BIN, &RTC_TimeStructure) == ERROR)
    {
        SR_LOG(ERR, ">> !! RTC Set Time failed. !! <<");
        return;
    }
}


SR_DEVICE_T *BspRtcOpen(SR_RTC_ID_E rtcId)
{
	uint8_t i;
	uint8_t devicenNum = sizeof(_rtcInfo) / sizeof(BSP_RTC_INFO_T);
	SR_RTC_DEV_T *rtcDev = SR_NULL;

	if(rtcId != SR_RTC0) return SR_NULL;
	
	rtcDev = &gs_rtcDevice[0];
	rtcDev->parent.deviceData = &_rtcInfo[0]; // 绑定设备数据

	
	/**默认初始化**/			
	RTC_CLKSourceConfig(RTC_CLK_SRC_TYPE_LSE, true, true);  //时钟初始化
	Calendar_Init();   // 初始日历设置

	return &rtcDev->parent;
}

int BspRtcClose(SR_DEVICE_T *dev)
{
	if(dev==SR_NULL) return SR_INVALID;
	return 0;
}

/**
 * @brief 获取rtc时间(UTC+0)
 *
 * @param time  日期时间结构体
 * @return 0成功，负值失败
 */
int BspRtcGet(SR_DEVICE_T *dev, SR_RTC_TIME_T *time)
{
	int ret = -1;

	if (dev == SR_NULL || dev->deviceData == SR_NULL || time==SR_NULL) return SR_INVALID;

	BSP_RTC_INFO_T *rtcInfo = (BSP_RTC_INFO_T *)dev->deviceData;

	if(SR_RTC0 != rtcInfo->srId) return SR_INVALID;
	
	// 读取时钟值
	RTC_TimeType RTC_TimeStructure;
	RTC_DateType RTC_DateStructure;
	RTC_GetTime(RTC_FORMAT_BIN, &RTC_TimeStructure); // 获取RTC时钟值
	RTC_GetDate(RTC_FORMAT_BIN, &RTC_DateStructure);

	time->tmYear = RTC_DateStructure.Year+2000;
	time->tmMon = RTC_DateStructure.Month;
	time->tmMday = RTC_DateStructure.Date;
	time->tmHour = RTC_TimeStructure.Hours;
	time->tmMin = RTC_TimeStructure.Minutes;
	time->tmSec = RTC_TimeStructure.Seconds;
	time->tmWday = RTC_DateStructure.WeekDay;


	return  SR_OK;
}

/**
 * @brief 设置rtc时间(UTC+0)
 *
 * @param time  日期时间结构体
 * @return 0成功，负值失败
 */
int BspRtcSet(SR_DEVICE_T *dev, SR_RTC_TIME_T *time)
{
	int ret = -1;

	if (dev == SR_NULL || dev->deviceData == SR_NULL || time==SR_NULL) return SR_INVALID;
	SR_ASSERT(IS_RTC_TIME(time));

	BSP_RTC_INFO_T *rtcInfo = (BSP_RTC_INFO_T *)dev->deviceData;

	if(SR_RTC0 != rtcInfo->srId) return SR_INVALID;

	RTC_TimeType RTC_TimeStructure;
	RTC_DateType RTC_DateStructure;

	RTC_DateStructure.Year = time->tmYear-2000;
	RTC_DateStructure.Month = time->tmMon;
	RTC_DateStructure.Date = time->tmMday;
	RTC_TimeStructure.Hours = time->tmHour;
	RTC_TimeStructure.Minutes = time->tmMin;
	RTC_TimeStructure.Seconds = time->tmSec;
	if(RTC_SetDate(RTC_FORMAT_BIN, &RTC_DateStructure) != SUCCESS)
	{
		return SR_FAIL;
	} 
	if(RTC_ConfigTime(RTC_FORMAT_BIN, &RTC_TimeStructure) != SUCCESS)
	{
		return SR_FAIL;
	}
	
	return  SR_OK;
}


int BspRtcWkupEnable(SR_DEVICE_T *dev, uint8_t enable,uint16_t time)
{
	if (dev == SR_NULL || dev->deviceData == SR_NULL) return SR_INVALID;

	BSP_RTC_INFO_T *rtcInfo = (BSP_RTC_INFO_T *)dev->deviceData;

	if(SR_RTC0 != rtcInfo->srId) return SR_INVALID;

	if(enable==1)
	{
		RTC_EnableWakeUp(DISABLE); // 此处必须先关掉wakeup 中断唤醒，否则RTC无法按正常设置时间进入唤醒中断
		EXTI_InitType EXTI_InitStructure;
		NVIC_InitType NVIC_InitStructure;
		EXTI_ClrITPendBit(EXTI_LINE20);
		EXTI_InitStructure.EXTI_Line = EXTI_LINE20;
		EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
		EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
		EXTI_InitStructure.EXTI_LineCmd = ENABLE;
		EXTI_InitPeripheral(&EXTI_InitStructure);
		/* Enable the RTC WakeUp Interrupt */
		NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
		/* wake up clock select */
		RTC_ConfigWakeUpClock(RTC_WKUPCLK_CK_SPRE_16BITS);
		/* wake up timer value */
		RTC_SetWakeUpCounter(time - 1);  //1HZ时钟
		/* Enable the RTC Wakeup Interrupt */
		RTC_ConfigInt(RTC_INT_WUT, ENABLE);
		RTC_EnableWakeUp(ENABLE);

	}
	else
	{
		RTC_ConfigInt(RTC_INT_WUT, DISABLE);
		RTC_EnableWakeUp(DISABLE);
	}	
	
	return  SR_OK;
}

void RTC_WKUP_IRQHandler(void)
{
    if (RTC_GetITStatus(RTC_INT_WUT) != RESET)
    {
        RTC_ClrIntPendingBit(RTC_INT_WUT);
        EXTI_ClrITPendBit(EXTI_LINE20);
        
        gs_rtcDevice[SR_RTC0].cb(SR_RTC_EVT_WAUP,gs_rtcDevice[SR_RTC0].cbArgs);
    }
}


int BspRtIntCbReg(SR_DEVICE_T *dev, RtcIntCb cb, void *args)
{
	if (dev == SR_NULL || dev->deviceData == SR_NULL) return SR_INVALID;

	BSP_RTC_INFO_T *rtcInfo = (BSP_RTC_INFO_T *)dev->deviceData;

	if(SR_RTC0 != rtcInfo->srId) return SR_INVALID;
	
	gs_rtcDevice[SR_RTC0].cb = cb;
	gs_rtcDevice[SR_RTC0].cbArgs = args;

	return  SR_OK;
}