#include "soft_rtc.h"
#include <stdint.h>
#include <time.h>

uint8_t tmp_time[4];
uint32_t tmp_time32;

extern RTC_HandleTypeDef hrtc;
static HAL_StatusTypeDef RTC1_WriteTimeCounter(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter);
static uint32_t RTC1_ReadTimeCounter(RTC_HandleTypeDef *hrtc);
static HAL_StatusTypeDef RTC1_ExitInitMode(RTC_HandleTypeDef *hrtc);
static HAL_StatusTypeDef RTC1_EnterInitMode(RTC_HandleTypeDef *hrtc);


uint32_t get_Current_Time(){
	return RTC1_ReadTimeCounter(&hrtc);
}

void set_Time(uint32_t unix_time){
	RTC1_WriteTimeCounter(&hrtc, unix_time);
}

uint8_t to_bcd(int value) {
    return ((value / 10) << 4) | (value % 10);
}

void unix_to_bcd(uint32_t unix_time, uint8_t *time) {
    struct tm *tm_info;
    time_t raw_time = (time_t)unix_time;
    tm_info = gmtime(&raw_time);

    time[0] = to_bcd(tm_info->tm_sec);
    time[1] = to_bcd(tm_info->tm_min);
    time[2] = to_bcd(tm_info->tm_hour);
    time[3] = to_bcd(tm_info->tm_mday);
    time[4] = to_bcd(tm_info->tm_mon + 1); // tm_mon is 0-11
    time[5] = to_bcd((tm_info->tm_year + 1900) % 100); // Year in 2 digits
    time[6] = to_bcd((tm_info->tm_year + 1900) / 100); // Century in 2 digits
}

void writeTimeReg(uint8_t reg_number, uint8_t value){
	tmp_time[reg_number] = value;
	if(reg_number == 3) set_Time((tmp_time[0])+(tmp_time[1]<<8)+(tmp_time[2]<<16)+(tmp_time[3]<<24));
};

uint8_t getTimeReg(uint8_t reg_number){
	if(reg_number == 0){
		tmp_time32 = get_Current_Time();
		return tmp_time32 & 0xFF;
	}else if(reg_number == 1){
		return (tmp_time32>>8) & 0xFF;
	}else if(reg_number == 2){
		return (tmp_time32>>16) & 0xFF;
	}else if(reg_number == 3){
		return (tmp_time32>>24) & 0xFF;
	}else{
		return 0x00;
	}
}

/**
  * @brief  Read the time counter available in RTC_CNT registers.
  * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @retval Time counter
  */
static uint32_t RTC1_ReadTimeCounter(RTC_HandleTypeDef *hrtc)
{
  uint16_t high1 = 0U, high2 = 0U, low = 0U;
  uint32_t timecounter = 0U;

  high1 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);
  low   = READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT);
  high2 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);

  if (high1 != high2)
  {
    /* In this case the counter roll over during reading of CNTL and CNTH registers,
       read again CNTL register then return the counter value */
    timecounter = (((uint32_t) high2 << 16U) | READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT));
  }
  else
  {
    /* No counter roll over during reading of CNTL and CNTH registers, counter
       value is equal to first value of CNTL and CNTH */
    timecounter = (((uint32_t) high1 << 16U) | low);
  }

  return timecounter;
}

/**
  * @brief  Write the time counter in RTC_CNT registers.
  * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @param  TimeCounter: Counter to write in RTC_CNT registers
  * @retval HAL status
  */
static HAL_StatusTypeDef RTC1_WriteTimeCounter(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Set Initialization mode */
  if (RTC1_EnterInitMode(hrtc) != HAL_OK)
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Set RTC COUNTER MSB word */
    WRITE_REG(hrtc->Instance->CNTH, (TimeCounter >> 16U));
    /* Set RTC COUNTER LSB word */
    WRITE_REG(hrtc->Instance->CNTL, (TimeCounter & RTC_CNTL_RTC_CNT));

    /* Wait for synchro */
    if (RTC1_ExitInitMode(hrtc) != HAL_OK)
    {
      status = HAL_ERROR;
    }
  }

  return status;
}

/**
  * @brief  Enters the RTC Initialization mode.
  * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @retval HAL status
  */
static HAL_StatusTypeDef RTC1_EnterInitMode(RTC_HandleTypeDef *hrtc)
{
  uint32_t tickstart = 0U;

  tickstart = HAL_GetTick();
  /* Wait till RTC is in INIT state and if Time out is reached exit */
  while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t)RESET)
  {
    if ((HAL_GetTick() - tickstart) >  RTC_TIMEOUT_VALUE)
    {
      return HAL_TIMEOUT;
    }
  }

  /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_DISABLE(hrtc);


  return HAL_OK;
}

/**
  * @brief  Exit the RTC Initialization mode.
  * @param  hrtc   pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @retval HAL status
  */
static HAL_StatusTypeDef RTC1_ExitInitMode(RTC_HandleTypeDef *hrtc)
{
  uint32_t tickstart = 0U;

  /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);

  tickstart = HAL_GetTick();
  /* Wait till RTC is in INIT state and if Time out is reached exit */
  while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t)RESET)
  {
    if ((HAL_GetTick() - tickstart) >  RTC_TIMEOUT_VALUE)
    {
      return HAL_TIMEOUT;
    }
  }

  return HAL_OK;
}

