#pragma once

#include "defs.h"
#include "Func.h"

S_BEGIN_DECLS


/**
 * @file
 * @defgroup SRingBuffer SRingBuffer
 * @addtogroup SRingBuffer
 * @{
 * An SRingBuffer is a special data structure that is circular in nature.
 *
 * This is not a represent for a queue, this is only for byte sized data
 * elements, like text and such.
 *
 * @section Theory
 * The theory behind a circular structure is simple: Never have to deal with
 * empty slots in an array, only permit indirect, non-indexed access.
 *
 <pre>
    [b|b|b|b|b|b|b|b|b|b| | | | ]
     ^                 ^
     start             end
     front             back



    [ | | | | | |b|b|b|b| | | | ]
                 ^     ^
             start     end
             front     back



    [b|b| | | | | | | |b|b|b|b|b]
       ^               ^
     end             start
     back            front
 </pre>
 *
 */

/**
 * The SRingBuffer is an opaque data structure that represent an array that
 * is circular in nature.
 */
typedef struct SRingBuffer SRingBuffer;

/**
 * Create a new SRingBuffer objects.
 *
 * @param size The number of items to allocate space for.
 * @param free_func The function that is used to free the data.
 *
 * @note It is recomended to have a big size of the buffer to make room for
 *       each item that should be stored plus a bit extra. (if you expect to
 *       have \f$n\f$ items allocate \f$n + \frac{n}{2}\f$ slots.)
 */
S_EXPORTED
SRingBuffer *
s_ring_buffer_new (size_t size);

/**
 * Free an SRingBuffer.
 *
 * @param self The SRingBuffer to free.
 * @param free_data Set to @c TRUE to free the data.
 */
S_EXPORTED
void
s_ring_buffer_free (SRingBuffer * self);

/**
 * determine if buffer is empty.
 */
S_EXPORTED
sboolean
s_ring_buffer_is_empty (SRingBuffer * self);

/**
 * Add data to the front of the ringbuffer.
 *
 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
void
s_ring_buffer_push (SRingBuffer * self,
                    sbyte data,
                    sboolean * err);

/**
 * add an item to the front of the ringbuffer.

 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
void
s_ring_buffer_push_front (SRingBuffer * self,
                          subyte data,
                          sboolean * err);

/**
 * Pop an item from the front of the array.

 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
spointer
s_ring_buffer_pop (SRingBuffer * self,
                   sboolean * err);

/**
 * Pop the front of the array.

 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
spointer
s_ring_buffer_pop_front (SRingBuffer * self,
                         sboolean * err);

/**
 * Peek at the byte at the end of the buffer.

 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
sbyte
s_ring_buffer_peek (SRingBuffer * self,
                    sboolean * err);

/**
 * Peek at the byte at the front of the buffer.
 *
 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
sbyte
s_ring_buffer_peek_front (SRingBuffer * self,
                          sboolean * err);

/**
 * Reallocate an SRingBuffer to a new size for the internal array.
 *
 * @param self The SRingBuffer to reallocate.
 * @param len The new length.
 * @param err Set to TRUE on error, FALSE otherwise.
 */
S_EXPORTED
void
s_ring_buffer_realloc (SRingBuffer * self,
                       size_t len
                       sboolean * err);

/**
 * Get the current length of the ring buffer.
 */
S_EXPORTED
size_t
s_ring_buffer_len (SRingBuffer * self);

/**
 * Get the total size of the internal array.
 */
S_EXPORTED
size_t
s_ring_buffer_size (SRingBuffer * self);

/**
 * Iterate over the bytes in the SRingBuffer.
 *
 * This is the only valid way to iterate over the bytes in an SRingBuffer.
 *
 * @param self The SRingBuffer to iterate over.
 * @param func Pointer to a function that is run on each byte.
 * @param user_data Data that is provided to the function when doing the
 *                  iteration.
 * @param err Set to TRUE on error, FALSE otherwise.
 *
 *
 * @note
 * This does not use pop or push operations, and is thus the only way to keep
 * the data and still be able to iterate over it.
 *
 * The signature of the function provided should be as follows
 @code{C}
void
my_for_each_func (SRingBuffer * buffer,
                  sbyte item,
                  spointer user_data) {
  // code here
}
 @endcode
 *
 * To prevent warnings cast the function using the @c FOREACHFUNC() macro.
 */
S_EXPORTED
void
s_ring_buffer_for_each (SRingBuffer * self,
                        ForEachFunc func,
                        spointer user_data,
                        sboolean * err);

/**
 * @}
 */

S_END_DECLS
