/**********************************************************************
 * Copyright (c) 2005, 2006 Scapa Technologies Limited and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: PerfmonAgent.cpp,v 1.2 2006/10/26 22:40:22 kcallaghan Exp $
 *
 * Contributors:
 * Scapa Technologies Limited - Initial API and implementation
 * Randy D. Smith, Intel Corporation - Re-fit to the new Agent Controller
 **********************************************************************/

#include "PerfmonAgent.h"

#ifndef ERROR
#define ERROR -1
#endif

int PRINT_XML = FALSE;

// Turning on VERBOSE will trigger all the couts showing the ongoing status of this agent
int VERBOSE = FALSE;

char digits[] = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};

#define TIME_STRING_SIZE 64

void PerfmonAgent::longToString(XINT64 i, char* str)
{
	int radix = 10;
	char buf[TIME_STRING_SIZE+1];

	int charPos = TIME_STRING_SIZE;
	int negative = (i < 0);

	if (!negative) {
	    i = -i;
	}

	while (i <= -radix)
	{
	    buf[charPos--] = digits[(int)(-(i % radix))];
	    i = i / radix;
	}
	buf[charPos] = digits[(int)(-i)];

	if (negative) {
	    buf[--charPos] = '-';
	}

	memcpy(str, &(buf[charPos]), (TIME_STRING_SIZE+1 - charPos));
}

int PerfmonAgent::increaseBufferSize(char** buf, int* buflen)
{
	//double the size of the buffer
	int newlen = *buflen * 2;
	char* newbuf = (char*)malloc(newlen);

	if (VERBOSE == TRUE)
		cout << "********** increaseBufferSize called **********" << endl;
	memcpy(newbuf, *buf, *buflen);
	free(*buf);

	*buf = newbuf;
	*buflen = newlen;

	return newlen;
}

void PerfmonAgent::clearData(char** array, int count)
{
	if (VERBOSE == TRUE)
		cout << "clearData called" << endl;
	for (int i =0; i<count; i++) {
		free(array[i]);
	}
	free(array);
}

void PerfmonAgent::setVariables(HashTable* hasht_variables)
{
	//variables hashtable initialisation
	tablePut(hasht_variables, DC_KEY_MONITORING, DC_VAL_FALSE);
	tablePut(hasht_variables, DC_AGENT_CONTROLLER_STOPPED, DC_VAL_FALSE); // ADDED... who was to set this?
	tablePut(hasht_variables, DC_KEY_FINISHED, DC_VAL_FALSE);
	tablePut(hasht_variables, DC_KEY_WAITTIME, (XINT64)1000);
	tablePut(hasht_variables, DC_KEY_TREE_REQUESTED, DC_VAL_FALSE);
}


