

/***************************************************************************//**
  @addtogroup ltimer
  @{
  @file ltimer.h
  Файл содержит описание функций для работы с таймером. Все функции реализованы
  как inline-функции, так что файл содержит и реализацию функций
  @author Borisov Alexey <borisov@lcard.ru>
  *****************************************************************************/
#ifndef __LTIMER_H__
#define __LTIMER_H__

#include "lclock.h"
#include "lcspec.h"

/** Структура для описания состояния таймера */
typedef struct ltimer {
  t_lclock_ticks start; /**< Значение системного таймера, с которого ведется отсчет */
  t_lclock_ticks interval; /**< Интервал времени, после которого таймер считается истекшим */
} t_ltimer;


/***************************************************************************//**
  @brief Получить время запуска таймера

  @param[in] t          Указатель на состояние таймера
  @return               Значение времени запуска таймера в системных клоках
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_start_time(const t_ltimer *t) {
    return t->start;
}

/***************************************************************************//**
  @brief Получить время срабатывания таймера

  @param[in] t          Указатель на состояние таймера
  @return               Значение времени срабатывания таймера в системных клоках
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_expiration_time(const t_ltimer *t) {
    return t->start + t->interval;
}

/***************************************************************************//**
  @brief Получить установленный интервал для таймера

  @param[in] t          Указатель на состояние таймера
  @return               Значение установленного интервала в системных клоках
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_interval(const t_ltimer *t) {
    return t->interval;
}

/***************************************************************************//**
  @brief Получить установленный интервал для таймера в мс

  @param[in] t          Указатель на состояние таймера
  @return               Значение установленного интервала в мс
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_interval_ms(const t_ltimer *t) {
    return LTIMER_CLOCK_TICKS_TO_MS(t->interval);
}

/***************************************************************************//**
  @brief Установить временной интервал относительно заданного времени

  Функция устанавливает таймер на заданный интервал, аналогично ltimer_set(),
  но относительно заданного времени (значения системного счетчика), а не
  относительно текущего.
  С помощью функции ltimer_expired() можно в дальнейшем проверить, истек ли
  заданный интервал.
  @param[in] t           Указатель на состояние таймера
  @param[in] interval    Интервал, через который таймер истечет в системных клоках.
  @param[in] start_time  Значение системного счетчика, относительно которого
                         устанавливается временной интервал
 ******************************************************************************/
static LINLINE void ltimer_set_at(t_ltimer *t, t_lclock_ticks interval, t_lclock_ticks start_time) {
    t->interval = interval;
    t->start = start_time;
}

/***************************************************************************//**
  @brief Установить временной интервал

  Функция устанавливает таймер на заданный интервал относительно текущего времени.
  С помощью функции ltimer_expired() можно в дальнейшем проверить, истек ли
  заданный интервал.
  @param[in] t        Указатель на состояние таймера
  @param[in] interval Интервал, через который таймер истечет в системных клоках.
 ******************************************************************************/
static LINLINE void ltimer_set(t_ltimer *t, t_lclock_ticks interval) {
    ltimer_set_at(t, interval, lclock_get_ticks());
}

/***************************************************************************//**
  @brief Установить временной интервал в мс

  Функция устанавливает таймер на заданный интервал в милисекундах относительно
  текущего времени.
  С помощью функции ltimer_expired() можно в дальнейшем проверить, истек ли
  заданный интервал.
  @param[in] t        Указатель на состояние таймера
  @param[in] ms       Интервал, через который таймер истечет в милисекундах.
 ******************************************************************************/
static LINLINE void ltimer_set_ms(t_ltimer *t, unsigned ms) {
    ltimer_set(t, LTIMER_MS_TO_CLOCK_TICKS(ms));
}



/***************************************************************************//**
  @brief Сброс таймера.

  Данная функции перезапускает таймер на интервал, заданный в последнем ltimer_set()
  относительно времени последнего истечения таймера (а не текущего времени, как
  ltimer_restart()). По сути функция продлевает время истечения таймера на тот
  же интервал, что был задан. Интервалы между срабатыванием таймеров
  сохраняется во времени, в отличие от использования ltimer_restart().
  @param[in] t        Указатель на состояние таймера
  *****************************************************************************/
static LINLINE void ltimer_reset(t_ltimer *t) {
    t->start += t->interval;
}



