/*
 * OggPacket will carry all relevant information of an ogg packet
 *
 * Copyright (C) 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 <iostream>
#include <sstream>
#include <cstring>

//#include "theora/theora.h"
#include "oggPacket.h"
#include "log.h"

OggPacketInternal::OggPacketInternal()
    : streamType(ogg_unknown), streamNo(255), streamHeader(false)
{
  packet = 0;
  bytes = 0;
  packetno = 0;
  granulepos = -1;
  b_o_s = 0;
  e_o_s = 0;
}

OggPacketInternal* OggPacketInternal::clone()
{
  uint8* data = new uint8[bytes];
  memcpy(data, packet, bytes);

  PacketType packetType(normal);

  if (b_o_s)
    packetType = bos;

  if (e_o_s)
    packetType = eos;

  OggPacketInternal* pkt = new OggPacketInternal(data, bytes, packetno, granulepos, packetType);

  pkt->streamNo = streamNo;
  pkt->streamType = streamType;
  pkt->streamHeader = streamHeader;

  return(pkt);
}

OggPacketInternal::OggPacketInternal(uint8* data, uint32 length,
                                     uint32 packetNo, int64 granulePos, PacketType packetType)
    : streamType(ogg_unknown), streamNo(255), streamHeader(false)
{
  packet     = data;
  bytes      = length;
  packetno   = packetNo;
  granulepos = granulePos;
  b_o_s      =  0;
  e_o_s      =  0;

  switch (packetType) {
  case bos:
    b_o_s = 256;
    break;
  case eos:
    e_o_s = 256;
    break;
  default: {}
  }

}

OggPacketInternal::~OggPacketInternal()
{
  if (packet)
    delete[] packet;
}

OggPacket::OggPacket()
{
}

OggPacket::OggPacket(OggPacketInternal* pagePtr)
    : RefObject<OggPacketInternal>(pagePtr)
{
}

OggPacket::OggPacket(const OggPacket& refObj)
    : RefObject<OggPacketInternal>(refObj)
{
}

OggPacket::~OggPacket()
{
}

OggPacket& OggPacket::operator=(const OggPacket& refObj)
{
  if (this == &refObj)
    return(*this);

  (*refCounter)--;

  if ((*refCounter) == 0) {
    delete refCounter;
    delete objPtr;
  }

  refCounter = refObj.refCounter;
  objPtr     = refObj.objPtr;

  (*refCounter)++;

  return(*this);
}

int64 OggPacket::granulepos()
{
  if (objPtr)
    return(objPtr->granulepos);

  return(-2);
}

void OggPacket::setGranulepos(int64 pos)
{
  if (objPtr)
    objPtr->granulepos = pos;
}

void OggPacket::setStreamHeader()
{
  objPtr->streamHeader = true;
}

bool OggPacket::isStreamHeader()
{
  return(objPtr->streamHeader);
}

OggPacket OggPacket::clone()
{
  OggPacketInternal* newPacket = new OggPacketInternal(*objPtr);

  if (newPacket) {
    newPacket->packet = new unsigned char[objPtr->bytes];
    memcpy(newPacket->packet, objPtr->packet, objPtr->bytes);
  }

  return(OggPacket(newPacket));
}

uint32 OggPacket::getPacketNo()
{
  return(objPtr->packetno);
}

uint32 OggPacket::length()
{
  return(objPtr->bytes);
}

bool OggPacket::isBOS()
{
  return(objPtr->b_o_s);
}

bool OggPacket::isEOS()
{
  return(objPtr->e_o_s);
}

void OggPacket::setBOS()
{
  objPtr->b_o_s = 1;
}

void OggPacket::setEOS()
{
  objPtr->e_o_s = 1;
}

void OggPacket::unsetBOS()
{
  objPtr->b_o_s = 0;
}

void OggPacket::unsetEOS()
{
  objPtr->e_o_s = 0;
}

/*
ogg_packet OggPacket::toLibogg()
{
  return(*objPtr);
}
*/
uint8 OggPacket::getStreamNo()
{
  return(objPtr->streamNo);
}

OggType OggPacket::getStreamType()
{
  return(objPtr->streamType);
}

void OggPacket::setStreamNo(int8 no)
{
  objPtr->streamNo = no;
}

void OggPacket::setStreamType(OggType type)
{
  objPtr->streamType = type;
}

/*
void OggPacket::fromLibogg(ogg_packet pack)
{
  // copy all information including the pointers
  objPtr->bytes      = pack.bytes;
  objPtr->packetno   = pack.packetno;
  objPtr->granulepos = pack.granulepos;
  objPtr->b_o_s      = pack.b_o_s;
  objPtr->e_o_s      = pack.e_o_s;

  objPtr->packet = new uint8[pack.bytes];

  if (objPtr->packet) {
    memcpy(objPtr->packet, pack.packet, pack.bytes);
  }
  else {
    logger.error() << "OggPacket::fromLibogg: out of memory\n";
  }
}
*/

uint8* OggPacket::data()
{
  return(objPtr->packet);
}

/* print levels:
 * 1) only data length information
 * 2) header information
 * 3) additional header information
 * 4) header dump
 * 5) body dump
 */
std::string OggPacket::print(uint8 level)
{
  std::stringstream retStream;

  retStream << "\nOgg Packet: packet length = " << objPtr->bytes << std::endl;

  if (level < 1)
    return(retStream.str());

  retStream << "\nHeader Information:"
  << "\n\tBegin of Stream     : ";

  if (objPtr->b_o_s)
    retStream << "true";
  else
    retStream << "false";

  retStream << "\n\tEnd of Stream       : ";

  if (objPtr->e_o_s)
    retStream << "true";
  else
    retStream << "false";

  retStream << "\n\tGranule Position    : " << objPtr->granulepos;
  retStream << "\n\tPacket Number       : " << objPtr->packetno;

  retStream << std::endl;

  if (level < 3)
    return(retStream.str());

  retStream << "\n\tStream Number       : " << (int)objPtr->streamNo;
  retStream << "\n\tStream Type         : ";

  switch (objPtr->streamType) {
  case ogg_vorbis:
    retStream << "Vorbis";
    break;
  case ogg_theora:
    retStream << "Theora";
    break;
  case ogg_kate:
    retStream << "Kate";
    break;
  case ogg_unknown:
  default:
    retStream << "unknown";
    break;
  }
  retStream << std::endl;

  if (level < 4)
    return(retStream.str());

  retStream << "\nPacket Hex dump:";

  for (int32 c(0); c<objPtr->bytes; ++c) {
    if ((c%16) == 0)
      retStream << std::endl;
    retStream << " " << std::hex;
    if (((unsigned int) (objPtr->packet[c])) < 16)
      retStream << "0";
    retStream << (unsigned int) (objPtr->packet[c]);
  }

  retStream << std::dec << std::endl;

  return(retStream.str());
}
