/**********************************************************************
 * Copyright (c) 2005, 2007 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: rac.c,v 1.2 2007/04/06 01:08:27 jkubasta Exp $
 * 
 * Contributors: 
 * Scapa Technologies Limited - Initial API and implementation
 **********************************************************************/

#ifdef WIN32
	#include <winsock2.h> /* Bug 134924 */
#endif
#include "rac.h"
#include "system.h"
#include "hashtable.h"

int available = 0;
int monitoring = 0;

unsigned char* msg_block;
int msg_block_len;

XLibrary rac_hcbnd;
XLibrary rac_hccldt;
XLibrary rac_hccls;
XLibrary rac_hcclsm;
XLibrary rac_hcjbnd;
XLibrary rac_hcclco;

//ra_commandHandler_t command_handler;
ra_data_target_hdl_t targetHdl;

RA_AGENT_HANDLE agent_handle;
TID* agent_tid;

PerformanceElement_t* counters;
int counters_len = -1;  //-1 = uninitialised

static memoryleak = 0;
static int first = 0;

char* agent_type;

int VERBOSE = FALSE;
int LOG_TO_FILE = FALSE;

////////////////////////////
// PERFMON SPECIFIC STUFF
////////////////////////////
HashTable* perf_filters;
HashTable* perf_variables;
HashTable* perf_tree;
////////////////////////////
// END PERFMON SPECIFIC STUFF
////////////////////////////

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'
};

static FILE* f = NULL;

static char filename[] = "RACDll.log";

void logToFile(const char*, ...);

void logToFile(const char* temp, ...)
{
	if (!VERBOSE)
		return;
	if (LOG_TO_FILE)
	{
		va_list ap;
		if (f == NULL)
			f = fopen(filename,  "w");
		va_start (ap, temp);
		vfprintf (f, temp, ap);
//		printf(temp,ap);
		va_end (ap);
		fflush(f);
	}
}

void longToString(XINT64 i, char* str) {
	int radix = 10;
	char buf[65];

	int charPos = 64;
	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]),(65 - charPos));
}

int StrEq(char* a, char* b) {
	int index = 0;
	char ch_a, ch_b;

	if (a == NULL) {
		return -1;
	} else if (b == NULL) {
		return -1;
	}

	while (0==0) {
		ch_a = a[index];
		ch_b = b[index];

		if (ch_a != ch_b) return index+1;

		//could get rid of the OR here?
		if (ch_a == '\0' || ch_b == '\0') {
			return 0;
		}

		index++;
	}
}

void handle(char* type, char* name, char* value)
{
	char* tmp;
	int len = strlen(value);
	//
	// This is how the filtering will work
	//
	logToFile("Received a RA_SET_NAME_VALUE_PAIR event, type %s, name %s, value %s\n", type, name, value);


	if (StrEq(type,"filter") == 0) {
		//this is a filter request

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

		logToFile("FILTER REQUEST %s, %s (%d filters in place)\n",name,value,perf_filters->count);

	} else if (StrEq(type,"updatetree") == 0) {

		logToFile("Requested children of tree node %s\n", name);

		tablePut(perf_variables,DC_KEY_TREE_REQUESTED,TRUE);
		if (name == NULL)
			tablePut(perf_tree,"",1);
		else
			tablePut(perf_tree,name,1);

	} else if (StrEq(type,"var_int") == 0) {

		tablePut(perf_variables,name,atoi(value));

	} else if (StrEq(type,"var_boolean") == 0) {

		if (StrEq(value,"true") == 0) {
			tablePut(perf_variables,name,DC_VAL_TRUE);
		} else {
			tablePut(perf_variables,name,DC_VAL_FALSE);
		}

	} 
	else if (StrEq(type,"var_string") == 0) 
	{
		tmp = (char*)tableGet(perf_variables,name);
		if (tmp != NULL) free(tmp);

		//convert string NULL to actual NULL
		if (StrEq(value,"NULL") == 0 || StrEq(value,"null") == 0) {
			logToFile("FOUND NULL STRING %s\n",value);
			tmp = NULL;
		} else {
			logToFile("FOUND NON-NULL STRING %s\n",value);
			tmp = (char*)malloc(sizeof(char) * (len + 2));
			memcpy(tmp,value,len);
			tmp[len] = '\0';
		}

		tablePut(perf_variables,name,(XINT64)tmp);

	} else {

		logToFile("ERROR - UNRECOGNISED SETENV TYPE %s\n",type);

	}
}

