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

int accumulationValue  = 0;
int accumulationCount  = 0;
int accumulationSeqCnt = 0;

char tempBuffer[1024]  = "\0";
char outputBuffer [65536] = "\0";

void accumulationCallback (int status, void *, cdevRequestObject &, cdevData & data)
	{
	int newValue;
	if(status == CDEV_SUCCESS) accumulationCount++;
	data.get("value", &newValue);
	if(accumulationValue!=newValue-1)
		{
		sprintf(tempBuffer, "   => ERROR: Missed a callback between value %i and new value %i\n", accumulationValue, newValue);
		strcat (outputBuffer, tempBuffer);
		accumulationSeqCnt++;
		}
	accumulationValue=newValue;	
	}

void countingCallback ( int status, void * arg, cdevRequestObject &, cdevData &)
	{
	if(status==CDEV_SUCCESS)
		{
		int * ptr = (int *)arg;
		*ptr = (*ptr)+1;
		}
	}

int accumulationTest ( char * device, char * attrib, int steps )
	{
	struct timeval first, second, lapsed;
	struct timezone tzp;
	int  resultCode   = 0;
	int  expectedHits = 0;
	int  callbackHits = 0;
	int  i;
	char monMsg[255];
	char mofMsg[255];
	char setMsg[255];
	cdevCallback cb      (accumulationCallback, NULL);
	cdevCallback counter (countingCallback, (void *)&callbackHits);
	cdevData     data;

	sprintf(monMsg, "monitorOn %s", attrib);
	sprintf(mofMsg, "monitorOff %s", attrib);
	sprintf(setMsg, "set %s",       attrib);
	
	*outputBuffer = 0;
	strcat(outputBuffer, "******************************************************************************\n");
	strcat(outputBuffer, "                       CUMULATIVE MONITORING TESTS	                       \n");  
	strcat(outputBuffer, "------------------------------------------------------------------------------\n");

	gettimeofday(&first, &tzp);
	cdevRequestObject & monReq = cdevRequestObject::attachRef(device, monMsg);
	cdevRequestObject & mofReq = cdevRequestObject::attachRef(device, mofMsg);
	cdevRequestObject & setReq = cdevRequestObject::attachRef(device, setMsg);
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "=> TIMER: Time necessary to create cdevRequestObjects: %li.%06li seconds\n\n", lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);
	
	// ***************************
	// * Start with a synchronous
	// * send test.
	// ***************************
	accumulationValue = -1;
	data.insert        ("value", 0);
	setReq.send        (data, NULL);
	monReq.sendCallback(NULL, cb);
	cdevSystem::defaultSystem().poll();
	accumulationCount =  0;
	accumulationSeqCnt = 0;
	
	sprintf(tempBuffer, "=> Attempting to submit %i changes with send\n", steps);
	strcat (outputBuffer, tempBuffer);

	gettimeofday(&first, &tzp);
	for(i=1; i<=steps; i++)
		{
		data.insert("value", i);
		if(setReq.send(data, NULL)==CDEV_SUCCESS) expectedHits++;
		}
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "   => TIMER: Time to submit %i set requests : %li.%06li seconds\n", steps, lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);

	if(expectedHits < steps)
		{
		sprintf(tempBuffer, "   => ERROR: Could not submit all requests to the device\n      => Attempted Sends: %i\n      => Actual Sends  : %i\n", steps, expectedHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(expectedHits > accumulationCount)
		{
		sprintf(tempBuffer, "   => ERROR: Not all changes were delivered to callback\n      => Expected Hits: %i\n      => Actual Hits  : %i\n", expectedHits, accumulationCount);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	else if (expectedHits < accumulationCount)
		{
		sprintf(tempBuffer, "   => WARN:  Additional changes were delivered to callback\n      => Expected Hits: %i\n      => Actual Hits  : %i\n", expectedHits, accumulationCount);
		strcat (outputBuffer, tempBuffer);
		}
	if(accumulationSeqCnt > 0)
		{
		sprintf(tempBuffer, "   => ERROR: %i Callbacks were out of sequence\n", accumulationSeqCnt);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(resultCode != -1) strcat(outputBuffer, "   => PASSED: Successfully passed the send monitoring test\n\n");
	else strcat(outputBuffer, "   => FAILED: Failed to correctly execute send monitoring test\n\n"); 

	// ***************************
	// * Next with an asynchronous
	// * sendCallback test.
	// ***************************
	resultCode = resultCode?-2:0;
	cdevSystem::defaultSystem().poll();
	accumulationValue = -1;
	data.insert ("value", 0);
	setReq.send (data, NULL);
	cdevSystem::defaultSystem().poll();
	accumulationCount  =  0;
	accumulationSeqCnt = 0;
	
	sprintf(tempBuffer, "=> Attempting to submit %i changes with sendCallback\n", steps);
	strcat (outputBuffer, tempBuffer);

	gettimeofday(&first, &tzp);
	for(expectedHits=0, i=1; i<=steps; i++)
		{
		data.insert("value", i);
		if(setReq.sendCallback(data, counter)==CDEV_SUCCESS) expectedHits++;
		}

	cdevTimeValue      t(15.0);
	cdevClock          timer;
	timer.schedule(NULL, t);
	do 	{
		cdevSystem::defaultSystem().poll();
		} while((accumulationCount<expectedHits || 
		        callbackHits<expectedHits)      &&
		        !timer.expired());
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "   => TIMER: Time to submit %i set requests : %li.%06li seconds\n", steps, lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);

	if(expectedHits < steps)
		{
		sprintf(tempBuffer, "   => ERROR: Could not submit all requests to the device\n      => Attempted Sends: %i\n      => Actual Sends  : %i\n", steps, expectedHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(expectedHits > callbackHits)
		{
		sprintf(tempBuffer, "   => ERROR: Not all set commands were processed\n      => Actual Sends: %i\n      => Sends Processed : %i\n", expectedHits, callbackHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;	
		}
	if(expectedHits > accumulationCount)
		{
		sprintf(tempBuffer, "   => ERROR: Not all changes were delivered to callback\n      => Expected Hits: %i\n      => Actual Hits  : %i\n", expectedHits, accumulationCount);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	else if (expectedHits < accumulationCount)
		{
		sprintf(tempBuffer, "   => WARN:  Additional changes were delivered to callback\n      => Expected Hits: %i\n      => Actual Hits  : %i\n", expectedHits, accumulationCount);
		strcat (outputBuffer, tempBuffer);
		}
	if(accumulationSeqCnt > 0)
		{
		sprintf(tempBuffer, "   => ERROR: %i Callbacks were out of sequence\n", accumulationSeqCnt);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(resultCode != -1) strcat(outputBuffer, "   => PASSED: Successfully passed the sendCallback monitoring test\n\n");
	else strcat(outputBuffer, "   => FAILED: Failed to correctly execute sendCallback monitoring test\n\n"); 

	// ***************************
	// * Finally strike with an
	// * asynchronous
	// * sendNoBlock test.
	// ***************************
	resultCode = resultCode?-2:0;
	cdevSystem::defaultSystem().poll();
	accumulationValue = -1;
	data.insert ("value", 0);
	setReq.send (data, NULL);
	cdevSystem::defaultSystem().poll();
	accumulationCount  =  0;
	accumulationSeqCnt = 0;
	
	sprintf(tempBuffer, "=> Attempting to submit %i changes with sendNoBlock\n", steps);
	strcat (outputBuffer, tempBuffer);

	gettimeofday(&first, &tzp);
	
	cdevGroup group;
	group.start();
	for(expectedHits=0, i=1; i<=steps; i++)
		{
		data.insert("value", i);
		if(setReq.sendNoBlock(data, NULL)==CDEV_SUCCESS) expectedHits++;
		}
	group.end();

	timer.schedule(NULL, t);
	do 	{
		group.poll();
		} while(!group.allFinished() && !timer.expired());

	while(accumulationCount<expectedHits && !timer.expired())
		cdevSystem::defaultSystem().poll();
	
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "   => TIMER: Time to submit %i set requests : %li.%06li seconds\n", steps, lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);

	if(expectedHits < steps)
		{
		sprintf(tempBuffer, "   => ERROR: Could not submit all requests to the device\n      => Attempted Sends: %i\n      => Actual Sends  : %i\n", steps, expectedHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(expectedHits > callbackHits)
		{
		sprintf(tempBuffer, "   => ERROR: Not all set commands were processed\n      => Actual Sends: %i\n      => Sends Processed : %i\n", expectedHits, callbackHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;	
		}
	if(expectedHits > accumulationCount)
		{
		sprintf(tempBuffer, "   => ERROR: Not all changes were delivered to callback\n      => Expected Hits: %i\n      => Actual Hits  : %i\n", expectedHits, accumulationCount);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	else if (expectedHits < accumulationCount)
		{
		sprintf(tempBuffer, "   => WARN:  Additional changes were delivered to callback\n      => Expected Hits: %i\n      => Actual Hits  : %i\n", expectedHits, accumulationCount);
		strcat (outputBuffer, tempBuffer);
		}
	if(accumulationSeqCnt > 0)
		{
		sprintf(tempBuffer, "   => ERROR: %i Callbacks were out of sequence\n", accumulationSeqCnt);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(resultCode != -1) strcat(outputBuffer, "   => PASSED: Successfully passed the sendNoBlock monitoring test\n\n");
	else strcat(outputBuffer, "   => FAILED: Failed to correctly execute sendNoBlock monitoring test\n\n"); 


	// **************************
	// * Detach the monitor using
	// * the monitorOff command.
	// **************************
	mofReq.sendCallback(NULL, cb);
	cdevSystem::defaultSystem().poll();

	strcat(outputBuffer, "******************************************************************************\n\n");
	return resultCode;
	}

int getTest ( char * device, char * attrib, int steps)
	{
	struct timeval first, second, lapsed;
	struct timezone tzp;
	int  i;
	int  resultCode   = 0;
	int  expectedHits = 0;
	int  callbackHits = 0;
	char getMsg[255];
	cdevData     data;
	cdevCallback counter (countingCallback, (void *)&callbackHits);
	sprintf(getMsg, "get %s", attrib);
	
	*outputBuffer = 0;
	strcat(outputBuffer, "******************************************************************************\n");
	strcat(outputBuffer, "                              SIMPLE GET TESTS\n");  
	strcat(outputBuffer, "------------------------------------------------------------------------------\n");

	gettimeofday(&first, &tzp);
	cdevRequestObject & getReq = cdevRequestObject::attachRef(device, getMsg);
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "=> TIMER: Time necessary to create cdevRequestObjects: %li.%06li seconds\n\n", lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);
	
	// ***************************
	// * Start with a synchronous
	// * send test.
	// ***************************
	sprintf(tempBuffer, "=> Attempting to submit %i get requests with send\n", steps);
	strcat (outputBuffer, tempBuffer);

	gettimeofday(&first, &tzp);
	for(i=1; i<=steps; i++) if(getReq.send(NULL, data)==CDEV_SUCCESS) expectedHits++;
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "   => TIMER: Time to submit %i get requests : %li.%06li seconds\n", steps, lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);

	if(expectedHits < steps)
		{
		sprintf(tempBuffer, "   => ERROR: Could not submit all requests to the device\n      => Attempted Sends: %i\n      => Actual Sends  : %i\n", steps, expectedHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(resultCode != -1) strcat(outputBuffer, "   => PASSED: Successfully passed the send test\n\n");
	else strcat(outputBuffer, "   => FAILED: Failed to correctly execute send test\n\n"); 

	// ***************************
	// * Next with an asynchronous
	// * sendCallback test.
	// ***************************
	if(resultCode<0) resultCode = -2;
	sprintf(tempBuffer, "=> Attempting to submit %i get requests with sendCallback\n", steps);
	strcat (outputBuffer, tempBuffer);

	gettimeofday(&first, &tzp);
	for(expectedHits=0, i=1; i<=steps; i++)
		{
		if(getReq.sendCallback(data, counter)==CDEV_SUCCESS) expectedHits++;
		}

	cdevTimeValue      t(15.0);
	cdevClock          timer;
	timer.schedule(NULL, t);
	do 	{
		cdevSystem::defaultSystem().poll();
		} while(callbackHits<expectedHits && !timer.expired());
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "   => TIMER: Time to submit %i get requests : %li.%06li seconds\n", steps, lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);

	if(expectedHits < steps)
		{
		sprintf(tempBuffer, "   => ERROR: Could not submit all requests to the device\n      => Attempted Sends: %i\n      => Actual Sends  : %i\n", steps, expectedHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(expectedHits > callbackHits)
		{
		sprintf(tempBuffer, "   => ERROR: Not all get commands were processed\n      => Actual Sends: %i\n      => Sends Processed : %i\n", expectedHits, callbackHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;	
		}
	if(resultCode != -1) strcat(outputBuffer, "   => PASSED: Successfully passed the sendCallback get test\n\n");
	else strcat(outputBuffer, "   => FAILED: Failed to correctly execute sendCallback get test\n\n"); 

	// ***************************
	// * Next with an asynchronous
	// * sendNoBlock test.
	// ***************************
	if(resultCode<0) resultCode = -2;
	sprintf(tempBuffer, "=> Attempting to submit %i get requests with sendNoBlock\n", steps);
	strcat (outputBuffer, tempBuffer);

	gettimeofday(&first, &tzp);
	cdevGroup group;
	group.start();
	for(expectedHits=0, i=1; i<=steps; i++)
		{
		if(getReq.sendNoBlock(NULL, data)==CDEV_SUCCESS) expectedHits++;
		}
	group.end();
	while(!group.allFinished()) group.poll();
	gettimeofday(&second, &tzp);

	if (first.tv_usec > second.tv_usec) 
		{
		second.tv_usec += 1000000;
		second.tv_sec--;
		}
	lapsed.tv_usec = second.tv_usec - first.tv_usec;
	lapsed.tv_sec = second.tv_sec - first.tv_sec;
	sprintf(tempBuffer, "   => TIMER: Time to submit %i get requests : %li.%06li seconds\n", steps, lapsed.tv_sec, lapsed.tv_usec);
	strcat (outputBuffer, tempBuffer);

	if(expectedHits < steps)
		{
		sprintf(tempBuffer, "   => ERROR: Could not submit all requests to the device\n      => Attempted Sends: %i\n      => Actual Sends  : %i\n", steps, expectedHits);
		strcat (outputBuffer, tempBuffer);
		resultCode = -1;
		}
	if(resultCode != -1) strcat(outputBuffer, "   => PASSED: Successfully passed the sendNoBlock get test\n\n");
	else strcat(outputBuffer, "   => FAILED: Failed to correctly execute sendNoBlock get test\n\n"); 

	strcat(outputBuffer, "******************************************************************************\n\n");
	return resultCode;
	}


int main(int argc, char ** argv)
	{
	int repetition = argc>1?(atoi(argv[1])):100;
	cdevSystem::defaultSystem().setThreshold(CDEV_SEVERITY_ERROR);

	accumulationTest("cdev_ai0", "VAL", repetition);
	fprintf(stdout, outputBuffer);
	getTest("cdev_ai0", "VAL", repetition);
	fprintf(stdout, outputBuffer);
	}
