#ifndef LTRSRV_CYCLEBUF_H
#define LTRSRV_CYCLEBUF_H


#include "ltrsrv_defs.h"
#include <stdlib.h>
#include <string.h>

typedef  struct {
    uint32_t put_pos;
    uint32_t get_pos;
    uint32_t wrds_cnt;
    uint32_t size;
    uint32_t *data;
} t_cycle_buf;


/* Функции управления циклическим буфером */

/* начальная инициализация буфера заданного размера */
#define CYCLEBUF_INIT(pbuf, init_size) do { \
        (pbuf)->put_pos = 0;\
        (pbuf)->get_pos = 0; \
        (pbuf)->wrds_cnt = 0; \
        (pbuf)->size = (init_size); \
        (pbuf)->data = (uint32_t*)malloc((pbuf)->size * sizeof((pbuf)->data[0])); \
    } while(0)

/* очистка памяти буфера */
#define CYCLEBUF_CLOSE(pbuf) do { \
        free((pbuf)->data); \
        (pbuf)->data = NULL; \
        (pbuf)->put_pos = 0; \
        (pbuf)->get_pos = 0; \
        (pbuf)->wrds_cnt = 0; \
        (pbuf)->size = 0; \
    } while(0)

/* удаление данных из буфера */
#define CYCLEBUF_CLEAR(pbuf) do { \
        (pbuf)->put_pos = 0; \
        (pbuf)->get_pos = 0; \
        (pbuf)->wrds_cnt = 0; \
    } while(0)



#define CYCLEBUF_IS_VALID(pbuf)   ((pbuf)->data != NULL)
#define CYCLEBUF_IS_EMPTY(pbuf)   ((pbuf)->wrds_cnt == 0)
#define CYCLEBUF_IS_FULL(pbuf)    ((pbuf)->wrds_cnt == (pbuf)->size)
#define CYCLEBUF_SIZE(pbuf)       ((pbuf)->size)
#define CYCLEBUF_FILL_SIZE(pbuf)  ((pbuf)->wrds_cnt)
#define CYCLEBUF_FREE_SIZE(pbuf)  ((pbuf)->size - (pbuf)->wrds_cnt)
#define CYCLEBUF_PUTPTR(pbuf)     &((pbuf)->data[(pbuf)->put_pos])
#define CYCLEBUF_GETPTR(pbuf)     &((pbuf)->data[(pbuf)->get_pos])

/* обновление указатели при извлечении одного слова */
#define CYCLEBUF_GET_WORD_DONE(pbuf) do { \
        if (++(pbuf)->get_pos == (pbuf)->size) \
            (pbuf)->get_pos = 0; \
        (pbuf)->wrds_cnt--; \
    } while(0)

/* обновление указатели при записи одного слова */
#define CYCLEBUF_PUT_WORD_DONE(pbuf) do { \
        if (++(pbuf)->put_pos == (pbuf)->size) \
            (pbuf)->put_pos = 0; \
        (pbuf)->wrds_cnt++; \
    } while(0)

/* запись слова с увеличением указателя */
#define CYCLEBUF_PUT_WORD(pbuf, wrd) do { \
        *CYCLEBUF_PUTPTR(pbuf) = wrd; \
        CYCLEBUF_PUT_WORD_DONE(pbuf); \
    } while(0)


/* Количество слов, которые можно положить в непрерывную часть буфера */
#define CYCLEBUF_PUT_CONT_FREE_SIZE(pbuf) (((pbuf)->put_pos >= (pbuf)->get_pos) ? \
                    ((pbuf)->size - (pbuf)->put_pos) : \
                    ((pbuf)->get_pos - (pbuf)->put_pos))

/* Обновление указателя, после перемещения done_size слов в непрервыную часть буфера */
#define CYCLEBUF_PUT_CONT_DONE(pbuf, done_size) do { \
        (pbuf)->put_pos += (uint32_t)(done_size); \
        if ((pbuf)->put_pos == (pbuf)->size) \
            (pbuf)->put_pos = 0; \
        (pbuf)->wrds_cnt += (uint32_t)(done_size); \
    } while(0)

/* Количество слов, которые можно извлечь из непрерывной части буфера */
#define CYCLEBUF_GET_CONT_RDY_SIZE(pbuf)  (((pbuf)->put_pos > (pbuf)->get_pos) ? \
                     ((pbuf)->put_pos - (pbuf)->get_pos) : \
                     ((pbuf)->size - (pbuf)->get_pos))

/* Обновление указателя, после извлечения done_size слов из непрервыной части буфера */
#define CYCLEBUF_GET_CONT_DONE(pbuf, done_size) do { \
        (pbuf)->get_pos += (uint32_t)(done_size); \
        if ((pbuf)->get_pos == (pbuf)->size) \
            (pbuf)->get_pos = 0; \
        (pbuf)->wrds_cnt -= (uint32_t)(done_size); \
    } while(0)

/* Изменение размера буфера с сохранением по возможности данных */
#define CYCLEBUF_REALLOC(pbuf, new_size) ltrsrv_cyclebuf_realloc(pbuf, new_size)

extern void ltrsrv_cyclebuf_realloc(t_cycle_buf *pbuf, uint32_t new_size);


#endif // LTRSRV_CYCLEBUF_H
