#include <cdevMessage.h>

// *****************************************************************************
// * cdevMessage::import :
// *	This metod is used by the cdevPacket class to generate a cdevMessage 
// *	object from a cdevBinaryPacket object.  In order for the cdevPacket
// *	class to perform this task, the import method and the 
// *	CDEV_PACKET_VERSION must be registered using the 
// * 	cdevPacket::registerImportMethod function.
// *****************************************************************************
cdevPacket * cdevMessage::import( cdevPacketBinary & packet)
	{
	char * binary    = NULL;
	size_t binaryLen = 0;

	packet.streamOut(&binary, &binaryLen);

	return new cdevMessage(binary, binaryLen);
	}

// *****************************************************************************
// * cdevMessage::cdevMessage :
// *	This is a parameterized constructor for the cdevMessage object.  If none
// *	of the parameters are specified then this constructor will build a 
// *	cdevMessage object made completely from defaults.
// *****************************************************************************
cdevMessage::cdevMessage ( short      ClientID, 
			   unsigned   TransIndex,
	                   unsigned   CancelTransIndex,
			   unsigned   LocalDataIndex,
			   unsigned   ForeignDataIndex,
			   unsigned   OperationCode,
	                   int        CompletionCode,
	                   unsigned   DeviceCount,
			   char **    DeviceList,
			   char *     Message,
			   cdevData * Data,
			   cdevData * Context,
			   cdevData * TagMap)
	: clientID         (ClientID),
	  transIndex       (TransIndex),
	  cancelTransIndex (CancelTransIndex),
	  localDataIndex   (LocalDataIndex),
	  foreignDataIndex (ForeignDataIndex),
	  operationCode    (OperationCode),
	  completionCode   (CompletionCode),
	  deviceCount      (DeviceCount),
	  deviceList       (NULL),
	  message          (NULL),
	  data             (NULL),
	  context          (NULL),
	  tagMap           (NULL)
	{
	saveTbl.rawData=0;

	if(DeviceCount>0 && DeviceList!=NULL)
		{
		deviceList = (char **)new char * [deviceCount];
		for(int i=0; i<deviceCount; i++)
			{
			deviceList[i] = strdup(DeviceList[i]);
			}
		}
	if(Message!=NULL) message = strdup(Message);
	if(Data!=NULL)    data    = new cdevData(*Data);
	if(Context!=NULL) context = new cdevData(*Context);
	if(TagMap!=NULL)  tagMap  = new cdevData(*TagMap);
	}
	

// *****************************************************************************
// * cdevMessage::cdevMessage : 
// * 	This is the constructor for the object.  It serves to initialize the 
// *	internals of the object with default values or the user specified 
// *	binary data if provided.
// *****************************************************************************
cdevMessage::cdevMessage ( char * binary, size_t binaryLen )
	: deviceCount(0), deviceList(NULL), message(NULL),
	  data(NULL), context(NULL), tagMap(NULL)
	{
	saveTbl.rawData = 0;
	streamIn(binary, binaryLen);
	}


// *****************************************************************************
// * cdevMessage::cdevMessage :
// *	This is the copy constructor for the object.  It creates a new 
// *	cdevMessage object that contains all of the data specified in the
// *	user provided object.
// *****************************************************************************
cdevMessage::cdevMessage ( cdevMessage & Message )
	: clientID         (Message.clientID),
	  transIndex       (Message.transIndex),
	  cancelTransIndex (Message.cancelTransIndex),
	  localDataIndex   (Message.localDataIndex),
	  foreignDataIndex (Message.foreignDataIndex),
	  operationCode    (Message.operationCode),
	  completionCode   (Message.completionCode),
	  deviceCount      (Message.deviceCount),
	  deviceList       (NULL),
	  message          (NULL),
	  data             (NULL),
	  context          (NULL),
	  tagMap           (NULL)
	{
	saveTbl.rawData = 0;
	if(Message.deviceCount>0 && Message.deviceList!=NULL)
		{
		deviceList = (char **)new char * [deviceCount];
		for(int i=0; i<deviceCount; i++)
			{
			deviceList[i] = strdup(Message.deviceList[i]);
			}
		}
	if(Message.message!=NULL) message = strdup(Message.message);
	if(Message.data!=NULL)    data    = new cdevData(*Message.data);
	if(Message.context!=NULL) context = new cdevData(*Message.context);
	if(Message.tagMap!=NULL)  tagMap  = new cdevData(*Message.tagMap);
	}
	

