#ifndef RINGBUFFER_H
#define RINGBUFFER_H

#include <stdbool.h>

// Data layout:                ↓head
// buffer = [ 4, undef, undef, 1, 2, 3 ]
// where 1 is the oldest element and 4 the newest.
// Enqueue adds the element after 4 and dequeue removes 1.
struct ringbuffer {
	int head;
	int size;
	void (*destroy_element)(void*);
	int buffer_n;
	void** buffer;
};

/**
 * Allocate memory and initialize a circular queue with given capacity
 *
 * A ringbuffer, or circular queue, is like a regular queue, but if it's full when you add an
 * element, it displaces the oldest element to make place. As such, adding elements never fails.
 *
 * Memory: this function allocates memory for the queue (free it with rbfr_destroy). The
 * destroy_element function user has to provide must free an element's memory.
 *
 * @param  capacity         maximum amount of elements to accept without dropping oldest
 * @param  destroy_element  function called when the queue has to drop an element, with the element
 *                          passed as argument
 * @return pointer to ringbuffer
 */
struct ringbuffer* rbfr_create(const int capacity, void (*destroy_element)(void*));

/**
 * Free the queue's memory
 *
 * Memory: all elements still present are freed using the destroy_element function provided by user
 * in rbfr_create.
 */
void rbfr_destroy(struct ringbuffer* this);

int rbfr_size(const struct ringbuffer* this);

int rbfr_capacity(const struct ringbuffer* this);

/**
 * Add one element at the end
 *
 * If the queue is full (if size==capacity), the oldest element is removed to make place. As such,
 * this operation does not fail if the queue is full.
 *
 * Memory: if an element has to be removed to make place, it is freed using the destroy_element
 * function provided by user in rbfr_create.
 */
void rbfr_enqueue(struct ringbuffer* this, void* element);

/**
 * Return first element without removing it from the buffer
 *
 * Memory: do not free the element's memory, it still lives in the ringbuffer.
 */
bool rbfr_peek(const struct ringbuffer* this, void** element);

/**
 * Remove first element and return it
 *
 * Memory: user is responsible for freeing the element's memory.
 */
bool rbfr_dequeue(struct ringbuffer* this, void** element);

/**
 * Remove all elements
 *
 * Memory: this function destroys elements using the destroy_element function provided by user in
 * rbfr_create.
 */
void rbfr_clear(struct ringbuffer* this);


#endif // RINGBUFFER_H
