#ifndef _CDEV_PACKET_H_
#define _CDEV_PACKET_H_

#include <cdevPlatforms.h>
#include <cdevData.h>
#include <xdrClass.h>

// *****************************************************************************
// * struct cdevPacketBinaryMap :
// *	This is the first 32 bit integer that is passed through the socket.  It
// *	contains a bitmap that identifies the version of the packet, as well as
// *	a bit field that indicates the contents of the packet.  This bitfield
// *	simplifies the process of decoding the packet and results in a smaller
// *	transmission size.
// *****************************************************************************
typedef union
	{
	unsigned rawData;
	struct	{
		unsigned version : 16;
		unsigned pad     : 16;
		} value;
	} cdevPacketBinaryMap;
	
// *****************************************************************************
// * class cdevPacketBinary :
// *    This is the base class for all cdevPacketBinary classes.  It is a pure
// *    virtual class that defines the mechanisms necessary for identifying
// *    the version of a class as well as reading or generating a binary
// *    stream.
// * 
// *    Enforced structural contraints...
// *
// *    1) Every packet that inherits from the cdevPacketBinary must have an
// *	   XDR encoded 4 byte integer at the beginning... 
// *
// *	   a) The first two bytes of that 4 byte integer is the packet version.
// *
// *       b) The second two bytes of that 4 byte integer is user defined data.
// *
// *	2) The next four bytes of the packet contains a XDR encoded short 
// *       integer that contains a 16 bit client identifier.
// *****************************************************************************
class GENERIC_SERVER_API cdevPacketBinary
{
protected:
	// *********************************************************************
	// * These are the data storage variables.
	// *********************************************************************
	char    * binary;
	size_t    binaryLen;

public:
	cdevPacketBinary           ( void );
	cdevPacketBinary           ( cdevPacketBinary & packet );
	virtual ~cdevPacketBinary  ( void );

	unsigned            map2int ( cdevPacketBinaryMap map, unsigned & value);
	cdevPacketBinaryMap int2map ( cdevPacketBinaryMap &map, unsigned value);
	
	virtual int  getVersion    ( short & version );	
	virtual int  getPad        ( short & pad );
	virtual int  getClientID   ( short & clientID );
	virtual int  setClientID   ( short   clientID );
	virtual int  streamOut     ( char ** stream, size_t * len );
	virtual int  streamIn      ( char *  stream, size_t   len );
	virtual void detachData    ( void );
	virtual int  attachData    ( char * stream, size_t len );
};
	
// *****************************************************************************
// * cdevPacketBinary::map2int ( void )
// *	This method will convert the cdevPacketBinaryMap to a long integer.
// *****************************************************************************
inline unsigned cdevPacketBinary::map2int ( cdevPacketBinaryMap map, unsigned & value)
	{
	value  = (map.value.version << 16);
	value |= (map.value.pad & 0xFFFF);

	return value;
	}

// *****************************************************************************
// * cdevPacketBinary::int2map ( void )
// *	This method will convert a long  integer to a cdevPacketBinaryMap.
// *****************************************************************************
inline cdevPacketBinaryMap cdevPacketBinary::int2map ( cdevPacketBinaryMap &map, unsigned value)
	{
	map.rawData                   = 0;
	map.value.version             = (value >> 16);
	map.value.pad                 = (value & 0xFFFF);
	return map;	
	}


// *****************************************************************************
// * cdevPacketBinary::getVersion :
// *	This method is used to obtain the version number of the packet.
// *
// *	Returns 0 on Success or -1 on Error
// *****************************************************************************
inline int cdevPacketBinary::getVersion ( short & version )
	{
	int                 result=-1;
	XDR_Reader          reader;
	cdevPacketBinaryMap map;

	version = -1;

	if(binary!=NULL && binaryLen>0)
		{
		unsigned packetMap = 0;
		reader.attachData(binary, binaryLen);
		map.rawData = 0;
		if((result  = !reader.get(packetMap))==0)
			{
			int2map(map, packetMap);
			version = map.value.version;
			}
		reader.detachData();
		}
	return result;
	}

// *****************************************************************************
// * cdevPacketBinary::getPad :
// * 	This method is used to obtain the 16 bit integer that follows the
// *	packet version.
// *****************************************************************************
inline int cdevPacketBinary::getPad ( short & pad )
	{
	int                 result=-1;
	XDR_Reader          reader;
	cdevPacketBinaryMap map;

	pad = -1;

	if(binary!=NULL && binaryLen>0)
		{
		unsigned packetMap = 0;
		reader.attachData(binary, binaryLen);
		map.rawData = 0;
		if((result  = !reader.get(packetMap))==0)
			{
			int2map(map, packetMap);
			pad = map.value.pad;
			}
		reader.detachData();
		}
	return result;
	}
		

