/*****************************
File:      Packet.h
Language:   C++ (header)
Project:    H3DNetworkingUtils
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is H3DNetworkingUtils v1.0.

The Initial Developer of the Original Code is CSIRO.
Portions created by the Initial Developer are Copyright (C) 1009 CSIRO. All Rights Reserved.
Contributor(s):
    Chris Gunn  <Chris.Gunn@csiro.au> <ChrisJGunn@gmail.com>
***************************/

/// \file Packet.h
/// \brief Header file for Packet, contains packet data for network communications.


#ifndef Packet_H
#define Packet_H

#include "H3DNetworkingUtils/Config.h"
#include "H3DNetworkingUtils/sockwrap.h"
#include "H3DNetworkingUtils/lockwrap.h"
#include "H3DNetworkingUtils/PacketSequenceChecker.h"

namespace H3DNetworkingUtils {

/// \class Packet
/// The Packet class contains a packet for sending or receiving across a network.
/// with TCP or UDP. \n
/// "Read"  implies getting data out of the packet. \n
/// "Write" implies putting data into the packet. \n
/// "receive" implies getting data from the socket into the packet. \n
/// "send" implies putting data from the packet to the socket. \n
/// A packet consists of a header and payload. \n
/// The header holds: \n
///   size   (only used on TCP sockets)            internal code sets this \n
///   sequence number (only used on UDP sockets)   use setSeqNum() getSeqNum() \n
///   packet id                                    use setId() getId() \n

/// The payload data holds the field value. It is filled with the write
/// functions and read with the read functions. Each time you call a write
/// function, a marker is progressed, marking the current end of the buffer.
/// Each time you call a read function a
/// different marker is progressed in the buffer.
/// A packet can be set to be a heartBeat packet, which has a special id which
/// is sent periodically in the casew of UDP sockets to tell the other end that it
/// is still alive.

   class H3D_NETWORKING_UTILS_DLL_SPEC Packet {
public:
   /// Constructor
   Packet(int sz = 128);

   /// Copy Constructor
   Packet(Packet const & other);

   /// Copy operator
   Packet & operator=(Packet const & other);

   virtual ~Packet() {delete [] buffer_head; }

   /// Write data into packet.
   void write(void * data, int num) {
      stretchBuffer(num);
      memcpy(&buffer_payload[payload_size], data, num);
      payload_size += num;
   }

   /// Sends packet to a remote host via TCP \n
   /// Returns true on success.
   bool send(TCPSock * sockP);

   /// Sends packet to a remote host via UDP \n
   /// Returns true on success.
   bool send(UDPSock * sockP, InetAddr * remote_end);

   /// Call this after finishing with data inside - (send already does this).
   void clear() {payload_size = 0;}

   /// Receive data from socket into packet \n
   /// Returns false on failure, true on success.
   bool receive(TCPSock * sockP);

   /// Reads packet from any remote host via UDP \n
   /// Fills 'remote_end' \n
   /// Returns -1 on failure, 0 on timeout, num received on success.
   int receive(UDPSock * sockP, InetAddr * remote_end);

   /// Read data from packet.
   void read(void * dest, int num_bytes) {
      memcpy(dest, &buffer_payload[get_pos], num_bytes);
      get_pos += num_bytes;
   }

   /// get a 16 bit integer from the packet buffer.
   int readInt16() const;

   /// Put a 16 bit integer into the packet buffer.
   void writeInt16(int const & val);

   /// Returns true if this packet is just a 'heart beat' packet, letting server
   /// know client is still alive.
   bool isHeartBeat() const {return (getId() == HEART_BEAT_ID); }

   /// Sets this packet to be a 'heart beat' packet, letting server
   /// know client is still alive.
   void setHeartBeat();

   /// returns the Id but doesn't change the read position for the next read.
   int getId() const;

   /// returns the sequence number but doesn't change the read position for the next read.
   short getSeqNum() const;

   /// sets the packet's Id.
   void setId(int id);

   /// sets the packet's sequence number.
   void setSeqNum(short seq);

   /// Increase the buffer by the extra size (plus a bit!)
   void stretchBuffer(u_int extra_size);

private:
   // Buffer head and buffer payload are contiguous.
   // The head has 6 bytes of size data, seq number and id.
   // For udp the size data is not used, for TCP the seq num is not used.
   char * buffer_head;
   char * buffer_payload;
   mutable int get_pos;
   bool keep_data; // If true, it keeps its buffer of data, doesn't 'erase' it
   // each time a send occurs.
   static const int HEART_BEAT_ID;
   static const char CR;
   static const char LF;
   int payload_size;
   int max_payload_size;
   static const int HEADER_SIZE;
   static const int PACKET_SIZE_OFFSET;
   static const int PACKET_SIZE_SIZE; // The size of the packet size.
   static const int SEQ_NUM_SIZE;
   static const int ID_SIZE;
   static const int SEQ_NUM_OFFSET;
   static const int ID_OFFSET;

   union {
      int v;
      char sz[4];
   } enc_int;
   union {
      int v;
      char sz[4];
   } dec_int;

   u_int buffer_size;
};

}

#endif
