/* LICENSE:
  =========================================================================
    CMPack'04 Source Code Release for OPEN-R SDK 1.1.5-r2 for ERS7
    Copyright (C) 2004 Multirobot Lab [Project Head: Manuela Veloso]
    School of Computer Science, Carnegie Mellon University
    All rights reserved.
  ========================================================================= */

#ifndef INCLUDED_CircBufPacket_h
#define INCLUDED_CircBufPacket_h

#include "DogTypes.h"

typedef int PacketOffset;

namespace PacketUtil {
  void *GetPtr(uchar *base, PacketOffset offset);
  
  template<class T>
  PacketOffset GetOffset(uchar *base, T *ptr);
}

/*------------------------------------------------------------------
CLASS
  BareRobotDataPacket

DESCRIPTION
  A brief packet of data about the state of the robot used in packet
stream storage.
------------------------------------------------------------------*/
struct BareRobotDataPacket {
public:
  // length of data or space available
  // lengths of up to 14 bits are currently supported
  ulong length;
  ulong timestamp;
  union {
    uchar *data;
    PacketOffset offset;
  };
};


/*------------------------------------------------------------------
CLASS
  RobotDataPacket

DESCRIPTION
  A packet of data about the state of the robot.
------------------------------------------------------------------*/
struct RobotDataPacket : public BareRobotDataPacket {
public:
  enum ContentType {EMPTY, TEXT, BINARY};

  ContentType contentType;
  uchar dataType;
};


/*------------------------------------------------------------------
CLASS
  PacketStream

DESCRIPTION
  A stream of data packets containing the same type of data.
------------------------------------------------------------------*/
class PacketStream {
public:
  int type;
  bool binary;
  // the last packet number which has valid data
  int lastPacket; // read-only

  // the first packet number which has valid data
  // if firstValidPacket > lastPacket there is no valid data
  int firstValidPacket; // read-only

  void init(uchar *data_start, uchar *data_end, int max_packets);

  bool writeBinary(const uchar *data, int length, ulong timestamp);
  bool writeBinary(const uchar *data, int length);

  bool writeText(const char *msg, ulong timestamp);
  bool writeText(const char *msg);
  friend int pprintf(PacketStream *ps,const char *fmt, ...);

  bool readHeader(int packet_id, RobotDataPacket *packet);
  bool read(int packet_id, RobotDataPacket *packet, int data_length);

  // makes a packet id valid by moving the packet id forward, if possible
  // return true if the resulting packet id is valid
  bool makePacketIdValidCatchup(int *packet_id);
  bool makePacketIdValidFirst  (int *packet_id);

private:
  PacketOffset dataStartOffset;
  PacketOffset dataEndOffset; // one past end of buffer allocated

  static const int MaxPackets=256; // would like to make this variable
  int maxPackets;
  BareRobotDataPacket packets[MaxPackets];

  int packetNumToIdx(int packet_num) {
    return packet_num % maxPackets;
  }

  PacketOffset getOffset(uchar *ptr) {
    return PacketUtil::GetOffset((uchar *)this,ptr);
  }

  uchar *getPtr(PacketOffset offset) {
    return (uchar *)PacketUtil::GetPtr((uchar *)this,offset);
  }
};

/*------------------------------------------------------------------
CLASS
  PacketStreamCollection

DESCRIPTION
  The collection of packet streams coming from an Aperios object.
------------------------------------------------------------------*/
class PacketStreamCollection {
public:
  static const int MAX_STREAMS=28;

  void init(uchar *data_start,uchar *data_end);

  int allocateStream(ulong size, int max_packets);

  PacketStream *getStream(int id);

  int getNumStreams();

private:
  PacketOffset dataStartOffset,dataEndOffset;
  PacketOffset freeStartOffset;

  int numStreams;
  PacketStream streams[MAX_STREAMS];

  PacketOffset getOffset(uchar *ptr) {
    return PacketUtil::GetOffset((uchar *)this,ptr);
  }

  uchar *getPtr(PacketOffset offset) {
    return (uchar *)PacketUtil::GetPtr((uchar *)this,offset);
  }
};

#ifdef PLATFORM_LINUX
// For documentation on GCC's attribute support, see:
//   http://www.cis.ohio-state.edu/cgi-bin/info/info/gcc,Attribute%20Syntax
#include <stdlib.h>
static PacketStream *TextOutputStream __attribute__((unused)) = NULL;
#else
extern PacketStream *TextOutputStream __attribute__((unused));
#endif

#endif