/***************************************************************************//**
  @brief Перезапуск таймера с заданного времени.

  Данная функции перезапускает таймер на интервал, заданный в последнем ltimer_set(),
  относительно текущего времени, начиная с указанного времени.
  @param[in] t          Указатель на состояние таймера
  @param[in] start_time Значение системного счетчика, соответствующего времени,
                         с которого перезапускается таймер
  *****************************************************************************/
static LINLINE void ltimer_restart_at(t_ltimer *t, t_lclock_ticks time) {
    t->start = time;
}


/***************************************************************************//**
  @brief Перезапуск таймера.

  Данная функции перезапускает таймер на интервал, заданный в последнем ltimer_set(),
  относительно текущего времени.
  @note  Время срабатывания периодического таймер будет "плыть" при использовании
  данной функции (из-за случайной разницы между текущим временем и временем
  завершения предыдущего интервала). Для периодического таймера можно использовать
  ltimer_reset()
  @param[in] t        Указатель на состояние таймера  
  *****************************************************************************/
static LINLINE void ltimer_restart(t_ltimer *t) {
    ltimer_restart_at(t, lclock_get_ticks());
}

/***************************************************************************//**
  @brief Проверка, истечет ли заданный интервал в определенное время

  Функция проверяет, истечет ли установленный с помощью ltimer_set() интервал
  в моент времени, определяемый заданным значением системного счетчика.
    @param[in] t        Указатель на состояние таймера
    @param[in] exp_time Значение системного счетчика, для которого проверяется,
                        истечет ли таймер в данный момент
    @return             Не ноль, если интервал истечет, иначе --- ноль
 ******************************************************************************/
static LINLINE int ltimer_expired_at(const t_ltimer *t, t_lclock_ticks exp_time) {
    /** @note Сравнение именно разницы моментов времени с интервалом обеспечивает
     *        корректную работу в том числе при переходе счетчика тиков от максимального
     *        значения к нулевому.  */
    /** @note Использование знака > вместо >= необходимо, чтобы гарантировать, что
     *        заданный интервал точно истек. В связи с тем, время запуска таймера
     *        и момент увеличения тиков не синхронизированы, время от запуска таймера
     *        до первого увеличения тиков может быть произвольным от нуля до
     *        времени одного тика. Таким образом для выполнения условия проходит
     *        от interval до (interval + 1) тиков в зависимости от момента старта
     *        таймера (при >= было бы от (interval - 1) до interval и условие
     *        могло бы выполнится раньше нужного времени */
    return (t_lclock_ticks)(exp_time - t->start) > (t_lclock_ticks)t->interval;
}

/***************************************************************************//**
  @brief Проверка, истек ли заданный интервал

  Функция проверяет, истек ли заданный с помощью ltimer_set() интервал.
    @param[in] t        Указатель на состояние таймера
    @return             Не ноль, если интервал истек, иначе --- ноль
 ******************************************************************************/
static LINLINE int ltimer_expired(const t_ltimer *t) {
    return ltimer_expired_at(t, lclock_get_ticks());
}

/***************************************************************************//**
  @brief Получить время до истечения интервала от заданного момента

  Функция возвращает значение интервала времени (в системных клоках) от заданного
  момента до времени истечения установленного с помощью ltimer_set() интервала.
    @param[in] t        Указатель на состояние таймера
    @param[in] cur_time Значение системного счетчика, от которого считается время
                        до момента истечения интервала
    @return             Время (в клоках системного счетчика) до истечения интервала
*******************************************************************************/
static LINLINE t_lclock_ticks ltimer_expiration_from(const t_ltimer *t, t_lclock_ticks cur_time) {
    const t_lclock_ticks expired = (cur_time - t->start);
    return (expired > t->interval) ? 0 : t->interval - expired;
}

/***************************************************************************//**
  @brief Получить время до истечения интервала

  Функция возвращает значение интервала времени (в системных клоках) от текущего
  момента до времени истечения установленного с помощью ltimer_set() интервала.
    @param[in] t        Указатель на состояние таймера
    @return             Время (в клоках системного счетчика) до истечения интервала
*******************************************************************************/
static LINLINE t_lclock_ticks ltimer_expiration(const t_ltimer *t) {
    return ltimer_expiration_from(t, lclock_get_ticks());
}

#endif /* __LTIMER_H__ */

/** @} */

