/**
 * Copyright 2013 Cabot Communications.  All rights reserved.
 *
 * @created Jan 7, 2013
 * 
 * @brief [TODO: Provide a brief description about the contents of this file]
 * 
 */

#include "frost_circular_buffer.h"
#include <stdlib.h>

#define INCREMENT_WRITE_INDEX(cb)   (cb->write_index = (cb->write_index + 1) % cb->capacity)
#define INCREMENT_READ_INDEX(cb)    (cb->read_index = (cb->read_index + 1) % cb->capacity)

#if 0
#define LOG(x)
#else
#define LOG(x)        printf x
#endif

CircularBuffer*
circular_buffer_create_new(frost_uint capacity)
{
    CircularBuffer *cb = (CircularBuffer *) malloc(sizeof(CircularBuffer));
    if (cb == NULL)
    {
        return NULL;
    }

    frost_uint8 *buf = malloc(sizeof(frost_uint8) * capacity);
    if (buf == NULL)
    {
        free(cb);
        return NULL;
    }

    LOG(("Created a new circular buffer with capacity: %d\n", capacity));

    cb->capacity = capacity;
    cb->write_index = 0;
    cb->read_index = 0;
    cb->num_items = 0;
    cb->buffer = buf;

    return cb;
}

void
circular_buffer_destroy(CircularBuffer* cb)
{
    LOG(("Destroying circular buffer\n"));
    if (cb != NULL)
    {
        if (cb->buffer != NULL)
        {
            free(cb->buffer);
        }

        free(cb);
        cb->capacity = 0;
        cb->write_index = 0;
        cb->read_index = 0;
        cb->num_items = 0;
    }
}

frost_int
circular_buffer_add(CircularBuffer *cb, const frost_uint8 *data, const frost_uint data_len)
{
    if (data == NULL)
    {
        return -1;
    }

    if (data_len > circular_buffer_avail_space(cb))
    {
        // Not available space in buffer
        LOG(("No space available in buffer, cannot add data\n"));
        return -1;
    }

    int i = 0;
    for(i = 0; i < data_len; ++i)
    {
        cb->buffer[cb->write_index] = data[i];
        INCREMENT_WRITE_INDEX(cb);
        cb->num_items += 1;
    }

    LOG(("Added %d elements to circular buffer: capacity(%d), remaining (%d)\n",
            data_len, cb->capacity, circular_buffer_avail_space(cb)));
    return i;
}


frost_int
circular_buffer_retrieve(CircularBuffer *cb, frost_uint8 *data, frost_uint data_len)
{
    frost_int avail = (cb->num_items > data_len) ? data_len : cb->num_items;

    frost_int i = 0;
    for(i = 0; i < avail; ++i)
    {
        data[i] = cb->buffer[cb->read_index];
        INCREMENT_READ_INDEX(cb);
        cb->num_items -= 1;
    }

    LOG(("Retrieved %d elements from circular buffer: capacity(%d), remaining(%d)\n",
            i, cb->capacity, circular_buffer_avail_space(cb)));
    return i;
}

frost_uint
circular_buffer_avail_space(const CircularBuffer *cb)
{
    frost_uint result = cb->capacity - cb->num_items;

    return result;
}

frost_bool
circular_buffer_empty(const CircularBuffer *cb)
{
    return (cb->num_items == 0) ? frost_true : frost_false;
}
