/*
 * Ringbuffer to prebuffer an ogg file
 *
 * 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.
 *
 */

/* History:
    01 2008: initial version is taken from the streamnik server project (JS)
*/

#include <iostream>
#include <string.h>

#include <stdlib.h>

#include "oggRingbuffer.h"
#include "oggHeader.h"
#include "exception.h"
#include "log.h"

OggRingbuffer::OggRingbuffer(unsigned int buffersize)
    :ringbuffer(buffersize)
{
}

OggRingbuffer::OggRingbuffer(unsigned char* data, unsigned int len)
    :ringbuffer(data, len)
{
}


OggRingbuffer::~OggRingbuffer()
{
}

bool OggRingbuffer::getNextPageLength(unsigned int& length, int pageNum)
{
  lock();

  int tmpend  = end;
  int tmpend2 = end;
  int tmpused = used;
  length = 0;

  for (; pageNum; pageNum--) {
    tmpend = tmpend2;
    if (tmpused < (int) sizeof(OggHeader)) {
      unlock();
      return(false);
    }

    // test is this aligned?
    char starter[5];
    for (unsigned int i=0; i<5; ++i) {
      starter[i] = fifo[tmpend];
      tmpend+=1;
      tmpend%=size;
    }

    if (strncmp(starter, "OggS", 4) != 0) {
      unlock();
      throw OggException("OggRingbuffer::getNextPageLength: ERROR ogg packet not aligned");
    }

    tmpend += sizeof(OggHeader)-6; // jump to the segment table
    tmpend %= size;

    unsigned int readsegments = fifo[tmpend];

    tmpend += 1;
    tmpend %= size;

    length += sizeof(OggHeader) + readsegments;

    if (tmpused < (int)(sizeof(OggHeader)+readsegments)) {
      unlock();
      return(false);
    }

    for (unsigned int i=0; i<readsegments; ++i) {
      length += fifo[tmpend];
      tmpend += 1;
      tmpend %= size;
    }

    if (tmpused < (int)length) {
      unlock();
      return(false);
    }
    tmpused -= length;
    tmpend2 = end + length;
    tmpend2 %= size;

  }
  unlock();
  return(true);

}

bool OggRingbuffer::getNextPages(unsigned char*& data, unsigned int& length, unsigned int size)
{
  if (!used)
    return(false);

  if (!getNextPageLength(length,size))
    return(false);

  if (!data)
    data = new unsigned char[length];

  if (length != getData(data, length))
    return(false);


  return(true);
}

bool OggRingbuffer::getNextPage(unsigned char*& data, unsigned int& length)
{
  return(getNextPages(data, length, 1));
}

void OggRingbuffer::dump()
{

  for (uint32 c(0); c<used; ++c) {
    if ((c%16) == 0)
      std::cerr << std::endl;
    std::cerr << " " << std::hex;
    if (fifo[(c+begin)%size] < 16)
      std::cerr << "0";
    std::cerr << (unsigned int)fifo[(c+begin)%size];
  }

  std::cerr << std::dec << std::endl;
}