int PerfmonAgent::manageResources(LPSTARTUP resStartup, LPSHUTDOWN resShutdown, LPGETTREE getTree, LPGETRESULTS getResults, PerfmonAgent* myPerfmonAgentInstance)
{
	char** treedata;
	char** results;

	XClock clock;
	XINT64 ctime;

	const int BATCH_XML_MESSAGES = TRUE;
	int ret, i;

	// Message Buffer Info (THIS WAS 20K; Let's see if 40K will do
	// [Scapa said they did have to reallocate occasionally]
	// for us with a 2K trigger (optimal for our data channel)
	// NOTE that currently our data channel has a 4K-12 byte limit!
	// Thus, the (relatively) low threshold.
#define MESSAGE_BUFSIZE 40960
#define MESSAGE_BUFFER_TRIGGER 2048
	int buf_ptr = 0;
	int buf_len = MESSAGE_BUFSIZE;
	char* buf;	//was 20k buffer... now will be MESSAGE_BUFSIZE

	// Time Buffer Info
	char timebuf[TIME_STRING_SIZE];	//print time into here
	XINT64 javatime;
	int total_dat = 0, numstrings=0, numresults=0;

	HashTable* hasht_request = tableCreate();
	perf_filters = tableCreate();
	perf_variables = tableCreate();
	perf_tree = tableCreate();


	setVariables(perf_variables);

	//
	// Startup the RES
	//
	resStartup( perf_variables, perf_filters, perf_tree);
	if (VERBOSE == TRUE)
		cout << "started up resutils" << endl;

	//
	// Set up the XML buffer
	//
	if (VERBOSE == TRUE)
		cout << "Allocating " << buf_len << " bytes of buffer space." << endl;
	buf = (char*)malloc(sizeof(char) * buf_len);

	// Everything is ready to go... time to start the receiver thread which will handle incoming requests to modify
	// the hashtables which determine the work we do in our sender thread.
	// **** NEEDED TO WAIT TIL ALL THE HASH TABLE STUFF WAS SET UP PRIOR TO ADMITTING OUR EXISTENCE!!
	// (We don't want incoming commands to be processed resulting in accessing the hash tables prior to their creation.)

	int registerReturn = myPerfmonAgentInstance->registerAgent();
	if (registerReturn != 0) {
		if (VERBOSE == TRUE)
			cout << "**** registerAgent FAILED!" << endl;
		return -1;
	}

	//
	// Create our clock
	//
    XClockCreate(&clock);
	XClockRead(&clock, &ctime);

	//
	//finished when an agent detaches from us
	// Bug 99153
	while (!tableGet(perf_variables, DC_KEY_FINISHED) && !tableGet(perf_variables, DC_AGENT_CONTROLLER_STOPPED))
	{
		XINT64 ctime_tmp = 0;
		XINT64 ctime_max = 0;

		XINT32 WAIT_TIME = (XINT32)tableGet(perf_variables, DC_KEY_WAITTIME);
		XINT32 TREE_REQUESTED = (XINT32)tableGet(perf_variables, DC_KEY_TREE_REQUESTED);

		//
		// Clean up the XML buffer
		//
		memset(buf, '\0', buf_len);
		buf_ptr = 0;

		ctime_max = ctime + WAIT_TIME;
		XClockRead(&clock, &ctime_tmp);
		
		while ((ctime_max - ctime_tmp > 2000) && !tableGet(perf_variables, DC_KEY_FINISHED))
		{
			ctime_tmp += 500;
			XClockWait(&clock, ctime_tmp);

			WAIT_TIME = (XINT32)tableGet(perf_variables, DC_KEY_WAITTIME);
			ctime_max = ctime + WAIT_TIME;
		}

		if (tableGet(perf_variables, DC_KEY_FINISHED))
		{
			break;
		}

		ctime += WAIT_TIME;

		//
		// Wait for a bit
		//
		XClockWait(&clock, ctime);
		XClockRead(&clock, &ctime);

		if (!tableGet(perf_variables, DC_KEY_MONITORING))
		{
			if (VERBOSE == TRUE)
				cout << "No agent monitoring yet (freq: " << (int) tableGet(perf_variables, DC_KEY_WAITTIME) << "ms)" << endl;
			tablePut(perf_variables, REGET_CHILDREN, (XINT64) 1);
			TREE_REQUESTED = TRUE;
			tablePut(perf_variables, DC_KEY_TREE_REQUESTED, TREE_REQUESTED);

			// We clear this so that we send back ALL the tree information again...
			tableClear(hasht_request);

			continue;
		}

		//
		// Get the current time in java System.currentTimeMillis() format
		//
		XClockMillis(&javatime);
		memset(timebuf, '\0', TIME_STRING_SIZE);
		longToString(javatime, timebuf);

		results = getResults(&numresults);

		if (VERBOSE == TRUE)
			cout << "NUMRESULTS = " << numresults << ", results = " << results << endl;

		//
		// go through each of the strings and create an XML
		//
		for (i = 0; results != NULL && i < numresults; i++)
		{
			char* dat[2];
			char* currdat = results[i];

			int keyindex = 1;
			int datindex = 0;

			//
			// check if buffer size is > MESSAGE_BUFSIZE here (was 10k!)  and send it back as
			// one message if it is - rather than sending a 4000k message
			//

			if (BATCH_XML_MESSAGES) {
				if (buf_ptr >= MESSAGE_BUFFER_TRIGGER)
				{
					ret = comSendData(destinationID, buf, buf_ptr);
					total_dat += buf_ptr;
					buf_ptr = 0;
				}

				if (PRINT_XML)
					cout << "XML = " << buf << endl;
			}

			//
			// Parse out all the strings for this counter
			//
			dat[0] = currdat;
			while (TRUE)
			{
				//filter out non-XML characters
				if (currdat[datindex] == '\"' || currdat[datindex] == '\'')
					currdat[datindex] = '_';
				if (currdat[datindex] < 0)
					currdat[datindex] = ' ';
				if (currdat[datindex] == '\0')
				{
					if (keyindex == 2)
						break;	//this means we check all the strings
					dat[keyindex++] = &currdat[datindex+1];
				}
				datindex++;
			}

			//dat[0] = id
			//dat[1] = value (string)

			if (VERBOSE == TRUE)
				cout << "--[" << dat[0] << ", " << dat[1] << "]--" << endl;

			while (TRUE)
			{
				ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<ContiguousObservation memberDescriptor=\"%s\" time=\"%s\" value=\"%s\" />\n", dat[0], timebuf, dat[1]);

				if (ret < 0)
					increaseBufferSize(&buf, &buf_len);
				else
				{
					buf_ptr += ret;
					break;
				}
			}

			if (!BATCH_XML_MESSAGES && (buf_ptr > 0))
			{
				ret = comSendData(destinationID, buf, buf_ptr);
				total_dat += buf_ptr;
				buf_ptr = 0;
			}

			if (PRINT_XML)
				cout << "XML = " << buf << endl;
		}

		//
		// Send any remaining buffer data
		//

		if (BATCH_XML_MESSAGES && (buf_ptr > 0))
		{
			ret = comSendData(destinationID, buf, buf_ptr);
			total_dat += buf_ptr;
			buf_ptr = 0;
		}

		if (PRINT_XML)
			cout << "XML = " << buf << endl;

		if (!TREE_REQUESTED)
			continue;

		TREE_REQUESTED = FALSE;
		tablePut(perf_variables, DC_KEY_TREE_REQUESTED, TREE_REQUESTED);

		//////////////////////////////////////////////
		// TREE BUILDING - descriptors				//
		//////////////////////////////////////////////
		//
		// Get the names of all the counters, treedata contains the names, numstrings the number...
		//

		treedata = getTree(&numstrings);

		//
		// go through each of the strings and create XML
		//
		for (i = 0; i < numstrings; i++)
		{
#define NUM_FIELDS 6
			char* dat[NUM_FIELDS];
			char* currdat = treedata[i];

			int keyindex = 1;
			int datindex = 0;

			//dat[0] = pid, dat[1] = id, dat[2] = name, dat[3] = help, dat[4] = suffix (not used)
			//check to see if this is the child of a requested parent
			if ((tableGet(perf_variables, REGET_CHILDREN) && currdat[0] == '\0') || (tableGet(perf_tree, currdat) == 1))
			{
				// check if buffer size is > 10k here and send it back as
				// one message if it is - rather than sending a 4000k message
				if (BATCH_XML_MESSAGES)
					if (buf_ptr >= MESSAGE_BUFFER_TRIGGER)
					{
						ret = comSendData(destinationID, buf, buf_ptr);
						total_dat += buf_ptr;
						buf_ptr = 0;
					}

				//
				// Parse out all the strings for this counter
				//
				dat[0] = currdat;

				while (TRUE)
				{
					//filter out non-XML characters
					if (currdat[datindex] == '\"' || currdat[datindex] == '\'')
						currdat[datindex] = '_';
					if (currdat[datindex] < 0)
						currdat[datindex] = ' ';
					if (currdat[datindex] == '\0')
					{
						if (keyindex == NUM_FIELDS)
							break;	//this means we check all the strings
						dat[keyindex++] = &currdat[datindex+1];
					}
					datindex++;
				}

				if (tableGet(hasht_request, dat[1]) == 0)
				{
					tablePut(hasht_request, dat[1], 1);

					if (VERBOSE == TRUE)
						cout << dat[0] << ", " << dat[1] << ", " << dat[2] << " (help), " << dat[4] << endl;

					while (TRUE)
					{
						if (dat[0][0] == '\0')
						{
							//no help = no description
							if (dat[3][0] == '\0')
							{
								//not sending back help strings drops data from 3.7M to 1.2M
								//new IDs should drop it down to around 800k with no help strings
								if (dat[5][0] == '0')
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<Descriptor id=\"%s\" name=\"%s\" />\n", dat[1], dat[2], dat[3]);
								else								
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<CounterDescriptor id=\"%s\" name=\"%s\" />\n", dat[1], dat[2], dat[3]);
							}
							else
							{
								if (dat[5][0] == '0')
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<Descriptor id=\"%s\" name=\"%s\" >\n<description>%s</description>\n</Descriptor>\n", dat[1], dat[2], dat[3]);
								else								
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<CounterDescriptor id=\"%s\" name=\"%s\" >\n<description>%s</description>\n</CounterDescriptor>\n", dat[1], dat[2], dat[3]);
							}

						}
						else
						{
							//no help = no description
							if (dat[3][0] == '\0')
							{
								//not sending back help strings drops data from 3.7M to 1.2M
								//new IDs should drop it down to around 800k with no help strings
								if (dat[5][0] == '0')
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<Descriptor parent=\"%s\" id=\"%s\" name=\"%s\" />\n", dat[0], dat[1], dat[2], dat[3]);
								else								
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<CounterDescriptor parent=\"%s\" id=\"%s\" name=\"%s\" />\n", dat[0], dat[1], dat[2], dat[3]);
							}
							else
							{
								if (dat[5][0] == '0')
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<Descriptor parent=\"%s\" id=\"%s\" name=\"%s\" >\n<description>%s</description>\n</Descriptor>\n", dat[0], dat[1], dat[2], dat[3]);
								else								
									ret = XSNPrintf( &buf[buf_ptr], buf_len-buf_ptr, "<CounterDescriptor parent=\"%s\" id=\"%s\" name=\"%s\" >\n<description>%s</description>\n</CounterDescriptor>\n", dat[0], dat[1], dat[2], dat[3]);
							}
						}

						if (ret < 0)
							increaseBufferSize(&buf, &buf_len);
						else
						{
							buf_ptr += ret;
							break;
						}

					}


					if (!BATCH_XML_MESSAGES && (buf_ptr > 0))
					{
						ret = comSendData(destinationID, buf, buf_ptr);
						total_dat += buf_ptr;
						buf_ptr = 0;
					}
					
					if (PRINT_XML)
						cout << "XML = " << buf << endl;
				}
			}
		}

		tablePut(perf_variables, REGET_CHILDREN, (XINT64) 0);
		//
		// print out how many messages we sent and the total number of bytes we sent
		//
		if (VERBOSE == TRUE)
			cout << numstrings << " descriptors, total bytes = " << total_dat/1000 << "k" << endl;

		//
		// deallocate the strings
		//
		clearData(treedata, numstrings);

		//
		// Send any remaining buffer data
		//
		if (BATCH_XML_MESSAGES && (buf_ptr > 0))
		{
			ret = comSendData(destinationID, buf, buf_ptr);
			total_dat += buf_ptr;
			buf_ptr = 0;
		}

		if (PRINT_XML)
			cout << "XML = " << buf;
	}

	//
	// We are finished - shutdown the RES and unload the library
	//
	resShutdown();
	if (VERBOSE == TRUE)
		cout << "resShutdown" << endl;

	//
	// Cleanup stuff we allocated
	//
	tableDelete(hasht_request);
	tableDelete(perf_filters);
	tableDelete(perf_variables);
	tableDelete(perf_tree);

	free(buf);
	return 0;
}

