/* -----------------------------------------------------------------------------
 * Copyright (c) 1994,1995 Southeastern Universities Research Association,
 *                         Continuous Electron Beam Accelerator Facility
 *
 * This software was developed under a United States Government license
 * described in the NOTICE file included as part of this distribution.
 *
 * -----------------------------------------------------------------------------
 *
 * grpCallbackCollector : This class is used to collect the callbacks from a
 *                        a group of collections and then compile the results
 *                        into a single cdevData object.
 *
 *                        The cdevCollectionRequest object will obtain the 
 *                        cdevData object from this object and will return it
 *                        to the caller.
 *
 * Author:  Walt Akers
 *
 * Revision History:
 *   grpCallbackCollector.cc,v
// Revision 1.1  1996/11/12  20:32:39  akers
// New collection device source code
//
 * -----------------------------------------------------------------------------
 */

#include "grpCallbackCollector.h"

// *****************************************************************************
// * grpCallbackCollector :
// *	This is the constructor for the grpCallbackCollectior.  It initializes
// *	all internal data items.
// *****************************************************************************
grpCallbackCollector::grpCallbackCollector(int NCollections, int * RequestOrder, int NRequests, cdevData &Format, cdevTranObj &XObj )
	: counter      (0),
	  nCollections (NCollections),
	  requestOrder (RequestOrder),
	  nRequests    (NRequests),
	  result       (Format),
	  format       (Format),
	  xobj         (&XObj)
	{
	// *********************************************************************
	// * Allocate a block of Request structures that will be used to 
	// * maintain information about each of the individual collections.
	// *********************************************************************
	collections = new Request[nCollections];
	
	// *********************************************************************
	// * Initialize the Request structures with the index information and
	// * count the number of requests that have the same index as this
	// * collection.
	// *********************************************************************
	for(int i=0; i<nCollections; i++)
		{
		collections[i].index     = i;
		collections[i].processed = 0;
		collections[i].parent    = this;
		collections[i].elements = 0;
		for(int j=0; j<nRequests; j++)
			{
			if(requestOrder[j]==i) 
				collections[i].elements++;
			}
		}
	}

// *****************************************************************************
// * grpCallbackCollector::~grpCallbackCollector :
// *	This is the destructor for the grpCallbackCollector object, it will
// *	delete any memory that was allocated during the constructor.
// *****************************************************************************
grpCallbackCollector::~grpCallbackCollector ( void ) 
	{ 
	// *********************************************************************
	// * Transfer the data structure to the format cdevData item - this
	// * will improve performance when the cdevRequestObject is reused.
	// *********************************************************************
	cdevDataIterator iter(&result);
	int tag;
	iter.init();
	while((tag=iter.tag())!=0)
		{
		cdevDataTypes fromType = result.getType(tag);
		cdevDataTypes toType   = format.getType(tag);
		
		if(fromType!=CDEV_INVALID && 
		   fromType!=CDEV_STRING  &&
		   fromType!=toType)
			{
			installEmptyArray(format, tag, fromType, nCollections);
			}
		++iter;
		}

	if(collections) delete collections; 
	if(xobj)        delete xobj;
	collections = NULL;
	xobj        = NULL;
	}		

// *****************************************************************************
// * grpCallbackCollector::finished :
// *	This method allows the caller to determine if all of the requests have
// *	been processed.
// *****************************************************************************
int grpCallbackCollector::finished ( void ) 
	{
	return (counter>=nCollections);
	}

// *****************************************************************************
// * grpCallbackCollector::processCollection :
// *	This method will copy the contents of the cdevData object associated
// *	with a single collection to the result cdevData object that will hold
// *	all of the results.
// *
// *	This method increments the counter that is used to detect when all of
// *	the collections have been processed.
// *****************************************************************************
void grpCallbackCollector::processCollection ( int idx, int status, cdevData *data ) 

	{ 
	if(idx>=0 && idx<nCollections && !collections[idx].processed)
		{
		if(data!=NULL) mergeData  (result, *data, idx);
		else	{
			cdevData temp;
			int    * ptr;
			installEmptyArray (temp, cdevCollectionRequest::resultCodeTag(), 
					   CDEV_INT32, collections[idx].elements);
			temp.find(cdevCollectionRequest::resultCodeTag(), (void * &)ptr);
			for(int i=0; i<collections[idx].elements; i++) ptr[i] = status;
			mergeData(result, temp, idx);
			}
		collections[idx].processed = 1;
		counter++;
		}
	}


// *****************************************************************************
// * grpCallbackCollector::mergeData :
// *	This method will copy the array of data items that is associated with
// *	an inidividual collection to the correct locations in the arrays of the
// *	result cdevData object that will hold the output from all of the
// *	individual collections.
// *****************************************************************************
void grpCallbackCollector::mergeData (cdevData &to, cdevData &from, int idx)
	{
	cdevDataIterator iter(&from);
	int tag;
	iter.init();
	while((tag=iter.tag())!=0)
		{
		int           done     = 0; 
		size_t        elems    = 0;
		cdevDataTypes fromType = from.getType(tag);
		cdevDataTypes toType   = to.getType(tag);
		from.getElems(tag, &elems);
		
		if(elems!=collections[idx].elements) done = 1;
		else to.getElems(tag, &elems); 

		if(!done && (toType==CDEV_INVALID || elems!=nRequests))
			{
			installEmptyArray(to, tag, fromType, nRequests);
			}
		if(!done) copyItemsToArray(to, from, tag, idx);

		++iter;
		}
	}


