#include <cdevPlatforms.h>
#include <cdevSystem.h>
#include <cdevRequestObject.h>
#include <cdevDevice.h>
#include <cdevGroup.h>
#include <cdevCommon.h>
#include <cdevMessage.h>

void callback ( int status, void *, cdevRequestObject & req, cdevData & data );

struct timeval first;
int            testLength         = 0x0FFFFFFF;
int            callbackCount      = 0;
int            reportCount        = 0;
int            reportFrequency    = 0;
int            bunchSize          = 0;
char *         deviceName         = "device0";
char *         messageStr         = "set attrib0";
double         PerformanceRate    = 0.0;
double *       performanceRatePtr = &PerformanceRate;
size_t         PacketSize         = 0;
size_t *       packetSizePtr      = &PacketSize;
cdevCallback   cb(callback, NULL);

void callback ( int status, void *, cdevRequestObject & req, cdevData & data )
	{
	if(status==CDEV_SUCCESS)
		{
		callbackCount++;
		reportCount++;

		if(reportCount==reportFrequency)
			{
			reportCount = 0;
			struct timeval second, lapsed;
			
			gettimeofday(&second);	

			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;
					
			if(lapsed.tv_sec)
				{
				*performanceRatePtr = (double)callbackCount/(double)lapsed.tv_sec;
				fprintf(stdout, "Average rate is %lf pkts/sec after %ld packets\n", *performanceRatePtr, callbackCount);		
				}
			fflush(stdout);

			if(callbackCount>=testLength)
				{
				exit(0);
				}
			}

		req.sendCallback(data, cb);

		}
	}


cdevData * createPayload1 ( int )
	{
	char   * binary    = 0;
	cdevMessage msg(1, 1, 0, 0, 0, 0, 0, 1, &deviceName, messageStr);
	msg.streamOut(&binary, packetSizePtr);
	delete binary;
	return NULL;
	}
	
cdevData * createPayload2 ( int cnt )
	{
	cdevData * data = new cdevData;
	if(cnt>1) 
		{
		int * x = new int[cnt];
		for(int i=0; i<cnt; i++) x[i] = (int)i;
		data->insert("value", x, cnt);
		delete x;
		}
	else data->insert("value", 1);

	char   * binary    = 0;
	cdevMessage msg(1, 1, 0, 0, 0, 0, 0, 1, &deviceName, messageStr, data);
	msg.streamOut(&binary, packetSizePtr);
	delete binary;

	return data;
	}
	
cdevData * createPayload3 ( int cnt )
	{
	cdevData * data = new cdevData;
	if(cnt>1) 
		{
		float * x = new float[cnt];
		for(int i=0; i<cnt; i++) x[i] = (float)i;
		data->insert("value", x, cnt);
		delete x;
		}
	else data->insert("value", (float)1.0);

	char   * binary    = 0;
	cdevMessage msg(1, 1, 0, 0, 0, 0, 0, 1, &deviceName, messageStr, data);
	msg.streamOut(&binary, packetSizePtr);
	delete binary;

	return data;
	}

cdevData * createPayload4 ( int cnt )
	{
	cdevData * data = new cdevData;
	if(cnt>1) 
		{
		double * x = new double[cnt];
		for(int i=0; i<cnt; i++) x[i] = (double)i;
		data->insert("value", x, cnt);
		delete x;
		}
	else data->insert("value", (double)1.0);

	char   * binary    = 0;
	cdevMessage msg(1, 1, 0, 0, 0, 0, 0, 1, &deviceName, messageStr, data);
	msg.streamOut(&binary, packetSizePtr);
	delete binary;

	return data;
	}
	
cdevData * createPayload5 ( int cnt )
	{
	cdevData * data = new cdevData;
	char buf[64];
	
	if(cnt>1) 
		{
		char ** x = new char *[cnt];
		int     i; 
		for(i=0; i<cnt; i++)
			{
			sprintf(buf, "This is string %i", i);
			x[i] = strdup(buf);
			}
		data->insert("value", x, cnt);
		for(i=0; i<cnt; i++)
			{
			free(x[i]);
			}
		delete x;
		}
	else
		{
		sprintf(buf, "This is string 0");
		data->insert("value", buf);
		}

	char   * binary    = 0;
	cdevMessage msg(1, 1, 0, 0, 0, 0, 0, 1, &deviceName, messageStr, data);

	msg.streamOut(&binary, packetSizePtr);
	delete binary;

	return data;	
	}

const int payloadCount = 10;

char * payloadType[payloadCount] = 
	{
	"None",
	"1 - Barebones cdevMessage",
	"2 - cdevMessage with one integer value",
	"3 - cdevMessage with one float value",
	"4 - cdevMessage with one double value",
	"5 - cdevMessage with a character string",
	"6 - cdevMessage with an array of integers",
	"7 - cdevMessage with an array of floats",
	"8 - cdevMessage with an array of doubles",
	"9 - cdevMessage with an array of strings"
	};

typedef cdevData * (*PayloadFunc)(int cnt);

