#include <cdevSystem.h>
#include <cdevRequestObject.h>
#include <cdevData.h>
#include <cdevCallback.h>

class rnsQueryCollector
{
private:
	cdevData         * data;
	int                deleteFlag;
	unsigned char    * byteData;
	short            * shortData;
	unsigned short   * ushortData;
	long             * longData;
	unsigned long    * ulongData;
	float            * floatData;
	double           * doubleData;
	cdev_TS_STAMP    * tsData;
	size_t             byteCnt;
	size_t             shortCnt;
	size_t             ushortCnt;
	size_t             longCnt;
	size_t             ulongCnt;
	size_t             floatCnt;
	size_t             doubleCnt;
	size_t             tsCnt;
			
public:
	rnsQueryCollector ( cdevData & userData, int deleteData = 0 )
		: data(&userData), deleteFlag(deleteData),
		  byteData(NULL), shortData(NULL), ushortData(NULL),
		  longData(NULL), ulongData(NULL), floatData(NULL),
		  doubleData(NULL), tsData(NULL), byteCnt(0), 
		  shortCnt(0), ushortCnt(0), longCnt(0), ulongCnt(0), 
		  floatCnt(0), doubleCnt(0), tsCnt(0)
		{
		data->remove();
		}
		
	~rnsQueryCollector ( void )
		{
		if(deleteFlag) delete data;
		if(byteData)   delete byteData;
		if(shortData)  delete shortData;
		if(ushortData) delete ushortData;
		if(longData)   delete longData;
		if(ulongData)  delete ulongData;
		if(floatData)  delete floatData;
		if(doubleData) delete doubleData;
		if(tsData)     delete tsData;
		}
		
	cdevData * getData   ( void ) { return data;  }
	void       clearData ( void ) { data->remove(); }
	inline int collect   ( cdevData & userData );

private:
	inline int  collectAttribute          ( cdevData & userData, int tag, int position );
	inline void collectByteAttribute      ( cdevData & userData, int tag, int position );
	inline void collectShortAttribute     ( cdevData & userData, int tag, int position );
	inline void collectUShortAttribute    ( cdevData & userData, int tag, int position );
	inline void collectLongAttribute      ( cdevData & userData, int tag, int position );
	inline void collectULongAttribute     ( cdevData & userData, int tag, int position );
	inline void collectFloatAttribute     ( cdevData & userData, int tag, int position );
	inline void collectDoubleAttribute    ( cdevData & userData, int tag, int position );
	inline void collectStringAttribute    ( cdevData & userData, int tag, int position );
	inline void collectTimestampAttribute ( cdevData & userData, int tag, int position );
};


// *****************************************************************************
// * class rnsQueryCallback :
// *	This class provides the methods to allow a rnsQueryCollector to be 
// *	incorporated into a cdevCallback.  The localCallback function will
// *	be called with results until the data has been completely collected,
// *	and only then will the user callback be executed - providing full
// *	data.
// *****************************************************************************
class rnsQueryCallback : public cdevCallback
{
private:
	rnsQueryCollector collector;
	cdevCallback      callback;
	
public:
	rnsQueryCallback ( cdevCallback & Callback )
		: cdevCallback(), callback(Callback), collector(*(new cdevData), 1)	
		{
		userarg_  = this;
		function_ = rnsQueryCallback::localCallback; 
		}
	
	virtual ~rnsQueryCallback ( void )
		{
		}
		
	static void localCallback ( int status, void * arg, cdevRequestObject & req, cdevData & data)
		{
		rnsQueryCallback * qCallback;
	
		if((qCallback = (rnsQueryCallback *)arg)!=NULL)
			{
			if(status==CDEV_SUCCESS || 
			   status==CDEV_INCOMPLETE)
				{
				qCallback->collector.collect(data);
				}

			if(status==CDEV_SUCCESS   || 
			   status==CDEV_ERROR     || 
			   status==CDEV_NOTFOUND  || 
			   status==CDEV_MSG_ERR)
				{
				qCallback->callback.fireCallback(status, qCallback->callback.userarg(), 
								 req, *qCallback->collector.getData(), 0);
				}
			}
		}
};


