#ifndef _RUNTIME_LOG_BUF_H_
#define _RUNTIME_LOG_BUF_H_
/*
 * runtime cyclic log buffer
 *
 * utf8 ru_RU
 *
 *  Created on: 21.10.2015
 *      Author: a_lityagin
 *-----------------------------------------------------------------------------
 * \~russian
 *  задаю опреции работы с журналом реального времени. это циклический буфер
 *  записей вида rtlog_node - включает штамп события, его сообщение и аргументы.
 *
 *  эмулятор vprintf позволяет сохранять в журнал сообщения без затратной печати,
 *  а распечатать сообщение по чтении из журнала.
 *-----------------------------------------------------------------------------
 *  это порт журнала из uOS-embedded
 * */


#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <project-conf.h>
#include <c_compat.h>

#include <lib/ring_index.h>


#ifdef __cplusplus
extern "C" {
#endif

//  макс. количество аргументов сообщения в записи журнала
//  от этого количества зависит размер записи
#ifndef RTLOG_ARGS_LIMIT
#define RTLOG_ARGS_LIMIT 6
#endif

//  максимальное количество объединенных записей:
//  записи в буффере можно объединять с целью добавления аргументов в сообщение.
//  чем больше аргументов тем сложнее устроена печать rtlog_printfXXX
#ifndef RTLOG_ARGS_COUPLES
#define RTLOG_ARGS_COUPLES 1
#endif


union rtlog_args_t {
    uint32_t            uL[RTLOG_ARGS_LIMIT];
    int32_t             L[RTLOG_ARGS_LIMIT];
    float               F[RTLOG_ARGS_LIMIT];
    double              D[RTLOG_ARGS_LIMIT/2];
    int64_t             LL[RTLOG_ARGS_LIMIT/2];
    uint64_t            uLL[RTLOG_ARGS_LIMIT/2];
};
typedef union rtlog_args_t  rtlog_args_t;

union __PACKED rtlog_packed_args_t {
    uint32_t            uL[RTLOG_ARGS_LIMIT];
    int32_t             L[RTLOG_ARGS_LIMIT];
    float               F[RTLOG_ARGS_LIMIT];
    double              D[RTLOG_ARGS_LIMIT/2];
    int64_t             LL[RTLOG_ARGS_LIMIT/2];
    uint64_t            uLL[RTLOG_ARGS_LIMIT/2];
};

typedef union rtlog_packed_args_t   rtlog_packed_args_t;

typedef struct _rtlog_node_t {
    // индекс сообщения - всегда растет +1, использую для контроля целостности
    //  журнала, от его заворачивания
    unsigned        stamp;
    // метка реального времени
    //unsigned    rtc;

    const char*     msg;
    rtlog_args_t    args;
} rtlog_node;

typedef struct _rtlog_t {
    unsigned        stamp;
    ring_uindex_t   idx;
    rtlog_node*     store;
} rtlog;


//*\arg size - размер store в байтах. буфера выделяется как массив rtlog_node
//*            размером в степень2
void rtlog_init    ( rtlog* u, void* store, size_t size);

INLINE 
void rtlog_clear   ( rtlog* u) {ring_uindex_reset(&u->idx);}

INLINE
int rtlog_avail   ( rtlog* u ) {return (int)ring_uindex_avail(&u->idx);}

INLINE
int rtlog_empty   ( rtlog* u ) {return ring_uindex_empty(&u->idx);}

INLINE
rtlog_node* rtlog_head(rtlog* u)
{
    return u->store+ring_uindex_cur_read(&u->idx);
}

INLINE
rtlog_node* rtlog_tail(rtlog* u)
{
    return u->store+ring_uindex_cur_write(&u->idx);
}

// отбрасывает из журнала nrecs первых записей
INLINE
void rtlog_pop1 ( rtlog* u){
    ring_uindex_get(&u->idx);
}

INLINE
void rtlog_pop     ( rtlog* u, unsigned nrecs ){
    ring_uindex_getn(&u->idx, nrecs);
}

// печать декодированной 1й записи в журнале.
// особенности формата: печать параметров типа float ведется форматом: %h(f|e|g)
// \return - количество декодироанных записей
NOT_THROWS( int rtlog_dumpsn_first( rtlog* u, char* buf, unsigned bufsz)  __noexcept );

//* печатает records_count последних записей журнала в dst
//void rtlog_dump_last( rtlog* u, stream_t *dst, unsigned records_count);

//-----------------------------------------------------------------------------
// сообщения печатаются строками форматирования, указываемыми в аргументе fmt
//  по дефолту rtlog_dumpsn_first хочет из этого индентификатора получить
//  реальную строку форматирования, через эту функцию.
//  приложение может перегрузить этй функцию своим мапером.
//  базовая версия функции просто фозвращает исходную строку
const char* rtlog_fmt_of_msg(const char *fmt);





//-----------------------------------------------------------------------------
//* это эмуляторы аналогичных функций печати. количество сохраняемых аргументов
//*     не более RTLOG_ARGS_LIMIT
//* !!! передача аргументов float/double не гарантируется!
NOT_THROWS( int rtlog_vprintf ( rtlog* u, unsigned argsn, const char *fmt, va_list args) __noexcept  );
//* 1й параметр va_args трактуется как const char *fmt
NOT_THROWS( int rtlog_printf  ( rtlog* u, unsigned argsn, ...)  __noexcept );
NOT_THROWS( int rtlog_puts    ( rtlog* u, const char *str) __noexcept );
NOT_THROWS( int rtlog_printf1 ( rtlog* u, const char *fmt, long arg1)  __noexcept );
NOT_THROWS( int rtlog_printf2 ( rtlog* u, const char *fmt, long arg1, long arg2)  __noexcept );
NOT_THROWS( int rtlog_printf4 ( rtlog* u, const char *fmt
                    , long arg1, long arg2, long arg3, long arg4)  __noexcept );
NOT_THROWS( int rtlog_printfLL( rtlog* u, const char *fmt, int64_t arg1)  __noexcept );
NOT_THROWS( int rtlog_printf1D( rtlog* u, const char *fmt, double arg1)  __noexcept );
NOT_THROWS( int rtlog_printf1F( rtlog* u, const char *fmt, float arg1)  __noexcept );
NOT_THROWS( int rtlog_printf2F( rtlog* u, const char *fmt, float arg1, float arg2)  __noexcept );
NOT_THROWS( int rtlog_printf4F( rtlog* u, const char *fmt
                    , float arg1, float arg2, float arg3, float arg4)  __noexcept );
NOT_THROWS( int rtlog_printfL3F( rtlog* u, const char *fmt
                    , long arg1, float arg2, float arg3, float arg4)  __noexcept );



//-----------------------------------------------------------------------------
// для корректной печати аргументов принтеру printf, веду анализ строки формата
//      - какие типы печатаются в '%'
//  формат составляется массивом битовых полей шириной stwide-битов
enum ArgStyle{
        //<! ширина бит алемента стиля
      stwide  = 4
        //<! бит-маска первого рагумента
    , stMASK  = (1<<stwide)-1
        //<! нет аргументов
    , stNO    = 0
    //<! long32
    , stL     = 1
    //<! float32
    , stF     = 2
    //<! long64
    , stLL    = 3
    //<! double
    , stD     = 4
    //<! char*
    , stS     = 8+stL
};
typedef enum ArgStyle ArgStyle;

// декодер типа вызова из формата строки. возвращает в результате массив значений
//  ArgStyle. эти значения объединены в общее число
unsigned fmt_style(const char* fmt);



#ifdef __cplusplus
}
#endif