void PerfmonAgent::handle(char* type, char* name, char* value)
{
	char* tmp;
	int len = strlen(value);

	//
	// This is how the filtering will work
	//
	if (VERBOSE == TRUE)
		cout << "Received a AC_SET_NAME_VALUE_PAIR event, type " << type << ", name " << name << ", value " << value << endl;

	if (isEqualString(type, "filter")) {
		//this is a filter request

		if (isEqualString(value, "true")) {
			tablePut(perf_filters, name, (XINT64)1);
		} else {
			tablePut(perf_filters, name, (XINT64)NULL);
		}

		if (VERBOSE == TRUE)
			cout << "FILTER REQUEST " << name << ", " << value << " (" << perf_filters->count << " filters in place)" << endl;
	} else if (isEqualString(type, "updatetree")) {
		if (VERBOSE == TRUE)
			cout << "Requested children of tree node " << name << endl;

		tablePut(perf_variables, DC_KEY_TREE_REQUESTED, TRUE);
		if (name == NULL)
			tablePut(perf_tree, "", 1);
		else
			tablePut(perf_tree, name, 1);
	} else if (isEqualString(type, "var_int")) {
		tablePut(perf_variables, name, atoi(value));
	} else if (isEqualString(type, "var_boolean")) {
		if (isEqualString(value, "true")) {
			tablePut(perf_variables, name, DC_VAL_TRUE);
		} else {
			tablePut(perf_variables, name, DC_VAL_FALSE);
		}
	} else if (isEqualString(type, "var_string")) {
		tmp = (char*)tableGet(perf_variables, name);
		if (tmp != NULL) free(tmp);

		//convert string NULL to actual NULL
		if (isEqualString(value, "NULL") || isEqualString(value, "null")) {
			if (VERBOSE == TRUE)
				cout << "FOUND NULL STRING " << value << endl;
			tmp = NULL;
		} else {
			if (VERBOSE == TRUE)
				cout << "FOUND NON-NULL STRING " << value << endl;
			tmp = (char*)malloc(sizeof(char) * (len + 2));
			memcpy(tmp, value, len);
			tmp[len] = '\0';
		}

		tablePut(perf_variables, name, (XINT64)tmp);
	} else {
		if (VERBOSE == TRUE)
			cout << "ERROR - UNRECOGNISED SETENV TYPE " << type << endl;
	}
}

