/**
 * @file circbuf.h
 * @author Ondřej Hruška, 2016,2023
 *
 * Circular buffer / queue / stack.
 * Slots are pre-allocated, values are copied into the buffer.
 *
 * The buffer may be used as a stack, event queue or a simple buffer.
 *
 * MIT license
 */

#ifndef CIRCBUF_H
#define CIRCBUF_H

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

// Enable to zero a freed slots after pop, useful for debugging
#define CIRCBUF_ZERO_FREE_SLOTS

typedef uint32_t circbuf_size_t;

/** Instance structure - public to allow static allocation, but consider the structure internal matter */
struct circbuf_struct {
  void *buf;
  circbuf_size_t num_used;
  circbuf_size_t elem_size;
  circbuf_size_t cap;
  circbuf_size_t back;
//  circbuf_size_t front;
};

typedef struct circbuf_struct CircBuf;

/**
 * @brief Initialize a circular buffer
 *
 * @param[in,out] cb - pointer to the buffer to init, can be statically or dynamically allocated
 * @param buf  : backing buffer, can be statically or dynamically allocated
 * @param capacity  : buffer capacity
 * @param elem_size : size of one element
 */
void cbuf_init(CircBuf *cb, void *buf, circbuf_size_t capacity, circbuf_size_t elem_size);


/** Test for full buffer */
bool cbuf_full(const CircBuf *cb);


/** Test for empty buffer */
bool cbuf_empty(const CircBuf *cb);


/** Get the max capacity of the buffer */
circbuf_size_t cbuf_capacity(const CircBuf *cb);


/** Get the current number of items in the buffer */
circbuf_size_t cbuf_count(const CircBuf *cb);


/** Peek at the nth element (counted from back) */
bool cbuf_nth(const CircBuf *cb, circbuf_size_t num, void *dest);


/** Get a mutable reference to nth element (counted from back) */
void *cbuf_ptr_nth(const CircBuf *cb, circbuf_size_t num);


/** @brief Remove all data from buffer */
void cbuf_clear(CircBuf *cb);


/**
 * @brief Append a value to the buffer (FIFO)
 * @param cb     : buffer
 * @param source : pointer to a value (will be copied)
 * @return success
 */
bool cbuf_push(CircBuf *cb, const void *source);


/**
 * @brief Read a value from the buffer, return susccess.
 *
 * @param cb   : buffer
 * @param dest : read destination. If NULL, value is discarded.
 * @return success
 */
bool cbuf_pop(CircBuf *cb, void *dest);


/**
 * @brief Copy the frontmost element without changing the buffer
 * @param cb   : buffer
 * @param dest : read destination
 * @return success
 */
bool cbuf_peek(const CircBuf *cb, void *dest);


/**
 * @brief Get a mutable reference to the front element
 * @param cb   : buffer
 * @param dest : read destination
 * @return reference or NULL
 */
void * cbuf_ptr(const CircBuf *cb);


/**
 * @brief Push a value into the circbuf (LIFO).
 *
 * @param cb     : buffer
 * @param source : pointer to a value (will be copied)
 * @return success
 */
bool cbuf_push_back(CircBuf *cb, const void *source);


/**
 * @brief Read a value from the buffer, return susccess.
 *
 * @param cb   : buffer
 * @param dest : read destination. If NULL, value is discarded.
 * @return success
 */
bool cbuf_pop_back(CircBuf *cb, void *dest);


/**
 * @brief Copy the backmost element without changing the buffer
 * @param cb   : buffer
 * @param dest : read destination
 * @return success
 */
bool cbuf_peek_back(const CircBuf *cb, void *dest);


/**
 * @brief Get a mutable reference to the backmost element
 * @param cb   : buffer
 * @param dest : read destination
 * @return reference or NULL
 */
void* cbuf_ptr_back(const CircBuf *cb);

#endif // CIRCBUF_H