#if defined(__cplusplus) //&& 0
// для С++ добавлю перегруженых функций rtlog_printf

INLINE
int rtlog_printf( rtlog* u, const char *fmt) {
    return rtlog_puts(u, fmt);
}

template<typename T1>
INLINE
int rtlog_printf( rtlog* u, const char *fmt, T1 arg1) {
    return rtlog_printf1(u, fmt, arg1);
}


template<typename T1, typename T2>
INLINE
int rtlog_printf( rtlog* u, const char *fmt, T1 arg1, T2 arg2){
    return rtlog_printf2(u, fmt, arg1, arg2);
}

template<typename T1, typename T2, typename T3, typename T4>
INLINE
int rtlog_printf( rtlog* u, const char *fmt
                    , T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
    return rtlog_printf4(u, fmt, arg1, arg2, arg3, arg4);
}

INLINE
int rtlog_printf( rtlog* u, const char *fmt, int64_t arg1) {
    return rtlog_printfLL(u, fmt, arg1);
}

INLINE
int rtlog_printf( rtlog* u, const char *fmt, double arg1){
    return rtlog_printf1D(u, fmt, arg1);
}

INLINE
int rtlog_printf( rtlog* u, const char *fmt, float arg1){
    return rtlog_printf2F(u, fmt, arg1, 0.0f);
}


INLINE
int rtlog_printf( rtlog* u, const char *fmt, float arg1, float arg2){
    return rtlog_printf2F(u, fmt, arg1, arg2);
}

INLINE
int rtlog_printf( rtlog* u, const char *fmt
                    , float arg1, float arg2, float arg3, float arg4) {
    return rtlog_printf4F(u, fmt, arg1, arg2, arg3, arg4);
}


INLINE
int rtlog_printf( rtlog* u, const char *fmt
                    , long arg1, float arg2, float arg3, float arg4) {
    return rtlog_printfL3F(u, fmt, arg1, arg2, arg3, arg4);
}

#endif


#endif
