/* -----------------------------------------------------------------------------
 * 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.
 *
 * -----------------------------------------------------------------------------
 *
 * defCallbackCollector : This class is used to collect the callbacks from a
 *                        a single cdevDefCollectionRequest.  This is an 
 *                        implementation class that is only associated with the
 *                        default cdevCollectionObject if the developer's 
 *                        service does not provide specific support for 
 *                        collections.
 *
 *                        The cdevCollectionRequest object will obtain the 
 *                        cdevData object from this object and will return it
 *                        to the caller or to an interim 
 *                        cdevGrpCollectionRequest object.
 *
 * Author:  Walt Akers
 *
 * Revision History:
 *   defCallbackCollector.cc,v
// Revision 1.1  1996/11/12  20:32:35  akers
// New collection device source code
//
 * -----------------------------------------------------------------------------
 */

#include "defCallbackCollector.h"

// *****************************************************************************
// * defCallbackCollector::defCallbackCollector :
// *	This is the constructor for the defCallbackCollectior.  It initializes
// *	all internal data items.
// *****************************************************************************
defCallbackCollector::defCallbackCollector(int NRequests, cdevData &Format, cdevTranObj &XObj )
	: counter(0),
	  requests (NULL),
	  nRequests(NRequests),
	  result   (Format),
	  format   (Format),
	  xobj     (&XObj)
	{
	requests = new Request[nRequests];
	for(int i=0; i<nRequests; i++)
		{
		requests[i].index     = i;
		requests[i].processed = 0;
		requests[i].parent    = this;
		}
	}

// *****************************************************************************
// * defCallbackCollector::~defCallbackCollector :
// *	This is the destructor for the defCallbackCollector object, it will
// *	delete any memory that was allocated during the constructor.
// *****************************************************************************
defCallbackCollector::~defCallbackCollector ( 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, nRequests);
			}
		++iter;
		}

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

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

// *****************************************************************************
// * defCallbackCollector::processRequest :
// *	This method will copy the contents of the cdevData object associated
// *	with a single request to the result cdevData object that will hold
// *	the output for all of the requests..
// *
// *	This method increments the counter that is used to detect when all of
// *	the collections have been processed.
// *****************************************************************************
void defCallbackCollector::processRequest ( int idx, int status, cdevData *data ) 
	{ 
	if(idx>=0 && idx<nRequests && !requests[idx].processed)
		{
		if(data!=NULL)	
			{
			data->insert(cdevCollectionRequest::resultCodeTag(), status);
			mergeData  (result, *data, nRequests, idx);
			}
		else	{
			cdevData temp;
			temp.insert(cdevCollectionRequest::resultCodeTag(), status);
			mergeData(result, temp, nRequests, idx);
			}
		requests[idx].processed = 1;
		counter++;
		}
	}


// *****************************************************************************
// * defCallbackCollector::mergeData :
// *	This method will copy the data items that are associated with
// *	an inidividual request to the correct location in the arrays of the
// *	result cdevData object that will hold the combined output from all of 
// *	the individual requests.
// *****************************************************************************
void defCallbackCollector::mergeData (cdevData &to, cdevData &from, int arraySize, 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>1) done = 1;
		else to.getElems(tag, &elems); 

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

		++iter;
		}
	}


// *****************************************************************************
// * defCallbackCollector::copyItemToArray :
// *	This method will copy the element in a single tagged data item to its
// *	correct index in the output array.
// *****************************************************************************
void defCallbackCollector::copyItemToArray ( cdevData &to, cdevData &from, int tag, int idx)
	{
	size_t        size;
	cdevDataTypes fromType   = from.getType(tag);
	cdevDataTypes toType     = to.getType(tag);
	cdevDataTypes targetType;
	
	to.getElems(tag, &size);

	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(size>idx && idx>=0) switch(targetType)
		{
		case CDEV_BYTE:
			{
			unsigned char * ptr;
			unsigned char   item;
			if(targetType!=toType)
				{
				ptr = new unsigned char[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find(tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}
		
		case CDEV_INT16:
			{
			short * ptr;
			short   item;
			if(targetType!=toType)
				{
				ptr = new short[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}

		case CDEV_UINT16:
			{
			unsigned short  * ptr;
			unsigned short    item;
			if(targetType!=toType)
				{
				ptr = new unsigned short[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}

		case CDEV_INT32:
			{
			long  * ptr;
			long    item;
			if(targetType!=toType)
				{
				ptr = new long[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}

		case CDEV_UINT32:
			{
			unsigned long  * ptr;
			unsigned long    item;
			if(targetType!=toType)
				{
				ptr = new unsigned long[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}

		case CDEV_FLOAT:
			{
			float  * ptr;
			float    item;
			if(targetType!=toType)
				{
				ptr = new float[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}

		case CDEV_DOUBLE:
			{
			double  * ptr;
			double    item;
			if(targetType!=toType)
				{
				ptr = new double[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}

		case CDEV_STRING:
			{
			int i;
			char ** ptr = new char *[size];
			memset(ptr, 0, sizeof(char *)*size);
			to.get(tag, ptr);
			delete ptr[idx];
			from.get(tag, &ptr[idx]);
			to.insert(tag, ptr, size);
			for(i=0; i<size; i++)  delete ptr[i];
			delete ptr;
			break;
			}

		case CDEV_TIMESTAMP:
			{
			cdev_TS_STAMP  * ptr;
			cdev_TS_STAMP    item;
			if(targetType!=toType)
				{
				ptr = new cdev_TS_STAMP[size];
				to.get(tag, ptr);
				to.insert(tag, ptr, size);
				delete ptr;
				}
			to.find (tag, (void *&)ptr);
			from.get(tag, &item);
			ptr[idx] = item;
			break;
			}
	
		case CDEV_INVALID:
		break;			
		}
	}
	
	
// *****************************************************************************
// * defCallbackCollector::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 defCallbackCollector::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;			
		}
	}