void RACCommandHandler(ra_command_t *command) {
	int ret = 0;

	logToFile("RAC Command %08X\n",command->tag);

	switch (command->tag) {
	case RA_START_MONITORING_AGENT_LOCAL:
	case RA_START_MONITORING_AGENT_REMOTE:
		{
			monitoring = 1;

			targetHdl.dtarget = RA_SHAREDMEMORY;

			if (first == 0)
			{
				ret = (*ra_attachToShm_f)(command->info.start_monitor_local.file.data, &targetHdl.dtargetHdl.shmHdl);
				first = 1;
			}

			tablePut(perf_variables,DC_KEY_MONITORING,(XINT64)DC_VAL_TRUE);

			logToFile("RAC Agent Started Monitoring (%d)\n",ret);

			break;
		}
	case RA_STOP_MONITORING_AGENT:
		{
			monitoring = 0;

			tablePut(perf_variables,DC_KEY_MONITORING,(XINT64)DC_VAL_FALSE);

			logToFile("RAC Agent Stopped Monitoring\n");
			break;
		}
	case RA_ATTACH_TO_AGENT:
		{
			logToFile("RAC attach agent\n");

			// RA_ATTATCH_TO_AGENT never seems to get called. Therefore, I have changed start and stop monitoring to
			// reflect this befaviour.
/*
			targetHdl.dtarget = RA_SHAREDMEMORY;

			if (sharingmemory == 0)
			{
				ret = (*ra_attachToShm_f)(command->info.start_monitor_local.file.data, &targetHdl.dtargetHdl.shmHdl);
				sharingmemory = 1;
			}

			logToFile("RAC attach agent\n");
*/
			break;

		}
	case RA_DETACH_FROM_AGENT:
		{
//			tablePut(perf_variables,DC_KEY_FINISHED,(XINT64)DC_VAL_FALSE);

            if(targetHdl.dtarget == RA_SHAREDMEMORY) 
               ret = (*ra_stopFlushingShm_f)(&targetHdl.dtargetHdl.shmHdl);
			first = 0;
// Bug 131140
			tableClear(perf_tree);
			tableClear(perf_filters);


			logToFile("RAC detach agent (%d)\n",ret);
			break;
		}
	case RA_AGENT_CONTROLER_AVAILABLE:
		{
			available = 1;
			logToFile("RAC Agent Controller available\n");
			break;
		}
	case RA_AGENT_CONTROLER_UNAVAILABLE:
		{
		    // Bug 99153
			if (available == 1)
				tablePut(perf_variables,DC_AGENT_CONTROLLER_STOPPED,(XINT64)DC_VAL_TRUE);
		    // End Bug 99153
			available = 0;
			logToFile("RAC Agent Controller unavailable\n");
			break;
		}
    case RA_SET_NAME_VALUE_PAIR:
		{
			char* type = command->info.set_nv_pair.type.data;
			char* name = command->info.set_nv_pair.name.data;
			char* value = command->info.set_nv_pair.value.data;

			handle(type,name,value);
			break;
		}
	case RA_CUSTOM_COMMAND:
		{
			char valid = 1;
			int i;
			char ch;

			char* type;
			char* name; 
			char* value;

			type = command->info.custom_command.message.data;			
			if (StrEq(type,"RESUME") == 0 || StrEq(type,"APPLYFILTERS") == 0 || StrEq(type,"TRACE_OBJECTS") == 0 || (StrEq(type,"ENABLE") == 0))
				return;

			for (i = 0; TRUE; i++) 
			{
				ch = type[i];
				if (ch == 30) 
				{
					type[i] = '\0';
					name = &type[i+1];
					break;
				} 
				else if (ch == '\0')
				{
					valid = 0;
					break;
				}
			}

			for (i = 0; TRUE; i++) 
			{
				if (valid == 0)
					break;
				ch = name[i];
				if (ch == 30) 
				{
					name[i] = '\0';
					value = &name[i+1];
					break;
				} 
			}

			if (valid == 0)
			{
				logToFile("Received a invalid RA_CUSTOM_COMMAND, data=%s\n",command->info.custom_command.message.data);			
			}
			else
			{
				logToFile("Received a RA_CUSTOM_COMMAND event, type = %s, name=%s, value=%s\n",type,name,value);				
				handle(type,name,value);
			}
			break;
		}
	default:
			logToFile("Unhandled RAC Command %08X\n",command->tag);
	}
}

