/*!
 * \file      timer.c
 *
 * \brief     Timer objects and scheduling management implementation
 *
 * \copyright Revised BSD License, see section \ref LICENSE.
 *
 * \code
 *                ______                              _
 *               / _____)             _              | |
 *              ( (____  _____ ____ _| |_ _____  ____| |__
 *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 *               _____) ) ____| | | || |_| ____( (___| | | |
 *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
 *              (C)2013-2017 Semtech
 *
 * \endcode
 *
 * \author    Miguel Luis ( Semtech )
 *
 * \author    Gregory Cristian ( Semtech )
 */

/******************************************************************************
 * @file    stm32_timer.c
 * @author  MCD Application Team
 * @brief   Time server utility
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under BSD 3-Clause license,
 * the "License"; You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                        opensource.org/licenses/BSD-3-Clause
 *
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "stm32_timer.h"

/** @addtogroup TIMER_SERVER
  * @{
  */

/* Private macro -----------------------------------------------------------*/
/**
 * @defgroup TIMER_SERVER_private_macro TIMER_SERVER private macros
 *  @{
 */
/**
  * @brief macro definition to initialize a critical section.
  *
  */
#ifndef UTIL_TIMER_INIT_CRITICAL_SECTION
  #define UTIL_TIMER_INIT_CRITICAL_SECTION( )
#endif

/**
  * @brief macro definition to enter a critical section.
  *
  */
#ifndef UTIL_TIMER_ENTER_CRITICAL_SECTION
  #define UTIL_TIMER_ENTER_CRITICAL_SECTION( )   UTILS_ENTER_CRITICAL_SECTION( )
#endif

/**
  * @brief macro definition to exit a critical section.
  *
  */
#ifndef UTIL_TIMER_EXIT_CRITICAL_SECTION
  #define UTIL_TIMER_EXIT_CRITICAL_SECTION( )    UTILS_EXIT_CRITICAL_SECTION( )
#endif
/**
  *  @}
  */
 
/* Private variables -----------------------------------------------------------*/
/**
 * @defgroup TIMER_SERVER_private_varaible TIMER_SERVER private variable
 *  @{
 */

/**
  * @brief Timers list head pointer
  *
  */
static UTIL_TIMER_Object_t *TimerListHead = NULL;

/**
  *  @}
  */

/**
 * @defgroup TIMER_SERVER_private_function TIMER_SERVER private function
 *  @{
 */
bool TimerExists( UTIL_TIMER_Object_t *TimerObject );

/**
  *  @}
  */

/* Functions Definition ------------------------------------------------------*/
/**
  * @addtogroup TIMER_SERVER_exported_function
  *  @{
  */

UTIL_TIMER_Status_t UTIL_TIMER_Init(void)
{
  UTIL_TIMER_INIT_CRITICAL_SECTION();
  TimerListHead = NULL;
  return UTIL_TimerDriver.InitTimer();
}

UTIL_TIMER_Status_t UTIL_TIMER_DeInit(void)
{
  return UTIL_TimerDriver.DeInitTimer();
}

UTIL_TIMER_Status_t UTIL_TIMER_Create( UTIL_TIMER_Object_t *TimerObject, uint32_t PeriodValue, UTIL_TIMER_Mode_t Mode, void ( *Callback )( void *), void *Argument)
{
	  if((TimerObject != NULL) && (Callback != NULL))
	  {
		osTimerDef(TimerObject, (void*)(const void *)Callback);
		TimerObject->Handle = osTimerCreate(osTimer(TimerObject), Mode, Argument);

		return UTIL_TIMER_OK;
	  }
	  else
	  {
	    return UTIL_TIMER_INVALID_PARAM;
	  }
}

UTIL_TIMER_Status_t UTIL_TIMER_Start( UTIL_TIMER_Object_t *TimerObject)
{
  UTIL_TIMER_Status_t  ret = UTIL_TIMER_OK;

  if(( TimerObject != NULL ) && ( TimerExists( TimerObject ) == true ) )
  {
	  osTimerStart( TimerObject->Handle,  TimerObject->ReloadValue);
  }
  else
  {
	  ret = UTIL_TIMER_INVALID_PARAM;
  }
  return ret;
}