// *****************************************************************************
// * cdevMessage::~cdevMessage :
// *	This is the destructor for the class.  It releases any previously 
// *	allocated memory prior to the destruction of the object.
// *****************************************************************************
cdevMessage::~cdevMessage ( void )
	{
	clear();
	}
	

// *****************************************************************************
// * cdevMessage::clear :
// *	This is the clear mechanism for the object.  It is used to deallocate
// *	any memory that has been allocated and set all data items to their
// *	initial default values.
// *****************************************************************************
void cdevMessage::clear ( void )
	{
	setClientID(-1);
	setTransIndex(0);
	setCancelTransIndex(0);
	setLocalDataIndex(0);
	setForeignDataIndex(0);
	setOperationCode(0);
	setCompletionCode(0);
	setDeviceList(NULL, 0, 0);
	setMessage(NULL);
	setData(NULL);
	setContext(NULL);
	setTagMap(NULL);
	}
	

// *****************************************************************************
// * cdevMessage::streamIn :
// *	This mechanism is used to use a binary packet to populate the 
// *	cdevMessage object.
// *
// *	The binary stream remains the property of the caller and must be
// *	delete by him.
// *****************************************************************************
int cdevMessage::streamIn( char * binary, size_t binaryLen )
	{
	int result = 0;
	cdevMessageBinary packet;
	
	clear();
	packet.attachData(binary, binaryLen);
	if(packet.hasClientID())         result+=packet.getClientID(clientID);
	if(packet.hasTransIndex())       result+=packet.getTransIndex(transIndex);
	if(packet.hasCancelTransIndex()) result+=packet.getCancelTransIndex(cancelTransIndex);
	if(packet.hasLocalDataIndex())   result+=packet.getLocalDataIndex(localDataIndex);
	if(packet.hasForeignDataIndex()) result+=packet.getForeignDataIndex(foreignDataIndex);
	if(packet.hasOperationCode())    result+=packet.getOperationCode(operationCode);
	if(packet.hasCompletionCode())   result+=packet.getCompletionCode(completionCode);
	if(packet.hasDeviceList())       result+=packet.getDeviceList(deviceList, deviceCount);	
	if(packet.hasMessage())          result+=packet.getMessage(message);	
	if(packet.hasData())
		{
		data = new cdevData;
		result+=packet.getData(*data);
		}
	if(packet.hasContext())
		{
		context = new cdevData;
		result+=packet.getContext(*context);
		}
	if(packet.hasTagMap())
		{
		tagMap = new cdevData;
		result+=packet.getTagMap(*tagMap);
		} 
	packet.detachData();
	return result;
	}
	

// *****************************************************************************
// * cdevMessage::streamOut :
// *	This mechanism is used to generate a binary representation of the 
// *	cdevMessage object.
// *
// *	The binary stream becomes the property of the caller and he must delete 
// *	it when it is no longer needed.
// *****************************************************************************
int cdevMessage::streamOut( char ** binary, size_t * binaryLen )
	{
	cdevMessageBinary packet;

	packet.set(clientID, transIndex, cancelTransIndex,
		   localDataIndex, foreignDataIndex, operationCode,
		   completionCode, deviceCount, deviceList,
		   message, data, context, tagMap);
	packet.streamOut (binary, binaryLen);
	packet.detachData();

	return 0;
	}
	
	
