#ifndef __SIMPLE_DATA_FIFO_H__
#define __SIMPLE_DATA_FIFO_H__

#include <stdint.h>
#include "stddef.h"

#include <base/bt_common.h>
/**
 * @brief   Define a Memory FIFO thread safe, and can full use pool.
 * @details API 1 and 2.
 *   read_index and write_index is normal process.
 *   unhandle_index for spec use.
 *   Thread-A will update read_index, for get buffer from FIFO.
 *   Thread-B will update write_index, for put buffer to FIFO
 *               update unhandle_index, for use the buffer Thread-A had processed.
 */
typedef struct {
    uint8_t n;         /* Number of buffers */
    uint8_t s;               /* Stride between elements */
    uint8_t r;               /* Read. Read index */
    uint8_t w;               /* Write. Write index */
    uint8_t* m;
} simple_data_fifo_t;


void simple_data_fifo_init(simple_data_fifo_t* sfifo, uint8_t n, uint8_t s, uint8_t* m);
uint8_t simple_data_fifo_is_empty(simple_data_fifo_t* sfifo);
uint8_t simple_data_fifo_size(simple_data_fifo_t* sfifo);
uint8_t simple_data_fifo_enqueue_mem(simple_data_fifo_t* sfifo, uint8_t* val);
uint8_t simple_data_fifo_enqueue_get(simple_data_fifo_t* sfifo, uint8_t **mem);
void simple_data_fifo_enqueue(simple_data_fifo_t* sfifo, uint8_t idx);
uint8_t* simple_data_fifo_dequeue(simple_data_fifo_t* sfifo);
uint8_t* simple_data_fifo_dequeue_peek(simple_data_fifo_t* sfifo);
uint8_t simple_data_fifo_avail_count_get(simple_data_fifo_t* sfifo);


#define SIMPLE_DATA_FIFO_DEFINE(_name, _num, _data_size)                                            \
    static simple_data_fifo_t _name;                                          \
    static uint8_t _name##_data_storage[_num + 1][MROUND(_data_size)];

#define SIMPLE_DATA_FIFO_INIT(_name, _num, _data_size)                                              \
    simple_data_fifo_init(&_name, _num, MROUND(_data_size), (uint8_t *)_name##_data_storage)


#define SIMPLE_DATA_FIFO_PTR_DEFINE(_name)                                            \
    simple_data_fifo_t _name;

#define SIMPLE_DATA_FIFO_DATA_STORAGE_SIZE(_num, _data_size) \
    sizeof(uint8_t) * (MROUND((_num + 1) * MROUND(_data_size)))

#define SIMPLE_DATA_FIFO_STORAGE_SIZE(_num, _data_size) \
    SIMPLE_DATA_FIFO_DATA_STORAGE_SIZE(_num, _data_size)

#define SIMPLE_DATA_FIFO_PTR_INIT(_name, _num, _data_size, _ram_base)                                              \
    simple_data_fifo_init(&_name, _num, MROUND(_data_size), (uint8_t*)((uint8_t *)_ram_base))

#define SIMPLE_DATA_FIFO_PTR_RAM_POOL_INIT(_name, _num, _data_size, _ram_pool)                                              \
    do {\
        uint8_t* _ram_base = simple_ram_pool_malloc(ram_pool, SIMPLE_DATA_FIFO_STORAGE_SIZE(_num, _data_size)); \
        SIMPLE_DATA_FIFO_PTR_INIT(_name, _num, _data_size, _ram_base); \
    } while(false);


#endif