// *****************************************************************************
// * cdevPacketBinary::getClientID :
// * 	This method is used to obtain the 16 bit integer that contains the 
// *	client identifier.
// *****************************************************************************
inline int cdevPacketBinary::getClientID ( short & clientID )
	{
	int        result = -1;
	XDR_Reader reader;		
	
	clientID = -1;
	
	if(binary!=NULL && binaryLen>0)
		{
		reader.attachData(binary, binaryLen);
		if((result = !xdr_setpos(reader.xdr(), XDR_Sizeof((unsigned)1)))==0)
			{
			result = !reader.get(clientID);
			}
		reader.detachData();
		}
	return result;
	}


// *****************************************************************************
// * cdevPacketBinary::getClientID :
// * 	This method is used to insert the 16 bit integer that contains the 
// *	client identifier.
// *****************************************************************************
inline int cdevPacketBinary::setClientID ( short clientID )
	{
	int        result = -1;
	XDR_Writer writer;		
	
	if(binary!=NULL && binaryLen>0)
		{
		writer.attachData(binary, binaryLen);
		if((result = !xdr_setpos(writer.xdr(), XDR_Sizeof((unsigned)1)))==0)
			{
			result = !writer.put(clientID);
			}
		writer.detachData();
		}
	return result;
	}


// *****************************************************************************
// * cdevPacketBinary::streamIn :
// *	The streamIn function will read a memory buffer provided by the caller 
// *	and then populate the class with these values. This stream remains the 
// *	property of the caller and he is responsible for deleting it.
// *
// *	Returns 0 on Success
// *****************************************************************************
inline int cdevPacketBinary::streamIn ( char * stream, size_t len )
	{
	if(binary!=NULL)
		{
		delete binary;
		binary = NULL;
		}
	binaryLen = 0;
	
	if(len>0 && stream!=NULL)
		{
		binary    = new char[len];
		binaryLen = len;
		memcpy(binary, stream, binaryLen);
		}
	return 0;
	}

// *****************************************************************************
// * cdevPacketBinary::streamOut :
// *	The streamOut and streamIn functions are the bread and butter of the 
// *	cdevPacketBinary classes.  They allow the class to convert itself into a 
// *	binary stream for transmission and then be reconstructed into a class
// *	when they are received on the other side.
// *
// *	The streamOut function will allocate a memory buffer to the stream
// *	variable that is sufficient to store the binary representation of
// *	the data that is to be transmitted.  This stream remains the 
// *	property of the cdevPacketBinary class and should not be deleted by the
// *	caller.
// *
// *	Returns 0 on Success
// *****************************************************************************
inline int cdevPacketBinary::streamOut ( char ** stream, size_t * len )
	{
	*stream = binary;
	*len    = binaryLen;
	return 0;
	}

// *********************************************************************
// * cdevPacketBinary::detachData :
// *	This method allows the caller to obtain a copy of the binary
// *	image that is stored in the cdevPacketBinary object (using 
// *	streamOut), and then force the cdevPacket object to detach it 
// *	(preventing it from being deleted when the object is destroyed.)
// *
// *	Returns nothing.
// *********************************************************************
inline void cdevPacketBinary::detachData ( void )
	{
	binary = NULL;
	binaryLen = 0;
	}
	
// *********************************************************************
// * cdevPacketBinary::attachData :
// *	This method allows the caller to assign a preallocated binary
// *	buffer to this object.  This prevents the cdevPacketBinary class
// *	from having to allocate the data.
// *
// *	Returns 0 on Success
// *********************************************************************
inline int cdevPacketBinary::attachData ( char * stream, size_t len )
	{
	if(binary!=NULL) 
		{
		delete binary;
		binary = NULL;
		}
	binaryLen = 0;

	if(stream!=NULL && len>0)
		{
		binary    = stream;
		binaryLen = len;
		}
	return 0;
	}

// *****************************************************************************
// * cdevPacketBinary::cdevPacketBinary :
// * This is the default constructor for the class.
// *****************************************************************************
inline cdevPacketBinary::cdevPacketBinary ( void ) 
	: binary(NULL), binaryLen(0) 
	{}

// *****************************************************************************
// * cdevPacketBinary::cdevPacketBinary :
// *	This is the copy constructor for the class.
// *****************************************************************************
inline cdevPacketBinary::cdevPacketBinary ( cdevPacketBinary & packet )
	: binary(NULL), binaryLen(0)
	{
	streamIn(packet.binary, packet.binaryLen);
	}

// *****************************************************************************
// * cdevPacketBinary::~cdevPacketBinary :
// *	This is the destructor for the object.
// *****************************************************************************
inline cdevPacketBinary::~cdevPacketBinary ( void )
	{
	if(binary!=NULL)
		{
		delete binary;
		binary = NULL;
		}
	binaryLen = 0;
	}

#endif /* _CDEV_PACKET_H_ */