// *****************************************************************************
// * cdevMessage::asciiDump :
// *	This mechanism is included to provide the ability to generate a 
// *	diagnostic dump of the cdevMessage object.
// *****************************************************************************
void cdevMessage::asciiDump ( FILE * fp )
	{
	fprintf(fp, "----------------------------------------------------------\n");
	fprintf(fp, "             Diagnostic Dump of CDEV Message\n");
	fprintf(fp, "----------------------------------------------------------\n\n");
	fprintf(fp, "Client Identifier  : %s\n", clientID==-1?"(not specified)":ltoa(clientID));
	fprintf(fp, "Transaction Index  : %s\n", transIndex==0?"(not specified)":ultoa(transIndex));
	fprintf(fp, "Cancel Transaction : %s\n", cancelTransIndex==0?"(not specified)":ultoa(cancelTransIndex));
	fprintf(fp, "Local Data Index   : %s\n", localDataIndex==0?"(not specified)":ultoa(localDataIndex));
	fprintf(fp, "Foreign Data Index : %s\n", foreignDataIndex==0?"(not specified)":ultoa(foreignDataIndex));
	fprintf(fp, "Operation Code     : %s\n", operationCode==0?"(not specified)":ultoa(operationCode));
	fprintf(fp, "Completion Code    : %i\n", completionCode);

	fprintf(fp, "Save Table         :\n");
	fprintf(fp, "                     Device List %s Permanent\n", saveTbl.value.saveDeviceList?"IS":"IS NOT");
	fprintf(fp, "                     Message %s Permanent\n", saveTbl.value.saveMessage?"IS":"IS NOT");
	fprintf(fp, "                     Data %s Permanent\n", saveTbl.value.saveData?"IS":"IS NOT");
	fprintf(fp, "                     Context %s Permanent\n", saveTbl.value.saveContext?"IS":"IS NOT");
	fprintf(fp, "                     Tag Map %s Permanent\n", saveTbl.value.saveTagMap?"IS":"IS NOT");		

	fprintf(fp, "Number of Devices  : %s\n", deviceCount==0?"(not specified)":ultoa(deviceCount));
	fprintf(fp, "List of Devices    : ");
	
	if(deviceCount==0) fprintf(fp, "(not specified)\n");
	else fprintf(fp, "%s\n", deviceList[0]);
	
	for(int i=1; i<deviceCount; i++) 
		{
		fprintf(fp, "                     %s\n", deviceList[i]);
		}

	fprintf(fp, "Message String     : %s\n", message==NULL?"(not specified)":message);
	
	fprintf(fp, "Data Object        : %s", data!=NULL?"\n":"(not specified)\n");
	fflush(fp);
	if(data) data->asciiDump(fp);

	fprintf(fp, "Context Object     : %s", context!=NULL?"\n":"(not specified)\n");
	fflush(fp);
	if(context) context->asciiDump(fp);

	fprintf(fp, "Tag Map            : %s", tagMap!=NULL?"\n":"(not specified)\n");
	if(tagMap)
		{
		size_t  tagNumCnt  = 0;
		int  *  tagNums    = NULL;
		size_t  tagNameCnt = 0;
		char ** tagNames   = NULL;

		if(tagMap->getElems(1, &tagNumCnt)==CDEV_SUCCESS  && 
		   tagMap->getElems(2, &tagNameCnt)==CDEV_SUCCESS &&
		   tagMap->getType (2)==CDEV_STRING               &&
		   tagNumCnt==tagNameCnt)
			{
			char ** tagNames   = NULL;
			char *  singleName = NULL;

			tagNums = new int[tagNumCnt];
			tagMap->get(1, tagNums);
			
			if(tagNameCnt==1)
				{
				tagNames = &singleName;
				tagMap->find(2, (void *&)singleName);
				}
			else tagMap->find(2, (void *&)tagNames);

			for(int i=0; i<tagNumCnt; i++)
				{
				fprintf(fp, "\t%-26.26s= %i\n", tagNames[i], tagNums[i]);
				} 
			delete tagNums;
			}
		}

	fprintf(fp, "----------------------------------------------------------\n");
	fprintf(fp, "            End of Diagnostic Dump of CDEV Message\n");
	fprintf(fp, "----------------------------------------------------------\n\n");
	fflush(fp);
	}
	

