#ifndef E502TSTP_H
#define E502TSTP_H

#include "x502api.h"
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/** @addtogroup tstpfunc_list Функции для работы с метками времени
    @{  **/

/***************************************************************************//**
  @addtogroup tstptype_list Константы и перечисления
  @{
  *****************************************************************************/

#define TSP_NSEC_PER_SEC (1000000000)

#define TSP_WRD1_SSEC_LEN   (24)
#define TSP_WRD2_SSEC_LEN   (7)
#define TSP_WRD2_SEC_LEN   (19)
#define TSP_WRD3_SEC_LEN   (13)
#define TSP_SSEC_WIDTH   (31)


#define TSP_WRD_NUM(wrd) (((wrd) >> 26) & 3)
#define TSP_WRD0_ADC_CLK_NUM_MASK   (0x3ffffff)
#define TSP_WRD1_LOCK_MASK   (1 << 25)
#define TSP_WRD1_FMARK_MASK   (1 << 24)
#define TSP_WRD1_SSEC_MASK   ((1 << TSP_WRD1_SSEC_LEN) - 1)
#define TSP_WRD2_SEC_MASK   (0x3ffff80)
#define TSP_WRD2_SSEC_MASK   ((1 << TSP_WRD2_SSEC_LEN) - 1)
#define TSP_WRD2_SECSSEC_MASK   ((1 << (TSP_WRD2_SEC_LEN + TSP_WRD2_SSEC_LEN)) - 1)
#define TSP_WRD3_SEC_MASK   ((1 << TSP_WRD3_SEC_LEN) - 1)

#define TSP_WRD1_IS_FMARK(wrd)  (!!((wrd) & TSP_WRD1_FMARK_MASK))
#define TSP_WRD1_IS_LOCK(wrd)  (!!((wrd) & TSP_WRD1_LOCK_MASK))
#define TSP_WRD1_GET_SSEC(wrd)  ((wrd) & TSP_WRD1_SSEC_MASK)
#define TSP_WRD2_GET_SECSSEC(wrd)  ((uint64_t)((wrd) & TSP_WRD2_SECSSEC_MASK) << TSP_WRD1_SSEC_LEN)
#define TSP_WRD2_GET_SSEC(wrd)  ((wrd) & TSP_WRD2_SSEC_MASK)
#define TSP_WRD3_GET_SEC(wrd)  ((uint64_t)((wrd) & TSP_WRD3_SEC_MASK) << (TSP_WRD1_SSEC_LEN + TSP_WRD2_SSEC_LEN + TSP_WRD2_SEC_LEN))
#define SSEC_MAX (0x7fffffff)
#define TSTP_SSEC_TO_NSEC(time) (time > 0 ? ((uint32_t)((((double)((time) & SSEC_MAX)) / (1U << TSP_SSEC_WIDTH)) * TSP_NSEC_PER_SEC)) : ((uint32_t)((((double)((time*(-1)) & SSEC_MAX)) / (1U << TSP_SSEC_WIDTH)) * TSP_NSEC_PER_SEC)))
#define TSTP_SECSSEC_TO_SEC(time) (time > 0 ? (uint32_t)((time) >> TSP_SSEC_WIDTH) : (uint32_t)((time * -1) >> TSP_SSEC_WIDTH))
#define TSTP_SECSSEC_TO_SEC_DOUBLE(time) (((double)(time)) / (1U << TSP_SSEC_WIDTH))
#define TSTP_SEC_TO_SSEC(time) (((uint64_t)(time)) << TSP_SSEC_WIDTH)

/** Тип данных для хранения времени
 * Время хранится в секундах прошедшее с начала этой эпохи (00:00:00 UTC, 1 Января 1970 года)
 * Дробный формат хранения 32.31: 32 целых бит, 31 дробных бит,
 * старшие 32 бита - секунды, младшие 31 бит сабсекунды = 1 / (1<<31) секунд
  + сабсекунды,
 */
typedef uint64_t t_x502_tstptime;

/** Структура для хранения контекста при обработке потока слов "на ввод" с включенными метками времени */
typedef struct {
    /** значение слов последней метки времени */
    uint32_t wrd[4];
    /** частота АЦП */
    uint32_t adc_freq;
    /** частота DIN */
    uint32_t din_freq;
    /** время  первой метки времени */
    t_x502_tstptime tstp_start_time;
    /** признак, что первая метка времени получена */
    bool tstp_mark_rcvd;
    /** значение текущего обрабатываемого слова */
    uint32_t cur_wrd;
    /** кол-во обработанных слов из потока */
    uint32_t processed_wrds;
    /** кол-во слов АЦП после последней метки времени */
    uint32_t adcwrds_after_tstp;
    /** кол-во слов DIN после последней метки времени */
    uint32_t dinwrds_after_tstp;
    /** общее кол-во слов после последней метки времени */
    uint32_t wrds_after_tstp;
    /** время последней метки времени */
    t_x502_tstptime last_tstp_time;
} t_x502_tstp_state;

/** @} */

/***************************************************************************//**
    @addtogroup func_tstp Функции для работы с метками времени
    @{
*******************************************************************************/

/***************************************************************************//**
    @brief Инициализация tstp_state, в нем хранится текущий контекст для операций с метками времени из потока "на ввод"

    Данная функция инициализирует структуру в которой хранится контекст для работы с метками времени
    Необходимо указывать на частоту АЦП и DIN т.к. время для слов между двумя метками
    будет считаться в зависимости от частоты.

   @param[in] tstp_state Указатель на существующую струтуру t_x502_tstp_state
   @param[in] adc_freq Частота АЦП
   @param[in] din_freq Частота DIN
*******************************************************************************/
X502_EXPORT(void) X502_tstp_init(t_x502_tstp_state *tstp_state, uint32_t adc_freq, uint32_t din_freq);

/** @brief Обработать очередное слово wrd из потока "на ввод"
*
* Функция должна быть вызвана только один раз и последовательно для каждого слова полученного из X502_Recv
*
* @param[in] tstp_state Указатель на струтуру t_x502_tstp_state предварительно инициализированную через tstp_init()
* @param[in] wrd Слово из потока "на ввод"
*/
X502_EXPORT(void) X502_tstp_process_wrd(t_x502_tstp_state *tstp_state, uint32_t wrd);

/** @brief Узнать время текущего обработанного слова
*
* Узнать время текущего слова которое до этого было обработано функцией tstp_process_wrd()
* Формат времени: 32бита - секунды, 31 бит сабсекунды = 1 / (1<<31) секунд
*
* @param[in] tstp_state Указатель на струтуру t_x502_tstp_state предварительно инициализированную через tstp_init()
* @param[in] ret Указатель на t_x502_tstptime по которому будет сохранено расчитанное значение времени для текущего слова
*/
X502_EXPORT(void) X502_tstp_get_curwrd_time(t_x502_tstp_state *tstp_state, t_x502_tstptime *ret);

/** @brief Возвращает признак того что часы синхронизированы
*
* Возвращает признак "захват PTP" для текущего обработанного слова
*
* @param[in] tstp_state Указатель на струтуру t_x502_tstp_state предварительно инициализированную через tstp_init()
*
* @return true: присутствует признак "захват PTP" для текущего обработанного слова, false: признак "захват PTP" отсутствует
*/
X502_EXPORT(bool) X502_tstp_get_lock(t_x502_tstp_state *tstp_state);

/** @} */
/** @} */

#ifdef __cplusplus
}
#endif

#endif //E502TSTP_H
