/*
 * simple ring buffer
 *
 * Copyright (C) 2005-2008 Joern Seger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <string.h>
#include "ringbuffer.h"
#include "exception.h"
#include "log.h"

ringbuffer::ringbuffer(unsigned int buffersize)
    : size(buffersize), used(0), begin(0), end(0)
{
  lock();
  fifo = new unsigned char[buffersize];
  unlock();
}

ringbuffer::ringbuffer(unsigned char* data, unsigned int len)
    : size(len), used(len), begin(0), end(0)
{
  // gonna make a copy for safety:
  lock();
  fifo = new unsigned char[len];
  memcpy(fifo, data, len);
  unlock();
}

ringbuffer::~ringbuffer()
{
  delete[] fifo;
}

unsigned int ringbuffer::addData(const unsigned char* data, unsigned int len)
{
  lock();

  if ((!len) || (!data)) {
    unlock();
    return(0);
  }

  if (len > size) {
    unlock();
    throw OggException("Ring buffer write overflow");
  }
  if (begin+len < size) {
    memcpy(fifo+begin,data,len);
  } else {
    // split
    int part1 = (size - begin);
    int part2 = len - part1;
    memcpy(fifo+begin,data,part1);
    memcpy(fifo,data+part1,part2);
  }

  begin += len;
  begin %= size;

  if (len > (size-used)) {
    throw OggException("Ring buffer overrun");
  } else {
    used += len;
  }

  unlock();

  return (len);

}

unsigned int ringbuffer::getData(unsigned char* data, unsigned int len)
{
  lock();

  if (used < len)
    len = used;

  if (len > size) {
    throw OggException("Ring buffer read overflow");
  }
  if (size < (end + len)) {
    // split
    int part1 = size - end;
    int part2 = len - part1;
    memcpy(data, fifo+end, part1);
    memcpy(data+part1, fifo, part2);
  } else
    memcpy(data, fifo+end, len);

  end += len;
  end %= size;
  used -= len;

  /*
  for(unsigned int i=0; i<len; ++i) {
    data[i] = fifo[end++];
    end %= size;
  }
  */

  unlock();

  return (len);
}

unsigned int ringbuffer::getAvailable()
{
  unsigned int retValue;

  lock();
  retValue = size-used;
  unlock();

  return(retValue);
}

unsigned int ringbuffer::getUsed()
{
  unsigned int retValue;

  lock();
  retValue = used;
  unlock();

  return(retValue);
}

void ringbuffer::clean()
{
  lock();
  begin = end = used = 0;
  unlock();
}

unsigned int ringbuffer::peekBack(unsigned char* data, unsigned int len)
{
  lock();
  if (len>used)
    len = used;

  int tmpEnd = begin-1;
  for (int i=len-1; i>=0; --i) {
    if (tmpEnd < 0)
      tmpEnd = size-1;
    data[i] = fifo[tmpEnd--];
  }
  unlock();

  return (len);
}

unsigned int ringbuffer::peekFront(unsigned char* data, unsigned int len)
{
  lock();
  if (used < len)
    len = used;

  unsigned int tmpEnd = end;
  for (unsigned int i=0; i<len; ++i) {
    data[i] = fifo[tmpEnd++];
    tmpEnd %= size;
  }
  unlock();

  return (len);
}

unsigned int ringbuffer::inc(unsigned int len)
{
  lock();
  if (used < len)
    len = used;

  end += len;
  end %= size;
  used -= len;

  unlock();

  return (len);
}