PayloadFunc payloadFunc[payloadCount] =
	{
	NULL,
	createPayload1,
	createPayload2,
	createPayload3,
	createPayload4,
	createPayload5,
	createPayload2,
	createPayload3,
	createPayload4,
	createPayload5
	};

int main ( int argc, char ** argv )
	{
	int          i;
	int          payload      = 0;
	int          payloadItems = 0;
		
	if(argc>1) for(i=1; i<argc; i++)
		{
		if(*argv[i]!='-') goto QUIT_LABEL;
		else	{
			if((i+1)>=argc) goto QUIT_LABEL;

			switch (argv[i][1])
				{
				case 'd': // (d)evice
				deviceName = strdup(argv[++i]);
				break;
				
				case 'm': // (m)essage
				messageStr = strdup(argv[++i]);
				break;
				
				case 't': // payload (t)ype
				payload = atoi(argv[++i]);
				break;
				
				case 'e': // payload (e)lements
				payloadItems = atoi(argv[++i]);
				break;
				
				case 'b': // (b)unch size
				bunchSize = atoi(argv[++i]);
				break;
				
				case 'f': // report (f)requency
				reportFrequency = atoi(argv[++i]);
				break;
				
				case 'c': // packet (c)ount
				testLength = atoi(argv[++i]);
				break;
				
				case 'h':
				default:
				QUIT_LABEL:
				printf("\nFormat is: PerformanceTest -d dev -m msg -t type -e elements -b bunch -f freq -c count\n");
				printf("              -d dev      ... where dev is the name of the cdev device\n");
				printf("              -m msg      ... where msg is the cdev message string\n");
				printf("              -t type     ... where type is the outbound data's payload type\n");
				for(int x=1; x<payloadCount; x++)
					{
					printf("                              %s\n", payloadType[x]);
					}			
				printf("              -e elements ... where elements is the number of array elements\n");
				printf("              -b bunch    ... where bunch is the number to send before waiting\n");
				printf("              -f freq     ... where freq is the report frequency\n");
				printf("              -c count    ... where count is the number of messages to send\n\n");
				fflush(stdout);
				return 0;
				}
			}
		}

	cdevData   * data = NULL;

	if(payload<1 || payload>=payloadCount)
		{
		fprintf(stdout, "Payload type specifies the type of data that will\n");
		fprintf(stdout, "transmitted in each message...\n");
		for(i=1; i<payloadCount; i++)
			{
			fprintf(stdout, "    %s\n", payloadType[i]);
			}
		fprintf(stdout, "\n");
	
		while(payload<1 || payload>=payloadCount)
			{
			fprintf(stdout, "Enter payload type (1 - %i): ", payloadCount-1);
			fscanf (stdin, "%ld", &payload);
			}
		}
			
	if(payload>5)
		{
		if(payloadItems<1)
			{
			for(payloadItems = 0; payloadItems<1; )
				{
				fprintf(stdout, "Enter the number of array elements: ");
				fscanf(stdin, "%ld", &payloadItems);
				}
			}
		}
	else payloadItems = 1;
	
	data = payloadFunc[payload](payloadItems);
	
	if(bunchSize<1 || bunchSize>500)
		{
		fprintf(stdout, "\nBunch size is the number of packets that will be\n");
		fprintf(stdout, "transmitted TO the server before the application\n");
		fprintf(stdout, "pauses to listen for replies FROM the server.\n\n");
		while(bunchSize<1 || bunchSize>500)
			{
			fprintf(stdout, "Enter bunch size (1-500): ");
			fscanf (stdin,  "%ld", &bunchSize);
			}
		}
				
	if(reportFrequency<1)
		{
		fprintf(stdout, "\nReport frequency is the number of packets that\n");
		fprintf(stdout, "should be received from the server before the application\n");
		fprintf(stdout, "pauses to calculate and report performance information.\n\n");
		while(reportFrequency<1)
			{
			fprintf(stdout, "Enter report frequency (1 - Infinity): ");
			fscanf (stdin,  "%ld", &reportFrequency);
			}
		}
			
	fprintf(stdout, "\nStarting Test:\n=> Device       : %s\n=> Message      : %s\n=> Payload      : %s\n=> Packet size  : %ld\n=> Bunch size   : %ld\n=> Frequency    : %ld\n", deviceName, messageStr, payloadType[payload], *packetSizePtr, bunchSize, reportFrequency);	
	if(payloadItems>1) fprintf(stdout, "=> Array Size   : %ld\n", payloadItems);
	fprintf(stdout, "=> Test Length  : %ld Packets\n", testLength);
	
	cdevRequestObject & req = cdevRequestObject::attachRef(deviceName, messageStr);
	if(req.send(NULL, NULL)!=CDEV_SUCCESS)
		{
		fprintf(stdout, "\nPerformanceTest Error: Request %s %s appears to be invalid\nExiting...\n\n", deviceName, messageStr); 
		fflush(stdout);
		return 0;
		}

	gettimeofday(&first);

	for(i=0; i<bunchSize; i++) req.sendCallback(data, cb);

	while( 1 )
		{		
		cdevSystem::defaultSystem().pend(10.0);
		}
		
	return 0;
	}