void PerfmonAgent::showInfo()
{
	cout << "PerfmonAgent" << endl << endl;
	cout << "Usage:   PerfmonAgent.exe [-v] [Resource DLL] [Communications DLL]" << endl;
	cout << "Default: PerfmonAgent.exe sysperf.dll rac.dll" << endl;
	cout << "\n-v\t: Enable verbose output" << endl;
}

tptp_int32 PerfmonAgent::sendRunResponse(CmdBlock* cmdBlock)
{
	int ret = -1;

	int destID = cmdBlock->getDestID();
	int sourceID = cmdBlock->getSourceID();
	int contextID = cmdBlock->getContextID();

	char   commandFormat[] = "<Cmd src=\"%ld\" dest=\"%ld\" ctxt=\"%ld\"> <agentRunning iid=\"%s\"></agentRunning></Cmd>";
	char   command[1024];
	sprintf( command, commandFormat, destID, sourceID, contextID, "org.eclipse.tptp.PerfmonAgent");

	if (VERBOSE == TRUE)
		cout << "Sending '" << command << "'!" << endl;

	ret = BaseAgentImpl::sendCommand(command);

	return ret;
}

tptp_int32 PerfmonAgent::sendStopResponse(CmdBlock* cmdBlock)
{
	int ret = -1;

	int destID = cmdBlock->getDestID();
	int sourceID = cmdBlock->getSourceID();
	int contextID = cmdBlock->getContextID();

	char   commandFormat[] = "<Cmd src=\"%ld\" dest=\"%ld\" ctxt=\"%ld\"> <agentStopped iid=\"%s\"></agentStopped></Cmd>";
	char   command[1024];
	sprintf( command, commandFormat, destID, sourceID, contextID, "org.eclipse.tptp.PerfmonAgent");

	if (VERBOSE == TRUE)
		cout << "Sending '" << command << "'!" << endl;

	ret = BaseAgentImpl::sendCommand(command);

	return ret;
}