// *****************************************************************************
// * grpCallbackCollector::copyItemsToArray :
// *	This method will copy the array elements of a single tagged data item 
// *	to their correct indices in the output array.
// *****************************************************************************
void grpCallbackCollector::copyItemsToArray ( cdevData &to, cdevData &from, int tag, int idx)
	{
	size_t        toSize;
	size_t        fromSize;
	cdevDataTypes fromType   = from.getType(tag);
	cdevDataTypes toType     = to.getType(tag);
	cdevDataTypes targetType;
	
	to.getElems  (tag, &toSize);
	from.getElems(tag, &fromSize);

	if(toType==CDEV_INVALID)
		targetType = fromType;
	else	targetType = (fromType>toType)?fromType:toType;

	// *********************************************************************
	// * Even though the CDEV_TIME_STAMP is a senior type - it should not
	// * overpower any other data type.
	// *********************************************************************
	if(targetType==CDEV_TIMESTAMP)
		{
		if(fromType!=targetType && fromType!=CDEV_INVALID) targetType=fromType;
		else if(toType!=targetType && toType!=CDEV_INVALID) targetType=toType;
		}
		
	if(toSize>idx && idx>=0) switch(targetType)
		{
		case CDEV_BYTE:
			{
			int i, j;
			unsigned char * ptr;
			unsigned char * items;
			if(targetType!=toType)
				{
				ptr = new unsigned char[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new unsigned char[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)items);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = items[j++];
					}
				}
			break;
			}
		
		case CDEV_INT16:
			{
			int i, j;
			short *ptr;
			short *item;
			if(targetType!=toType)
				{
				ptr = new short[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new short[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}

		case CDEV_UINT16:
			{
			int i, j;
			unsigned short  * ptr;
			unsigned short  * item;
			if(targetType!=toType)
				{
				ptr = new unsigned short[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new unsigned short[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}

		case CDEV_INT32:
			{
			int i, j;
			long  *ptr;
			long  *item;
			if(targetType!=toType)
				{
				ptr = new long[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new long[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}

		case CDEV_UINT32:
			{
			int i, j;
			unsigned long  *ptr;
			unsigned long  *item;
			if(targetType!=toType)
				{
				ptr = new unsigned long[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new unsigned long[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}

		case CDEV_FLOAT:
			{
			int i, j;
			float  *ptr;
			float  *item;
			if(targetType!=toType)
				{
				ptr = new float[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new float[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}

		case CDEV_DOUBLE:
			{
			int i, j;
			double  *ptr;
			double  *item;
			if(targetType!=toType)
				{
				ptr = new double[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new double[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}

		case CDEV_STRING:
			{
			int i, j;
			char ** ptr   = new char *[toSize];
			char ** item = new char *[fromSize];
			memset(ptr, 0, sizeof(char *)*toSize);
			to.get(tag, ptr);
			from.get(tag, item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					delete ptr[i];
					ptr[i]     = item[j];
					item[j++] = NULL;
					}
				}
			to.insert(tag, ptr, toSize);
			for(i=0; i<toSize; i++)  delete ptr[i];
			for(i=0; i<fromSize; i++) if(item[i]) delete item[i];
			delete ptr;
			delete item;
			break;
			}

		case CDEV_TIMESTAMP:
			{
			int i, j;
			cdev_TS_STAMP  *ptr;
			cdev_TS_STAMP  *item;
			if(targetType!=toType)
				{
				ptr = new cdev_TS_STAMP[toSize];
				to.get(tag, ptr);
				to.insert(tag, ptr, toSize);
				delete ptr;
				}
			if(targetType!=fromType)
				{
				ptr = new cdev_TS_STAMP[fromSize];
				from.get(tag, ptr);
				from.insert(tag, ptr, fromSize);
				delete ptr;
				}
			to.find  (tag, (void* &)ptr);
			from.find(tag, (void* &)item);
			for(j=0, i=0; i<nRequests; i++)
				{
				if(requestOrder[i]==idx)
					{
					ptr[i] = item[j++];
					}
				}
			break;
			}
	
		case CDEV_INVALID:
		break;			
		}
	}
	
	
// *****************************************************************************
// * grpCallbackCollector::installEmptyArray :
// *	This method will create an empty array that is of the specified size
// *	and type.  By creating empty arrays in the cdevData object, the caller
// *	may insert data directly into the data structure rather than using
// *	the cdevData interface.
// *****************************************************************************
void grpCallbackCollector::installEmptyArray ( cdevData &data, int tag, cdevDataTypes type, int size)
	{
	switch(type)
		{
		case CDEV_BYTE:
			{
			unsigned char * ptr = new unsigned char[size];
			memset(ptr, 0, sizeof(unsigned char)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}
			
		case CDEV_INT16:
			{
			short * ptr = new short[size];
			memset(ptr, 0, sizeof(short)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_UINT16:
			{
			unsigned short * ptr = new unsigned short[size];
			memset(ptr, 0, sizeof(unsigned short)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_INT32:
			{
			long * ptr = new long[size];
			memset(ptr, 0, sizeof(long)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_UINT32:
			{
			unsigned long * ptr = new unsigned long[size];
			memset(ptr, 0, sizeof(unsigned long)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_FLOAT:
			{
			float * ptr = new float[size];
			memset(ptr, 0, sizeof(float)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_DOUBLE:
			{
			double * ptr = new double[size];
			memset(ptr, 0, sizeof(double)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_STRING:
			{
			char ** ptr = new char *[size];
			char    val = '\0';
			for(int i=0; i<size; i++) ptr[i] = &val;
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}

		case CDEV_TIMESTAMP:
			{
			cdev_TS_STAMP * ptr = new cdev_TS_STAMP[size];
			memset(ptr, 0, sizeof(cdev_TS_STAMP)*size);
			data.insert(tag, ptr, size);
			delete ptr;
			break;
			}
		
		case CDEV_INVALID:
		break;			
		}
	}