int RACLoadRacLibs() {
	int ret = 0;
	if ((ret = XLibraryCreate(&rac_hcbnd,"hcbnd")) != 0) {
		logToFile("Error loading RAC Bindings library\n");
		return ret;
	}
	if ((ret = XLibraryCreate(&rac_hccldt,"hccldt")) != 0) {
		logToFile("Error loading RAC Data Transfer library\n");
		return ret;
	}
	if ((ret = XLibraryCreate(&rac_hccls,"hccls")) != 0) {
		logToFile("Error loading RAC Socket library\n");
		return ret;
	}
	if ((ret = XLibraryCreate(&rac_hcjbnd,"hcjbnd")) != 0) {
		logToFile("Error loading RAC HCJ bindings library\n");
		return ret;
	}
	if ((ret = XLibraryCreate(&rac_hcclsm,"hcclsm")) != 0) {
		logToFile("Error loading RAC shared memory library\n");
		return ret;
	}
	if ((ret = XLibraryCreate(&rac_hcclco,"hcclco")) != 0) {
		logToFile("Error loading RAC Common library\n");
		return ret;
	}
	return 0;
}

int RACGetRacFuncs() {
	int ret = 0;

	if ((ret = XLibrarySymbol(&rac_hcbnd,"ra_initializeBindings",(void**)&ra_initializeBindings_f)) != 0) {
		logToFile("Error getting ra_initializeBindings symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hcbnd,"ra_startListener",(void**)&ra_startListener_f)) != 0) {
		logToFile("Error getting ra_startListener symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hcbnd,"ra_stopListener",(void**)&ra_stopListener_f)) != 0) {
		logToFile("Error getting ra_stopListener symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hcbnd,"ra_finalizeBindings",(void**)&ra_finalizeBindings_f)) != 0) {
		logToFile("Error getting ra_finalizeBindings symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hcbnd,"ra_sendMessage",(void**)&ra_sendMessage_f)) != 0) {
		logToFile("Error getting ra_sendMessage symbol\n");
		return ret;
	}

	if ((ret = XLibrarySymbol(&rac_hccldt,"ra_allocateMessageBlock",(void**)&ra_allocateMessageBlock_f)) != 0) {
		logToFile("Error getting ra_allocateMessageBlock symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hccldt,"ra_freeMessageBlock",(void**)&ra_freeMessageBlock_f)) != 0) {
		logToFile("Error getting ra_freeMessageBlock symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hccldt,"ra_writeMessageBlock",(void**)&ra_writeMessageBlock_f)) != 0) {
		logToFile("Error getting ra_writeMessageBlock symbol\n");
		return ret;
	}

	if ((ret = XLibrarySymbol(&rac_hcclsm,"ra_attachToShm",(void**)&ra_attachToShm_f)) != 0) {
		logToFile("Error getting ra_attachToShm symbol\n");
		return ret;
	}
	if ((ret = XLibrarySymbol(&rac_hcclsm,"ra_stopFlushingShm",(void**)&ra_stopFlushingShm_f)) != 0) {
		logToFile("Error getting ra_stopFlushingShm symbol\n");
		return ret;
	}
	
	if ((ret = XLibrarySymbol(&rac_hcclco,"ra_setLastError",(void**)&ra_setLastError_f)) != 0) {
		logToFile("Error getting ra_setLastError symbol\n");
		return ret;
	}
	return 0;
}

int RACInitBindings() 
{
	XINT64 clock;

	char agent_name[256];
	char agent_time[256];
	
	XClockMillis(&clock);

	memset(agent_name,'\0',256);
	memset(agent_time,'\0',256);

	longToString(clock,agent_time);
//	XSNPrintf(agent_name,255,"%s - %s",agent_type,agent_time);
	XSNPrintf(agent_name,255,"%s",agent_type);
	
	logToFile("Connecting to RAC - %s\n",agent_name);

	agent_handle =  (*ra_initializeBindings_f)(agent_name,"Statistical",RACCommandHandler,FALSE);
	if (agent_handle == NULL) {

		logToFile("Error initializing RAC bindings\n");
		return -1;
	}

	return 0;
}

int RACFiniBindings() {
	(*ra_finalizeBindings_f)(agent_handle);
	return 0;
}


int RACStartListener() {
	agent_tid = (*ra_startListener_f)(agent_handle,0);
	return 0;
}

int RACStopListener() {
	(*ra_stopListener_f)(agent_handle);
	return 0;
}

int RACAllocateMessageBlock(int siz) {
	msg_block = (*ra_allocateMessageBlock_f)(siz);
	msg_block_len = siz;
	if (msg_block == NULL) {
		msg_block_len = 0;
		return -1;
	}
	return 0;
}

int RACReallocateMessageBlock(int siz) {
	int ret = 0;
	(*ra_freeMessageBlock_f)(msg_block);
	if ((ret = RACAllocateMessageBlock(siz)) != 0) {
		msg_block_len = 0;
		return -1;
	}
	msg_block_len = siz;
	return 0;
}

int RACFreeMessageBlock() {
	(*ra_freeMessageBlock_f)(msg_block);
	return 0;
}

int DC_TransferSetAgentName(char* name)
{
	logToFile("Setting the agent name is the transfer DLL to %s\n",name);
	agent_type = name;
	return 0;
}
	
int DC_TransferStartup(HashTable* variables,
					   HashTable* filters,
					   HashTable* tree
					   ) {
	int ret = 0;

	perf_filters = filters;
	perf_variables = variables;
	perf_tree = tree;

	logToFile("Loading RAC libraries\n");
	if ((ret = RACLoadRacLibs()) != 0) {
		return ret;
	}
	logToFile("Getting RAC functions\n");
	if ((ret = RACGetRacFuncs()) != 0) {
		return ret;
	}
	logToFile("Initializing RAC bindings\n");
	if ((ret = RACInitBindings()) != 0) {
		return ret;
	}
	logToFile("Starting RAC listener\n");
	if ((ret = RACStartListener()) != 0) {
		return ret;
	}
	logToFile("Allocating message block\n");
	if ((ret = RACAllocateMessageBlock(1024)) != 0) {
		return ret;
	}

	logToFile("RAC Startup succesful\n");
	return 0;
}

int RACIsMonitoring() 
{
	return monitoring == 1;
}

int DC_SendMessage(char* msg, int off, int len) 
{
	//check the length and reallocate the message block if necessary
	if (len > msg_block_len-1) 
	{
		RACReallocateMessageBlock(len+1);
		len = msg_block_len-1;
	} 

	if (len == 0)
		return 0;

	//copy the message data over to the message block
	memset(msg_block,'\0',msg_block_len);
	memcpy(msg_block,&(msg[off]),len);
	msg_block[len] = '\0';

	//check if the agent is listening etc
	if (available != 1) 
	{
		return -1;
	}

	if (monitoring != 1) 
	{
		return -1;
	}

	logToFile("Sending message back...\n");

	//then just use ra_sendMessage
	(*ra_writeMessageBlock_f)(&targetHdl,RA_UTF8_STRING_DATA,msg_block,len);

	return 0;
}

int DC_TransferShutdown() {
	int ret = 0;
	logToFile("Stopping RAC Listener\n");
	if ((ret = RACStopListener()) != 0) {
		return ret;
	}
	logToFile("Finalising RAC bindings\n");
	if ((ret = RACFiniBindings()) != 0) {
		return ret;
	}
	return 0;
}