tptp_int32 PerfmonAgent::comSendData(int destinationID, char buffer[], int bufferLength) {
	if (VERBOSE == TRUE)
		cout << "Sending " << bufferLength << " bytes to destinationID " << destinationID << endl;
	return sendData(destinationID, buffer, bufferLength);
}



tptp_int32 PerfmonAgent::run(CmdBlock* cmdBlock)
{
	destinationID = cmdBlock->getSourceID();
	tablePut(perf_variables, DC_KEY_MONITORING, (XINT64)DC_VAL_TRUE);
	sendRunResponse(cmdBlock);
	return 0;
}

tptp_int32 PerfmonAgent::stop(CmdBlock* cmdBlock)
{
	tablePut(perf_variables, DC_KEY_MONITORING, (XINT64)DC_VAL_FALSE);
	sendStopResponse(cmdBlock);
	return 0;
}

tptp_int32 PerfmonAgent::processCommand(CmdBlock* cmdBlock)
{
	tptp_int32 ret = 0;

	ret = BaseCollectorImpl::processCommand(cmdBlock);

	if (ret == 0) return ret;

	char* cmdName = cmdBlock->getCommandName();

	if (VERBOSE == TRUE)
		cout << "processCommand: processing cmd: " << cmdName << " sent to " << destinationID << endl;

	if (!isEqualString(cmdBlock->getIID(), "org.eclipse.tptp.PerfmonAgent"))
	{
		if (VERBOSE == TRUE)
			cout << "PerfmonAgent received cmdBlock for another agent!" << endl;
		return ERROR;
	}

	if (isEqualString(cmdName, "setNameValuePair"))
	{
		tptp_list_t* paramList = cmdBlock->getParamList();
		char* type = NULL;
		char* name = NULL;
		char* value = NULL;

		if (getStringParam("Type", paramList, &type) < 0) {
			if (VERBOSE == TRUE)
				cout << "No type?!?" << endl;
			return ERROR;
		}
		if (getStringParam("Name", paramList, &name) < 0) {
			if (VERBOSE == TRUE)
				cout << "No type?!?" << endl;
			return ERROR;
		}
		if (getStringParam("Value", paramList, &value) < 0) {
			if (VERBOSE == TRUE)
				cout << "No type?!?" << endl;
			return ERROR;
		}
		handle(type, name, value);
		free(type);
		free(name);
		free(value);
	} else {
		if (VERBOSE == TRUE)
			cout << "NOT a setNameValuePair command! A new command for processCommand to handle??" << endl;
		return ERROR;
	}


	//	case AC_AGENT_CONTROLLER_UNAVAILABLE:
		    // Bug 99153
	//		if (available == 1)
	//			tablePut(perf_variables, DC_AGENT_CONTROLLER_STOPPED, (XINT64)DC_VAL_TRUE);
		    // End Bug 99153

	return ret;
}