UTIL_TIMER_Status_t UTIL_TIMER_StartWithPeriod( UTIL_TIMER_Object_t *TimerObject, uint32_t PeriodValue)
{
  UTIL_TIMER_Status_t  ret = UTIL_TIMER_OK;

  if(NULL == TimerObject)
  {
    ret = UTIL_TIMER_INVALID_PARAM;
  }
  else
  {
    TimerObject->ReloadValue = UTIL_TimerDriver.ms2Tick(PeriodValue);
    if(TimerExists(TimerObject))
    {
      (void)UTIL_TIMER_Stop(TimerObject);
    }
    ret = UTIL_TIMER_Start(TimerObject);
  }
  return ret;
}

UTIL_TIMER_Status_t UTIL_TIMER_Stop( UTIL_TIMER_Object_t *TimerObject )
{
  UTIL_TIMER_Status_t  ret = UTIL_TIMER_OK;

  if(NULL == TimerObject)
  {
	  ret = UTIL_TIMER_INVALID_PARAM;
  }
  else
  {
	  osTimerStop( TimerObject->Handle );
  }
  return ret;
}

UTIL_TIMER_Status_t UTIL_TIMER_SetPeriod(UTIL_TIMER_Object_t *TimerObject, uint32_t NewPeriodValue)
{
  UTIL_TIMER_Status_t  ret = UTIL_TIMER_OK;
  
  if(NULL == TimerObject)
  {
	  ret = UTIL_TIMER_INVALID_PARAM;
  }
  else
  {
    TimerObject->ReloadValue = NewPeriodValue;
    if(TimerExists(TimerObject))
    {
      (void)UTIL_TIMER_Stop(TimerObject);
      ret = UTIL_TIMER_Start(TimerObject);
    }
  }
  return ret;
}

UTIL_TIMER_Status_t UTIL_TIMER_SetReloadMode(UTIL_TIMER_Object_t *TimerObject, UTIL_TIMER_Mode_t ReloadMode)
{
  UTIL_TIMER_Status_t  ret = UTIL_TIMER_OK;

  if(NULL == TimerObject)
  {
	ret = UTIL_TIMER_INVALID_PARAM;
  }
  else
  {
	TimerObject->Mode = ReloadMode;
  }
  return ret;
}

uint32_t UTIL_TIMER_IsRunning( UTIL_TIMER_Object_t *TimerObject )
{
  if( TimerObject != NULL )
  {
    return TimerObject->Handle != NULL;
  }
  else
  {
    return 0;
  }
}

UTIL_TIMER_Time_t UTIL_TIMER_GetCurrentTime(void)
{
  uint32_t now = UTIL_TimerDriver.GetTimerValue( );
  return  UTIL_TimerDriver.Tick2ms(now);
}

UTIL_TIMER_Time_t UTIL_TIMER_GetElapsedTime(UTIL_TIMER_Time_t past )
{
  uint32_t nowInTicks = UTIL_TimerDriver.GetTimerValue( );
  uint32_t pastInTicks = UTIL_TimerDriver.ms2Tick( past );
  /* intentional wrap around. Works Ok if tick duation below 1ms */
  return UTIL_TimerDriver.Tick2ms( nowInTicks- pastInTicks );
}

/**
  *  @}
  */

/**************************** Private functions *******************************/

/**
  *  @addtogroup TIMER_SERVER_private_function
  *
  *  @{
  */
/**
 * @brief Check if the Object to be added is not already in the list
 *
 * @param TimerObject Structure containing the timer object parameters
 * @retval 1 (the object is already in the list) or 0
 */
bool TimerExists( UTIL_TIMER_Object_t *TimerObject )
{
  return TimerObject->Handle != NULL;
}

/**
  *  @}
  */

/**
  *  @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
