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

#include "lclock_arch.h"
#include "lcspec.h"

#ifndef LCLOCK_TICKS_PER_SECOND
    /** Константа, определяющее количество отсчетов системного счетчика в секунду */
    #define LCLOCK_TICKS_PER_SECOND 1000
#endif

#ifndef LCLOCK_TICKS_CUSTOM_CONV_VAL_TYPE
    /** Промежуточный тип, используемый при преобразовании из значения системного счетчика
        в мс и обратно.
        По умолчнию используется тот же тип, что и для значения счетчика, однако
        при необходимости возможно переопределить данный тип на целое большей разрядности,
        чтобы избежать возможного переполнения из-за домножения перед делением во
        время преобразования */
    typedef t_lclock_ticks t_lclock_ticks_conv_val;
#endif

#ifndef LCLOCK_TICKS_PER_SECOND_CONV_THRESHOLD
    /** Пороговое значение количества отсчетов системного счетчика в секунду.
     *  Если значение #LCLOCK_TICKS_PER_SECOND выше данного порога,
     *  то функция преобразования из мс в значение счетчика #LTIMER_MS_TO_CLOCK_TICKS
     *  выполняет деление #LTIMER_MS_TO_CLOCK_TICKS перед умножением для того чтобы
     *  избежать переполнения промежуточного результата, пусть и с добавлением погрешности
     *  за счет округления при делении */
    #define LCLOCK_TICKS_PER_SECOND_CONV_THRESHOLD 100000
#endif


#if !defined LCLOCK_TICKS_PER_SECOND_NOT_COMP_DEF && (LCLOCK_TICKS_PER_SECOND == 1000)
    /** Макрос для перевода интервала в мс в количество отсчетов системного счетчика */
    #define LTIMER_MS_TO_CLOCK_TICKS(tout)  (tout)
    /** Макрос для перевода интервала в количестве отсчетов системного счетчика в интервал в мс */
    #define LTIMER_CLOCK_TICKS_TO_MS(tout)  (tout)
#else
    /* Округление при перводе идет в большую сторону, чтобы в результате возможной
     * ошибки округления интервал в отсчетах системного таймера
     * получился в любом случае не меньше запрошенного  в мс
     * Для варианта LCLOCK_TICKS_PER_SECOND больше порога используем деление до
     * умножения для избежания сокращения диапазона возможных входных параметров времени
     * из-за переполнения типа t_lclock_ticks_conv_val.
     */
    #if defined LCLOCK_TICKS_PER_SECOND_NOT_COMP_DEF || (LCLOCK_TICKS_PER_SECOND > LCLOCK_TICKS_PER_SECOND_CONV_THRESHOLD)
        #define LTIMER_MS_TO_CLOCK_TICKS(tout) ((t_lclock_ticks_conv_val)(tout)*((LCLOCK_TICKS_PER_SECOND + 999)/1000))
    #else
        #define LTIMER_MS_TO_CLOCK_TICKS(tout) (((t_lclock_ticks_conv_val)(tout) * LCLOCK_TICKS_PER_SECOND + 999)/1000)
    #endif


    #define LTIMER_CLOCK_TICKS_TO_MS(tout) ((t_lclock_ticks_conv_val)(tout)*1000/LCLOCK_TICKS_PER_SECOND)
#endif


/** Маска для выделения старшего бита в типе #t_lclock_ticks */
#define LCLOCK_HIGH_BIT        (((t_lclock_ticks)-1)/2 + 1)
/** Проверка, установлен ли старший бит для типа #t_lclock_ticks. Если
    переменная данного типа содержит интервал в отсчетах системного счетчика,
    полученный как разность значений отсчетов счетчика, то данный макрос
    можно использовать как признак, отрицательный ли интервал */
#define LCLOCK_IS_NEG(clock)   ((clock) & LCLOCK_HIGH_BIT)


#ifdef __cplusplus
extern "C" {
#endif
/** Инициализация системного счетчика заданным значением и его запуск */
void lclock_init_val(t_lclock_ticks init_val);
/** Получение текущего значения системного счетчика */
t_lclock_ticks lclock_get_ticks(void);

/** Запуск системного счетчика в случае, если его начальное значение не важно */
static LINLINE void lclock_init(void) {
    lclock_init_val(0);
}

/** Проверка, был ли уже инициализирован системный счетчик */
char lclock_is_initialized(void);

/** Запрет работы системного счетчика */
void lclock_disable(void);

/** Для некоторых портов есть возможность реализовать пользовательскую функцию,
 * которая будет вызываться раз в мс. Для этого необходимо глобально определить
 * LCLOCK_USE_USER_MS_CB и реализовать функцию lclock_user_ms_cb() */
#ifdef LCLOCK_USE_USER_MS_CB
    extern void lclock_user_ms_cb(void);
#endif


#ifdef __cplusplus
}
#endif


#endif /* LCLOCK_H_ */

/** @} */