// *****************************************************************************
// * rnsQueryCollector::collect :
// *	This method is called when a cdevData object needs to be appended to 
// *	the cdevData object that is serving as a collection.  This method will
// *	transfer each of the data items in the userData object to the proper
// *	position in the local data object.
// *****************************************************************************
int rnsQueryCollector::collect ( cdevData & userData )
	{
	int              cnt      = 0;
	size_t           size     = 0;
	cdevDataIterator iter(&userData);
	cdevDataIterator dIter(data);
	
	// *********************************************************************
	// * Look through the existing data and determine the largest number
	// * of items that exist in a single tagged item.  This value will be
	// * used to specify the position of the data that is being added to the
	// * array.
	// *********************************************************************
	dIter.init();
	while(dIter.tag()!=0)
		{
		size_t elems = 0;
		data->getElems(dIter.tag(), &elems);
		if(elems>size) size = elems;
		++dIter;
		}
		
	// *********************************************************************
	// * Walk through the list of tagged data items that are stored in the
	// * inbound data object.  Call the collectAttribute method for each 
	// * data item.  This method will copy the contents of the inbound 
	// * data object to the correct position in the local data object.
	// *********************************************************************
	iter.init();
	while(iter.tag()!=0)
		{
		cnt+=(collectAttribute(userData, iter.tag(), size)==0)?1:0;
		++iter;
		}

	// *********************************************************************
	// * Return the count of data items that were assimilated into the
	// * local data object.
	// *********************************************************************
	return cnt;
	}
	
	
// *****************************************************************************
// * rnsQueryCollector::collectAttribute :
// *	This method will assimilate a single attribute from the specified 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
int rnsQueryCollector::collectAttribute ( cdevData & userData, int tag, int position )
	{
	cdevSystem &  sys      = cdevSystem::defaultSystem();
	int           result   = 0;
	size_t        dim      = 0;
	size_t        userDim  = 0;	
	cdevDataTypes type     = data->getType(tag);
	cdevDataTypes userType = userData.getType(tag);

	if(userData.getDim(tag, &dim)==CDEV_NOTFOUND || dim > 0)
		{
		sys.reportError(CDEV_SEVERITY_ERROR, "Name Server", NULL,
				"Cannot add array data to query reply");
		result = -1;
		}
	else if(data->getDim(tag, &dim)!=CDEV_NOTFOUND && dim > 1)
		{
		sys.reportError(CDEV_SEVERITY_ERROR, "Name Server", NULL,
	   			"Cannot assemble a query with multi-dimensional data");
		result = -1;
		}
	else if (userType == CDEV_INVALID)
		{
		sys.reportError(CDEV_SEVERITY_ERROR, "Name Server", NULL,
				"Inbound query data object has invalid data type");
		result = -1;
		}
	else	{
		if(type!=CDEV_INVALID)
			{
			if(type<CDEV_TIMESTAMP && userType<CDEV_TIMESTAMP)
				{
				type = (type>userType)?type:userType;
				}
			else if(type==CDEV_TIMESTAMP) type = userType;
			}
		else type = userType;

		switch(type)
			{
			case CDEV_BYTE:
				collectByteAttribute(userData, tag, position);
				break;
			case CDEV_INT16:
				collectShortAttribute(userData, tag, position);
				break;
			case CDEV_UINT16:
				collectUShortAttribute(userData, tag, position);
				break;
			case CDEV_INT32:
				collectLongAttribute(userData, tag, position);
				break;
			case CDEV_UINT32:
				collectULongAttribute(userData, tag, position);
				break;
			case CDEV_FLOAT:
				collectFloatAttribute(userData, tag, position);
				break;
			case CDEV_DOUBLE:
				collectDoubleAttribute(userData, tag, position);
				break;
			case CDEV_STRING:
				collectStringAttribute(userData, tag, position);
				break;
			case CDEV_TIMESTAMP:
				collectTimestampAttribute(userData, tag, position);
				break;
			default:
				result = -1;
				break;
			}
		}
	return result;
	}
	
	
// *****************************************************************************
// * rnsQueryCollector::collectByteAttribute :
// *	This method will transfer the byte attribute stored in the userData 
// *	object into the local cdevData object at the specified position.
// *****************************************************************************
void rnsQueryCollector::collectByteAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>byteCnt)
		{
		if(byteData!=NULL) delete byteData;
		byteCnt = ((position+1)>100)?(position+100):100;
		byteData = new unsigned char [byteCnt];
		}
	memset(byteData, 0, byteCnt*sizeof(unsigned char));
	data->get(tag, byteData);
	userData.get(tag, &byteData[position]);
	data->insert(tag, byteData, position+1);
	}
	
