/**************************************/
/* INCLUDES */

#include "round_buf.h"


/*******************************************/
/* FUNCTIONS */

round_buf::round_buf()
{
    // Create a new empty round buffer.
    buf = NULL;
    alloc_size = 1;
    read_index = 0;
    write_index = 0;
}

round_buf::round_buf(int buf_size)
{
    // Create a new round buffer of buf_size bytes.
    // Arguments:
    // Buffer size.
    buf = NULL;

    set_size(buf_size);
}

round_buf::~round_buf()
{
    clean_up();
}

round_buf::round_buf(round_buf & other)
{
    buf = NULL;

    *this = other;
}

round_buf & round_buf::operator=(round_buf & other)
{
    if (&other == this) {
	return *this;
    }

    clean_up();

    alloc_size = other.alloc_size;
    read_index = other.read_index;
    write_index = other.write_index;
    buf = new char[alloc_size];

    memcpy(buf, other.buf, alloc_size);

    return *this;
}

int round_buf::size()
{
    // Return the size of the buffer.
    return (alloc_size - 1);
}

void round_buf::set_size(int new_size)
{
    // Change the size of the buffer to new_size. The content of the buffer
    // is lost, and the buffer behaves as it has just been created.
    // Arguments:
    // New size.
    assert(new_size >= 0);

    clean_up();

    // We allocate one more slot than requested for housekeeping.
    alloc_size = new_size + 1;
    read_index = 0;
    write_index = 0;

    buf = new char[alloc_size];
}

void round_buf::clean_up()
{
    delete[]buf;
}

bool round_buf::is_empty()
{
    // This function returns true if the buffer is empty.
    return (read_index == write_index);
}

bool round_buf::is_full()
{
    // This function returns true if the buffer is full.
    return (avail_size() == 0);
}

int round_buf::avail_size()
{
    // This function returns the size of space available for writing in the
    // buffer.
    int gap = read_index - write_index;

    if (gap <= 0) {
	gap += alloc_size;
    }

    return (gap - 1);
}

int round_buf::used_size()
{
    // This function returns the size of the space used by the data in the
    // buffer.
    return (alloc_size - avail_size() - 1);
}

void round_buf::seek(int pos)
{
    // This function advances the read pointer of 'size' bytes. It is the
    // responsability of the user to make sure that enough data is 
    // available for reading the data buffer.
    assert(used_size() >= abs(pos));

    read_index = (read_index + pos) % alloc_size;
}

void round_buf::internal_read_buf(char *data_buf, int data_buf_size,
				  int *local_index_ptr)
{
    // Since the code for read_buf and peek_buf is mostly the same, we use
    // this function to do the job and use the previous functions
    // as front-end. It is the responsability of the user to make sure that
    // enough data is available for reading the data buffer.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    // Pointer to the read index after the read is completed.
    assert(used_size() >= abs(data_buf_size));

    int remaining_size = data_buf_size;
    int local_index = read_index;

    // Calculate how many bytes may be read before we reach the right end
    // of the buffer.
    int read_size = alloc_size - local_index;

    if (read_size > data_buf_size) {
	read_size = data_buf_size;
    }

    if (read_size) {
	memcpy(data_buf, buf + local_index, read_size);
	remaining_size -= read_size;
	local_index += read_size;
    }
    // Roll over.
    if (local_index == alloc_size) {
	local_index = 0;
    }
    // Finished.
    if (remaining_size == 0) {
	*local_index_ptr = local_index;
	return;
    }
    // Copy the rest of the buffer.
    memcpy(data_buf + read_size, buf + local_index, remaining_size);

    // Assing the new read index.
    local_index += remaining_size;
    *local_index_ptr = local_index;
}


void round_buf::read_buf(char *data_buf, int data_buf_size)
{
    // Read data_buf from the round buffer. data_buf is assumed to be
    // data_buf_size long. It is the responsability of the user to make 
    // sure that enough data is available for reading the data buffer.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    internal_read_buf(data_buf, data_buf_size, &read_index);
}

void round_buf::peek_buf(char *data_buf, int data_buf_size)
{
    // Read data_buf from the round buffer. data_buf is assumed to be
    // data_buf_size long. It is the responsability of the user to make 
    // sure that enough data is available for reading the data buffer. The
    // data read won't be removed from the buffer and will be still
    // available for the next call.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    int dummy;

    internal_read_buf(data_buf, data_buf_size, &dummy);
}

void round_buf::write_buf(char *data_buf, int data_buf_size)
{
    // Write data_buf inside the round buffer. data_buf is assumed to be
    // data_buf_size long. It is the responsability of the user to 
    // make sure that enough space is available for writing the data
    // buffer.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    assert(avail_size() >= abs(data_buf_size));

    int remaining_size = data_buf_size;

    // Calculate how many bytes may be written before we reach the right
    // end of the buffer.
    int write_size = alloc_size - write_index;

    if (write_size > data_buf_size) {
	write_size = data_buf_size;
    }

    if (write_size) {
	memcpy(buf + write_index, data_buf, write_size);
	remaining_size -= write_size;
	write_index += write_size;
    }
    // Roll over.
    if (write_index == alloc_size) {
	write_index = 0;
    }
    // Finished.
    if (remaining_size == 0) {
	return;
    }
    // Copy the rest of the buffer.
    memcpy(buf + write_index, data_buf + write_size, remaining_size);

    write_index += remaining_size;
}