tptp_int32 PerfmonAgent::receiveData(tptp_int32 sourceID, char buffer[], tptp_int32 bytesRead, DIME_HEADER_PTR_T dimeHeader)
{
	char* incoming;
	tptp_int32 dime_length;

	if (!is_valid_header(dimeHeader, bytesRead+dimeHeader->id_length+sizeof(DIME_HEADER_T))) {
		return 0;
	}
	incoming = GET_DIME_ID(dimeHeader);
	dime_length = GET_DIME_LENGTH(dimeHeader);

	if (VERBOSE == TRUE)
		cout << "For some reason Perfmon is RECEIVING data for " << incoming << " of size " << dime_length << endl;

	return 0;
}

//
//	Load the DLL functions for the resource gatherer.
//
int PerfmonAgent::process(char* resDLLname, PerfmonAgent* myPerfmonAgentInstance)
{	
	LPSTARTUP resStartup;
	LPSHUTDOWN resShutdown;
	LPGETTREE getTree;
	LPGETRESULTS getResults;
	LPGETAGENTNAME getAgentName = NULL;
	int rt;

#ifdef LINUX
  const char *error;
  void *resmodule;

	// First lets try and open the library
// cout << "trying to open resource library " << resDLLname << endl;
	resmodule = dlopen(resDLLname, RTLD_LAZY);
	if (!resmodule)
	{
		cerr << "Couldn't open reslibrary: " << dlerror() << endl;
		exit(1);
	}

	//Okay, we must now try and get all the methods we need!

	dlerror();
	resStartup = (LPSTARTUP) dlsym(resmodule, "DC_CollectionStartup");
	if ((error = dlerror()))
	{
		dlclose(resmodule);

		cerr << "Couldn't find DC_CollectionStartup: " << error << endl;
		exit(1);
	}
	dlerror();
	resShutdown = (LPSHUTDOWN) dlsym(resmodule, "DC_CollectionShutdown");
	if ((error = dlerror()))
	{
		dlclose(resmodule);

		cerr << "Couldn't find DC_CollectionShutdown: " << error << endl;
		exit(1);
	}
	dlerror();
	getTree = (LPGETTREE) dlsym(resmodule, "DC_CollectionGetTree");
	if ((error = dlerror()))
	{
		dlclose(resmodule);
		
		cerr << "Couldn't find DC_CollectionGetTree: " << error << endl;
		exit(1);
	}
	dlerror();
	getResults = (LPGETRESULTS) dlsym(resmodule, "DC_CollectionGetResults");
	if ((error = dlerror()))
	{
		dlclose(resmodule);
		
		cerr << "Couldn't find DC_CollectionGetResults: " << error << endl;
		exit(1);
	}
	dlerror();
	getAgentName = (LPGETAGENTNAME) dlsym(resmodule, "DC_CollectionGetAgentName");
	if ((error = dlerror()))
	{
		dlclose(resmodule);
		
		cerr << "Couldn't find DC_CollectionGetAgentName: " << error << endl;
		exit(1);
	}

	// Perform processing
	rt = manageResources(resStartup, resShutdown, getTree, getResults, myPerfmonAgentInstance);

 /* All done, close things cleanly */
	dlclose(resmodule);
	return 0;

#else
				
	HINSTANCE hResDLL;

	// LOAD THE RESOURCE DLL FUNCTIONS
	hResDLL = LoadLibrary(resDLLname);
	if (hResDLL == NULL)
	{
		if (VERBOSE == TRUE)
			cout << "Error encountered: " << resDLLname << " is not a valid DLL" << endl;
			
		MessageBox(NULL, "Error Loading resourceDLL", "Error Loading resourceDLL", MB_OK);
		return -1;
	}

	resStartup	= (LPSTARTUP)	GetProcAddress(hResDLL, "DC_CollectionStartup");
	resShutdown	= (LPSHUTDOWN)	GetProcAddress(hResDLL, "DC_CollectionShutdown");
	getTree		= (LPGETTREE)	GetProcAddress(hResDLL, "DC_CollectionGetTree");
	getResults	= (LPGETRESULTS)GetProcAddress(hResDLL, "DC_CollectionGetResults");
	getAgentName = (LPGETAGENTNAME) GetProcAddress(hResDLL, "DC_CollectionGetAgentName");

	if (!resStartup)
		cout << "resStartup" << endl;
	if (!resShutdown)
		cout << "resShutdown" << endl;
	if (!getTree)
		cout << "getTree" << endl;
	if (!getResults)
		cout << "getResults" << endl;
	if (!getAgentName)
		cout << "getAgentName" << endl;

	if (!resStartup || !resShutdown || !getTree || !getResults || !getAgentName)
	{
		// handle the error
		FreeLibrary(hResDLL);
		cout << "Unable to load function(s) from Resource DLL." << endl;
		MessageBox(NULL, "Error Loading Functions (RESOURCEDLL)", "Error Loading Functions (RESOURCEDLL)", MB_OK);
	    return -1;
	}

	rt = manageResources(resStartup, resShutdown, getTree, getResults, myPerfmonAgentInstance);

	FreeLibrary(hResDLL);
	return rt;
#endif
}

//
//	Main Agent Method : Parse the command line arguments (potentially setting DLL name)
//
int main(int argc, char** argv)
{
	char* agentName = DEFAULT_PERFMON_AGENT_NAME;

	PerfmonAgent* PerfmonAgentI
		= new PerfmonAgent(agentName);

	PerfmonAgentI->processCommandLine(argc, argv);

	return PerfmonAgentI->process(DEFAULT_RES, PerfmonAgentI);
}