// *****************************************************************************
// * rnsQueryCollector::collectShortAttribute :
// *	This method will transfer the short attribute stored in the userData 
// *	object into the local cdevData object at the specified position.
// *****************************************************************************
void rnsQueryCollector::collectShortAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>shortCnt)
		{
		if(shortData!=NULL) delete shortData;
		shortCnt = ((position+1)>100)?(position+100):100;
		shortData = new short [shortCnt];
		}
	memset(shortData, 0, shortCnt*sizeof(short));
	data->get(tag, shortData);
	userData.get(tag, &shortData[position]);
	data->insert(tag, shortData, position+1);
	}

// *****************************************************************************
// * rnsQueryCollector::collectUShortAttribute :
// *	This method will transfer the unsigned short attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectUShortAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>ushortCnt)
		{
		if(ushortData!=NULL) delete ushortData;
		ushortCnt = ((position+1)>100)?(position+100):100;
		ushortData = new unsigned short [ushortCnt];
		}
	memset(ushortData, 0, ushortCnt*sizeof(unsigned short));
	data->get(tag, ushortData);
	userData.get(tag, &ushortData[position]);
	data->insert(tag, ushortData, position+1);
	}

// *****************************************************************************
// * rnsQueryCollector::collectLongAttribute :
// *	This method will transfer the long attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectLongAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>longCnt)
		{
		if(longData!=NULL) delete longData;
		longCnt = ((position+1)>100)?(position+100):100;
		longData = new long [longCnt];
		}
	memset(longData, 0, longCnt*sizeof(long));
	data->get(tag, longData);
	userData.get(tag, &longData[position]);
	data->insert(tag, longData, position+1);
	}

// *****************************************************************************
// * rnsQueryCollector::collectULongAttribute :
// *	This method will transfer the unsigned long attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectULongAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>ulongCnt)
		{
		if(ulongData!=NULL) delete ulongData;
		ulongCnt = ((position+1)>100)?(position+100):100;
		ulongData = new unsigned long [ulongCnt];
		}
	memset(ulongData, 0, ulongCnt*sizeof(unsigned long));
	data->get(tag, ulongData);
	userData.get(tag, &ulongData[position]);
	data->insert(tag, ulongData, position+1);
	}

// *****************************************************************************
// * rnsQueryCollector::collectFloatAttribute :
// *	This method will transfer the float attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectFloatAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>floatCnt)
		{
		if(floatData!=NULL) delete floatData;
		floatCnt = ((position+1)>100)?(position+100):100;
		floatData = new float [floatCnt];
		}
	memset(floatData, 0, floatCnt*sizeof(float));
	data->get(tag, floatData);
	userData.get(tag, &floatData[position]);
	data->insert(tag, floatData, position+1);
	}

// *****************************************************************************
// * rnsQueryCollector::collectDoubleAttribute :
// *	This method will transfer the double attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectDoubleAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>doubleCnt)
		{
		if(doubleData!=NULL) delete doubleData;
		doubleCnt = ((position+1)>100)?(position+100):100;
		doubleData = new double [doubleCnt];
		}
	memset(doubleData, 0, doubleCnt*sizeof(double));
	data->get(tag, doubleData);
	userData.get(tag, &doubleData[position]);
	data->insert(tag, doubleData, position+1);
	}

// *****************************************************************************
// * rnsQueryCollector::collectStringAttribute :
// *	This method will transfer the string attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectStringAttribute (cdevData & userData, int tag, int position )
	{
	int  i;
	char blank = 0;
	
	char ** stringData = new char * [ position+1 ];
	memset(stringData, 0, (position+1)*sizeof(char *));
	data->get(tag, stringData);
	userData.get(tag, &stringData[position]);

	for(i=0; i<position+1; i++)
		{
		if(stringData[i] == NULL) stringData[i] = &blank;
		}
	data->insert(tag, stringData, position+1);

	for(i=0; i<position+1; i++)
		{
		if(stringData[i]!=&blank) delete stringData[i];
		}
	delete stringData;
	}

// *****************************************************************************
// * rnsQueryCollector::collectTimestampAttribute :
// *	This method will transfer the time stamp attribute stored in the 
// *	userData object into the local cdevData object at the specified 
// *	position.
// *****************************************************************************
void rnsQueryCollector::collectTimestampAttribute (cdevData & userData, int tag, int position )
	{
	if((position+1)>tsCnt)
		{
		if(tsData!=NULL) delete tsData;
		tsCnt = ((position+1)>100)?(position+100):100;
		tsData = new cdev_TS_STAMP [tsCnt];
		}
	memset(tsData, 0, tsCnt);
	data->get(tag, tsData);
	userData.get(tag, &tsData[position]);
	data->insert(tag, tsData, position+1);
	}
