/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation.
 * 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
 *
 * Contributors:
 *    Vishnu K Naikawadi,Intel - Initial API and implementation
 *
 * $Id: BaseAgentImpl.cpp,v 1.80 2009/08/26 15:00:00 jwest Exp $ 
 *******************************************************************************/ 

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#ifndef _WIN32
#include <strings.h>
#endif

#include "tptp/agents/BaseAgentImpl.h"
#include "tptp/NamedPipeTL.h"
#include "tptp/TPTPSupportUtils.h"
#include "tptp/TPTPOSCalls.h"
#include "tptp/TPTPCommon.h"
#include "tptp/agents/AgentLog.h"
#include "tptp/TPTPConfigBinding.h"
#include "tptp/MsgPipeline.h"
#include "tptp/TPTPUtils.h"
#include "tptp/ACFlags.h"
#include "tptp/TPTPLogUtils.h"

/* The default name of the master named pipe */
#define PIPE_DEFAULT_ADDRESS "acmaster"

#define BUFFER_LENGTH 8192
/* Shared memory specifics */
#define TPTP_SHM_BUF_NAME_ROOT  "acbuffer"
#define CONNECT_TO_AC_RETRY_WAIT_TIME 1000

using namespace std ;

class CmdBlock;

BaseAgentImpl::BaseAgentImpl(char* name)
{
	if (name)
	{
		int len = strlen(name) + 1;
		agentName = (char *) tptp_malloc(len);
		memcpy(agentName, name, len);
	}


	agentConnected = false;
	agentRegistered = false;
	agentID = -1;
	agentPipeName = NULL;
	agentNamedPipeHandle = TPTP_HANDLE_NULL;
	contextValue = 0;
	msgHandlerThreadHandle = TPTP_HANDLE_NULL;
	#ifdef MVS
		memset(&msgHandlerThreadHandle, 0, sizeof(TID));
	#else
		msgHandlerThreadID = 0;
	#endif
	configFile = NULL;
	_serviceConfigFile = NULL;
	_raShmBufNameRoot = TPTP_SHM_BUF_NAME_ROOT;
	_raMasterAddress = PIPE_DEFAULT_ADDRESS;
	//TODO: Initialize? agentConfig
	tptp_initializeSemaphore( &terminateSemaphore );
	tptp_initializeLock( &cmdPipeLock );
	tptp_initializeLock( &logPipeLock );

	tptp_list_init( &controllerList );
	tptp_list_init( &observerList );
	tptp_initializeLock( &clientListsLock );

	// Until we have a chance to read otherwise, only log critical messages
	loggingFormat = TPTP_LOGFORMAT_CBE;
	loggingLevel  = TPTP_CRITICAL;
	initializeXMLPlatformUtils();
}

BaseAgentImpl::~BaseAgentImpl()
{
	tptp_free(agentName);

	//TODO: Who's freeing agentPipeName and closing that pipe handle?
	if (msgHandlerThreadHandle) CLOSE_THREAD_HANDLE(msgHandlerThreadHandle);

	tptp_deleteLock( &cmdPipeLock );
	tptp_deleteLock( &logPipeLock );

	tptp_list_clear( &controllerList );
	tptp_list_clear( &observerList );
	tptp_deleteLock( &clientListsLock );

	//TPTP_LOG_DEBUG_MSG(this, "BaseAgentImpl destructor called");
	terminateXMLPlatformUtils();
}

void BaseAgentImpl::waitForTermination()
{
	tptp_waitSemaphore( &terminateSemaphore );
	tptp_deleteSemaphore( &terminateSemaphore );
}

int BaseAgentImpl::getNextContext()
{
	return contextValue++;
}


int BaseAgentImpl::initializeAgentConfiguration(char *configDir)
{
	tptp_int32 rc;
	AC_Config_t configData;
	tptp_node_t* node;

	configData.agentConfigLocation =0;
	configData.applicationAliases = 0;
	configData.envConfig = 0;
	configData.loggingDirectory = 0;
	configData.procCntlrAgent = 0;
	configData.version = 0;	
	
	if (configDir != NULL) {
		rc = loadTPTPConfig(configDir, &configData, FALSE);
		if ( ! rc )	{
			for ( node = configData.msgPipelineList.head; node != NULL; node = node->next )
			{
				message_pipeline_config_t* mpc = (message_pipeline_config_t*)node->data;
				char *transportBlock = mpc->transportLayerConfig;
				char *transportLayerLib = mpc->transportLayerLib;
				char *pipeName;
				char *sharedMemName;
				SocketConfigInfo socketInfo;

				//this may change if we add a name tag in config, but for
				//now we should select off the TL name.
				if (strcmp(transportLayerLib, "socketTL") == 0) {
					rc = getSocketConfigInfo(transportBlock, &socketInfo);
				} else if (strcmp(transportLayerLib, "namedPipeTL") == 0) {
					rc = getNamedPipeConfigInfo(transportBlock, &pipeName);
					_raMasterAddress = (char *) tptp_malloc(
						strlen(pipeName)+1);
					strcpy(_raMasterAddress, pipeName);
				} else if (strcmp(transportLayerLib, "sharedMemTL") == 0) {
					rc = getSharedMemConfigInfo(transportBlock, &sharedMemName);
					_raShmBufNameRoot = (char *) tptp_malloc(
						strlen(sharedMemName)+1);
					strcpy(_raShmBufNameRoot, sharedMemName);
				} else {
					TPTP_LOG_ERROR_MSG(this, "Unknown TL configuration ");
				}
			}

			loggingFormat = configData.loggingFormat;
			loggingLevel  = configData.loggingLevel;
			return (0);
		}else{
			TPTP_LOG_ERROR_MSG1(this, "Fail to read config file %s.\n", configDir);
		}

	}else{
		TPTP_LOG_ERROR_MSG1(this, "Config file %s is NULL.\n", configDir);
	}
	char *sr;
#ifdef _WIN32
		sr = "\\";
#else
		sr = "/";
#endif
	char *acHome = getenv("TPTP_AC_HOME");
	if (acHome != NULL) {
		configData.agentConfigLocation =  (char*)tptp_malloc(strlen(acHome)+ strlen(sr) + 10);
		sprintf(configData.agentConfigLocation, "%s%s%s", acHome, sr, "agents");
	
		configData.envConfig = "default";

#ifdef _WIN32
		configData.procCntlrAgent = (char*)tptp_malloc(strlen(acHome)+ 50);
		sprintf(configData.procCntlrAgent, "%s%s", acHome, "\\bin\\tptpProcessController.exe");	
#else
		configData.procCntlrAgent = (char*)tptp_malloc(strlen(acHome)+ 50);
		sprintf(configData.procCntlrAgent, "%s%s", acHome, "/bin/tptpProcessController");
#endif	
		loggingFormat = TPTP_LOGFORMAT_CBE;
		loggingLevel  = TPTP_INFORMATION;
		TPTP_LOG_DEBUG_MSG(this, "BaseAgent initializeAgentConfiguration: Init variables from TPTP_AC_HOME.");
		return 0;
	}else{

		// Agent doesn't have access to config file or user doesn't set up environment variable.
		// default value of pipe name and log would be used.
		loggingFormat = TPTP_LOGFORMAT_CBE;
		loggingLevel  = TPTP_INFORMATION;

		TPTP_LOG_DEBUG_MSG(this, "BaseAgent initializeAgentConfiguration: Debug - TPTP_AC_HOME is not set.  Using default settings.");
		return (-1); // Without a config file we cannot config.
	}

}

void BaseAgentImpl::setServiceConfigFile(int argc, char* argv[])
{
	int i;
	char *name;

	if (argc > 0) {
		name = argv[0];
	} else {
		name = NULL;
	}

	// set the service config file
	// first check if agent was launched with a qualifier specifying config file.
	// Next check for an env variable.
	// Finally use the relative path to the config directory.
	//
	if (argc > 2) {
		for (i=1; i< argc; i++)
		{
			if (strcmp(argv[i], "-serviceconfig") == 0) {
				if (i+1 < argc)
				{
					_serviceConfigFile = (char *) tptp_malloc(strlen(argv[i+1])) +1;
					strcpy(_serviceConfigFile, argv[i+1]);
					return;
				} 
				else 
				{ 
					//user did not specify serviceconfig
					//They can still specify via TPTP_AC_HOME or by relative path.
					break;
				}
			}
		}
	}
	char *configFilePath = getenv("TPTP_AC_CONFIG_HOME");
	if(configFilePath != NULL){
		_serviceConfigFile = (char *) tptp_malloc(strlen(configFilePath) + strlen(SERVICE_CONFIG_FILE_NAME)+1);
		strcpy(_serviceConfigFile, configFilePath);

		strcat(_serviceConfigFile, SERVICE_CONFIG_FILE_NAME);

		return;
	}else{
		//if TPTP_AC_CONFIG_HOME is not set then use the config file under TPTP_AC_HOME
		configFilePath = getenv("TPTP_AC_HOME");
		if (configFilePath != NULL) {
			_serviceConfigFile = (char *) tptp_malloc(strlen(configFilePath) + strlen(SERVICE_CONFIG_FILE_NAME) + strlen(RELATIVE_CONFIGURATION_DIR)+1);
			strcpy(_serviceConfigFile, configFilePath);

			strcat(_serviceConfigFile, CONFIG_FILE_PATH);

			return;
		}
	}
#ifdef _WIN32
	if ((name != NULL) && (strlen(name) != 0)) {
		_serviceConfigFile = (char *) tptp_malloc(strlen(name) + strlen(PARENT_CONFIG_FILE_PATH)+1);
		for (i=strlen(name)-1;i >0; i--) {
			if (name[i] == '\\') {
				break;
			}
		}
		if (i== 0) {
			strcpy(_serviceConfigFile, PARENT_CONFIG_FILE_PATH);
		} else {
			name[i] = '\0';
			strcpy(_serviceConfigFile, name);
			strcat(_serviceConfigFile, SLASH_PARENT_CONFIG_FILE_PATH);
		}
	}
	else
	{
		_serviceConfigFile = (char *) tptp_malloc(strlen(PARENT_CONFIG_FILE_PATH)+1);
		strcpy(_serviceConfigFile, PARENT_CONFIG_FILE_PATH);
	}
#else
	if ((name != NULL) && (strlen(name) != 0)) {
		_serviceConfigFile = (char *) tptp_malloc(strlen(name) + strlen(PARENT_CONFIG_FILE_PATH) +1);
		for (i=strlen(name)-1;i >0; i--) {
			if (name[i] == '/') {
				break;
			}
		}
		if (i== 0) {
			strcpy(_serviceConfigFile, PARENT_CONFIG_FILE_PATH);
		} else {
			name[i] = '\0';
			strcpy(_serviceConfigFile, name);
			strcat(_serviceConfigFile, SLASH_PARENT_CONFIG_FILE_PATH);
		}
	}
	else
	{
		_serviceConfigFile = (char *) tptp_malloc(strlen(PARENT_CONFIG_FILE_PATH) +1);
		strcpy(_serviceConfigFile, PARENT_CONFIG_FILE_PATH);
	}
#endif
}

void BaseAgentImpl::processCommandLine(int argc, char* argv[])
{
	//at present the only processing of the command line is obtaining
	//the serviceconfig file.
	setServiceConfigFile(argc, argv);
}

char *BaseAgentImpl::getServiceConfigFile()
{
	if (_serviceConfigFile == NULL) {
		//cout << "service config is null " << endl;
		//This means that the agent did not call processCommandLine
		//but we should still set the config based on our normal criteria
		setServiceConfigFile(0, NULL);
	}
	return _serviceConfigFile;
}


// asyncRegisterAgent() establishes a private connection with the Agent Controller
// and creates a thread to get messages coming in from that connection. It is 
// asynchronous method and returns immediately. 
// registerFunc executed in dedicated thread and waits until the AC's agentRegistered
// response is received.
int BaseAgentImpl::asyncRegisterAgent()
{
	TPTP_HANDLE registerThreadHandle;
	TID registerThreadID;
	int status = startNewThread(registerFunc, (void *) this, &registerThreadID, &registerThreadHandle);
	CLOSE_THREAD_HANDLE(registerThreadHandle);
	return status;
}

// registerFunc register agent at AC.
// Does not return until the AC's agentRegistered response is received.
//
// 1) Creates a named pipe which this agent will use to receive commands
// coming from the Agent Controller process. This pipe cannot be used to send
// commands to the Agent Controller because the AC will never read from this pipe,
// only write to it.
// 2) Starts a thread which runs the handleMessages() function, that listens on that
// pipe for commands coming to the agent from the AC.
// 3) Sends a CONNECT command request to the AC which will result in a CONNECTION_COMPLETE
// response being received on the handleMessages thread.
// 4) Sends a registerAgent command to the AC which will result in a agentRegistered response
// being received on the handleMessages thread.
THREAD_USER_FUNC_RET_TYPE  BaseAgentImpl::registerFunc(void* arg)
{
    BaseAgentImpl* agent = (BaseAgentImpl*)arg;
    agent->registrationStatus = -1;
	char commandFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"> <registerAgent iid=\"org.eclipse.tptp.agentManager\"><agentID>%d</agentID><processID>%lu</processID><agentName>%s</agentName></registerAgent></Cmd>";
	char command[BUFFER_LENGTH];
	PID agentProcessID;
	int rc;
	char *pUniqueId = NULL ;
	
	// Initialize semaphores that handleMessages() thread will use to let us know
	// when replies arrive from the AC during this registration process.
	rc = tptp_initializeSemaphore(&(agent->registerAgentConnectedSem));
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG(agent, "BaseAgent registerFunc: Error - Exiting, failed to create synch event for registerAgentConnected.");
		return 0;
	}

	rc = tptp_initializeSemaphore(&(agent->registerAgentCompletedSem));
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG(agent, "BaseAgent registerFunc: Error - Exiting, failed to create synch event for registerAgentConnected.");
		return 0;
	}

	rc = agent->initializeAgentConfiguration(agent->getServiceConfigFile());
	if (rc != 0)
	{
        printf("Default configuration values for connection with AC are used.\n");
        printf("Note: Agent and AC should use same configuration for correct work.\n");
    }

	// generate the unique id string for the pipe name
	pUniqueId = generateUUID() ;

	//Create the named pipe that we will use to get msgs from the AC.
	agent->agentPipeName = (char*)tptp_malloc(strlen(RA_PIPE_NAMESPACE) + strlen(pUniqueId) +1);
	sprintf(agent->agentPipeName, "%s%s", RA_PIPE_NAMESPACE, pUniqueId);
	agent->agentNamedPipeHandle = createReadOnlyNamedPipe(RA_PIPE_NAMESPACE, pUniqueId, FALSE) ;
	if (agent->agentNamedPipeHandle != INVALID_HANDLE_VALUE)
	{
		TPTP_LOG_DEBUG_MSG1(agent, "BaseAgent registerFunc: Created Agent Named Pipe - %s", agent->agentPipeName);
	}
	else
	{
		TPTP_LOG_ERROR_MSG1(agent, "Error creating Named Pipe - %s", agent->agentPipeName);
		// TODO: Send ERROR response; do cleanup
		if (agent->agentPipeName) {tptp_free(agent->agentPipeName); agent->agentPipeName=0;}
		return 0;
	}

	//TPTP_LOG_DEBUG_MSG1(this, "The AgentNamedPipeHandle is - %s", agentNamedPipeHandle);


	//Start Listening to the named pipe
	TPTP_LOG_DEBUG_MSG(agent, "Starting the Listener thread...");
	startNewThread(handleMessages, (void *) agent, &(agent->msgHandlerThreadID), &(agent->msgHandlerThreadHandle));

	// Tell the AC to connect to this pipe - it knows how to form the name of our pipe
	// by using the unique id string we generated.
	rc = agent->sendCONNECTCommand(pUniqueId) ;
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG1(agent, "Error: Attempt to send CONNECT to AC failed (unique ID:""%s"")", pUniqueId );
		//TODO: Send ERROR; do cleanup
		if (agent->agentPipeName) {tptp_free(agent->agentPipeName); agent->agentPipeName=0;}
		CLOSE_TPTP_HANDLE(agent->agentNamedPipeHandle);
		return 0;
	}

	// Wait for the handleMessages thread to get a response to the CONNECT request.
	rc = tptp_waitSemaphore(&(agent->registerAgentConnectedSem));
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG(agent, "BaseAgent registerFunc: Error - wait for registerAgentConnected failed.");
		return 0;
	}

	TPTP_LOG_DEBUG_MSG1(agent, "BaseAgent registerFunc: Agent (%s) connected to AC ", agent->agentName);
	
	// Do pre-registration initialization.  That is, initialization
	// that requires the AC communication path to be setup but before the agent advertises
	// that it is here and ready to interact with others - which the registerAgent cmd
	// will do.
	agent->preRegisterInitialization();

	//Send a register message to the AC named pipe
	agentProcessID = getCurrentlyRunningProcessId();

	sprintf( command, commandFormat, agent->agentID, agent->getAgentControllerID(), agent->getNextContext(), agent->agentID, agentProcessID, agent->agentName);		

	rc = agent->sendCommand((char*)command);  //TODO: Check return code

	// Wait for a reply to the registerAgent command we just sent to the AC.
	// When msg handling thread gets it, registration is complete.
	rc = tptp_waitSemaphore(&(agent->registerAgentCompletedSem));
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG(agent, "BaseAgent registerFunc: Error - wait for registerAgentCompleted failed.");
		return 0;
	}

	TPTP_LOG_DEBUG_MSG1(agent, "BaseAgent registerFunc: Agent register with AC completed (%s)", agent->agentName);
	tptp_deleteSemaphore(&(agent->registerAgentConnectedSem));
	tptp_deleteSemaphore(&(agent->registerAgentCompletedSem));
	agent->registrationStatus = 0;
	return 0;
}

int BaseAgentImpl::getAgentRegistrationStatus()
{
    return (registrationStatus);
}


// registerAgent() establishes a private connection with the Agent Controller
// and creates a thread to get messages coming in from that connection. Does not
// return until the AC's agentRegistered response is received.
// returns 0 if successful
int BaseAgentImpl::registerAgent()
{
	registerFunc(this);
	return getAgentRegistrationStatus();
}

int BaseAgentImpl::deRegisterAgent()
{
	char commandFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"> <deregisterAgent iid=\"org.eclipse.tptp.agentManager\"><agentID>%d</agentID></deregisterAgent></Cmd>";
	char command[BUFFER_LENGTH];

	sprintf( command, commandFormat, agentID, getAgentControllerID(), getNextContext(), agentID);		

	int rc = sendCommand((char*)command);  //TODO: Check return code

	return rc;
}


char* BaseAgentImpl::getConfigValue(char* parName)
{
	char* val = 0;
	tptp_node_t*  node;
	agent_custom_data_t* agentData;
	tptp_list_t agentDataList = agentConfig.agentCustomData;
	
	for (node = agentDataList.head; node != 0; node = node->next )
	{
		agentData = (agent_custom_data_t*)node->data;
		if (isEqualString(agentData->name, parName))
		{
			val = agentData->value;
			break;
		}
	}

	return val;
}

/**
 *********************************************************
 *                                                       
 * @brief                                                
 *    Send an XML TPTP_Error command through the command channel
 *
 * destID - connection ID of recipient of this message
 * ctxt - context value from the command which resulted in this error
 * iid - interface ID string of the component reporting the error
 * tptpErrCode - a signed integer value from the list TPTP err codes list
 *				Not a platform system specific value.
 * errInfo - a string with details about the error; preferrably in CBE format
 *
 * @return
 *    0 if success
 *    nonzero if error
 *
 *********************************************************/
int BaseAgentImpl::sendErrorCommand(const int destID,
									const int ctxt,
									const int tptpErrCode,
									char *errInfo) 
{
	//TODO: CREATE A CBE String

	char errorCmd[BUFFER_LENGTH];
	char *errStr = NULL;
	char *fullStr = NULL;

	// Convert TPTP error code into a string and append errInfo string
	// to get the complete error msg.
	errStr = tptp_getErrorString(tptpErrCode);
	if (errInfo)
	{
		fullStr = (char *) tptp_malloc(strlen(errStr) + 3 + strlen(errInfo) +1);
		strcpy(fullStr, errStr);
		strcat(fullStr, " : "); //separator between messages
		strcat(fullStr, errInfo);
		tptp_free(errStr); //free space allocated by tptp_getErrorString()
	} else
	{
		fullStr = errStr; //we'll free errStr later as fullStr
	}
		
	sprintf( errorCmd, "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"> "\
					   "<TPTP_Error iid=\"%s\">"\
					   "<ErrCode>%d</ErrCode>"\
					   "<ErrInfo>\"%s\"</ErrInfo>"\
					   "</TPTP_Error></Cmd>",
					   agentID, destID, ctxt,
					   TPTP_ERROR_IID,
					   tptpErrCode,
					   fullStr);
					   //(errInfo)?errInfo:"");

	tptp_free(fullStr);
	return (sendCommand(errorCmd));
}

/**
 *********************************************************
 *                                                       
 * @brief                                                
 *    Send the CONNECT command.
 *
 *
 * @return
 *    0 if success
 *    nonzero if error
 *
 *********************************************************/
int BaseAgentImpl::sendCONNECTCommand(char *pUniqueId) 
{

	char cmd[BUFFER_LENGTH] ;

	strcpy(cmd, pUniqueId);

	return (sendCommand(cmd, strlen(cmd)+1, CONNECT));
}


/**
 *********************************************************
 *                                                       
 * @brief                                                
 *    Send an XML command to Agent Controller's named pipe.
 *
 * @return
 *    0 if success
 *    nonzero if error
 *
 *********************************************************/
int BaseAgentImpl::sendCommand(char *pMessage) 
{
	return (sendCommand(pMessage, strlen(pMessage)+1, 0));
}

/**
 *********************************************************
 *                                                       
 * @brief                                                
 *    Send a command to Agent Controller's named pipe.
 *
 *	If flags are passed in, body of message need not be XML formatted
 *	as the transport layer typically processes commands that come as flags.
 *
 * @return
 *    0 if success
 *    nonzero if error
 *
 *********************************************************/
int BaseAgentImpl::sendCommand(char *pMessage, int msgLength, unsigned int flags) 
{
	int rc = 0;

	char *cmdbuffer = 0;
	int  bufferLength = 0;
	int  bytesSent = 0;
	int  bytesWritten = 0;
	TPTP_HANDLE ACNamedPipeHandle = 0;
	
#ifdef LOCAL_DEBUG
	if (flags)
		printf("\nAgent '%s' sending flag msg (len %d) to AC: 0x%08x\n", agentName, msgLength, flags);
	else
		printf("\nAgent '%s' sending msg (len %d) to AC: %s\n", agentName, msgLength, pMessage);
#endif

	// Add the Message header
	addBasicMsgHeader(pMessage, msgLength, &cmdbuffer, &bufferLength, flags) ;
	
	int connectTryCount = 0;

	do
	{
		tptp_getWriteLock( &cmdPipeLock );
		//Open the named pipe
		ACNamedPipeHandle = openWriteOnlyNamedPipe(RA_PIPE_NAMESPACE, _raMasterAddress, FALSE) ;			
		
		if (ACNamedPipeHandle == (TPTP_HANDLE)-1)
		{
			tptp_releaseWriteLock(&cmdPipeLock);
			SLEEP(CONNECT_TO_AC_RETRY_WAIT_TIME) ;			
		}
		connectTryCount++;
		TPTP_LOG_DEBUG_MSG2(this, "Try count %d and connect result - %d", connectTryCount, ACNamedPipeHandle);
	}
	while(ACNamedPipeHandle == (TPTP_HANDLE)-1);

	
	// send a message to the agent controller
	bytesWritten = writeToNamedPipe(ACNamedPipeHandle, cmdbuffer, 0, bufferLength, &bytesSent);
	if (bytesSent < 0)
	{
//		TPTP_LOG_ERROR_MSG1(this, "Error: Failed to send cmd ""%s"" through AC named pipe", pMessage);
		rc = TPTP_AGENT_CANT_WRITE_AC_COMM;
	}
	else
	{
//		TPTP_LOG_DEBUG_MSG2(this, "Sent cmd to AC, flags=0x%x, cmd=""%s"" ", flags, pMessage);
	}

	if (cmdbuffer) tptp_free(cmdbuffer);

	cleanPipeUp(&ACNamedPipeHandle) ;

	tptp_releaseWriteLock(&cmdPipeLock);

	return ( rc ) ;
}


/**
 *********************************************************
 *                                                       
 * @brief                                                
 *    Perform agent initialization that requires Agent Controller communication prior to registration.
 *
 *	Allows an agent to insert initialzation into the registration process. This method
 *  is called after the agent's communication channel with the Agent Controller has
 *  been established, so it can include an exchange of commands. But the call occurs
 *  prior to sending the registerAgent request to the Agent Controller, so no other
 *  component can try to communicate with this agent yet.
 *
 * @return
 *    0 if success
 *    nonzero if error
 *
 *********************************************************/
int BaseAgentImpl::preRegisterInitialization()
{
	TPTP_LOG_DEBUG_MSG(this, "BaseAgentImpl - preRegisterInitialization() called");

	return 0;

}


int BaseAgentImpl::initialize()
{
	TPTP_LOG_DEBUG_MSG(this, "BaseAgentImpl - initialize() called");

	return 0;

}


int BaseAgentImpl::terminate()
{

	TPTP_LOG_DEBUG_MSG(this, "BaseAgentImpl - terminate() called");

	/* TODO: Stop the handleMessages thread.  Perform other cleanup */

	tptp_postSemaphore( &terminateSemaphore );

	return 0;
}
int getMsgLength(char * pBuffer)
{
	unsigned int magicNumber = 0;
	unsigned int flags = 0 ;
	unsigned int payLoadLength = 0 ;
	unsigned int msgLength = 0 ;
	unsigned char     *pMsg = NULL ;

	/* We aren't using the first two elements here.  We're reading them to skip over them. */
	pMsg = readUINTFromBuffer((unsigned char*)pBuffer, &magicNumber);
	pMsg = readUINTFromBuffer(pMsg, &flags);

	/* read the payload length */
	pMsg = readUINTFromBuffer(pMsg, &payLoadLength) ;

	msgLength = payLoadLength + sizeof(tptp_basic_msg_header_t) ;

	return msgLength;
}

THREAD_USER_FUNC_RET_TYPE BaseAgentImpl::handleMessages(void* pRequestBlock)
{
	HANDLE hAgentPipe;
	int bytesRead = 0;
	int offset = 0;
	static int length = 8192;
	static char *buffer = (char *) tptp_malloc(length + 1);
	int result = 0;
	BaseAgentImpl* AgentInstance;

	if (pRequestBlock)
	{
		AgentInstance = (BaseAgentImpl*)pRequestBlock;

		TPTP_LOG_DEBUG_MSG(AgentInstance, "Agent Message Handling Thread Started");

		int isConnectedSuccessful = 0 ;
		
		do
		{
			isConnectedSuccessful = connectToNamedPipe(&AgentInstance->agentNamedPipeHandle, AgentInstance->agentPipeName);
			if (isConnectedSuccessful != 0)
				SLEEP(100) ;
		} while (isConnectedSuccessful != 0) ;
		
		hAgentPipe = (AgentInstance)->agentNamedPipeHandle;	

		while(true)
		{
			bytesRead = 0 ;

			/* Another message might come in while we're processing
			 *    so we read until the pipe is empty                 */
			while ( 0 < (result = readFromNamedPipe(hAgentPipe, buffer, offset, length, &bytesRead)) )
			{
				if ((bytesRead + offset) < sizeof(tptp_basic_msg_header_t))
				{
					/* too small to determine the payload length. Go read some more */
					offset += bytesRead ;
				}
				else if (bytesRead == length) 
				{
					//We read up to the length of our buffer
					//We need to check the size of the message coming in to know if we need to
					//realloc a bigger buffer.
					int msgLength = getMsgLength(buffer);
					char *temp = buffer;

					if (msgLength > length)
					{
						offset += bytesRead ;
						buffer = (char *) tptp_realloc(buffer, msgLength+1);

						//Try to handle not being able to realloc memory.
						if ( buffer == NULL )
						{
							buffer = temp;
						}
						else
						{
							length = msgLength;
						}
					} else if (msgLength == length)
					{
						//The message length is exactly equal to our buffer.
						//Process the message.
						offset = AgentInstance->processRecdMessages(buffer, bytesRead + offset);
					}
				}
				else
				{
					//process the message
					offset = AgentInstance->processRecdMessages(buffer, bytesRead + offset);
				}
			}

			if (result <0)
			{
				// If get negative return val, pipe is bad (probably broken because AC has died).
				TPTP_LOG_ERROR_MSG1(AgentInstance, "handleMessages: Error attempting to read from AC named pipe (rc=%d)", result);
				return 0;
				//continue;
			}
			else if (result == 0)
			{
				// 0 returned when at EOF - i.e., read from broken pipe.
				TPTP_LOG_ERROR_MSG1(AgentInstance, "handleMessages: Error attempting to read from AC named pipe (rc=%d)", result);
				return 0;
			}
		}
	}
	return 0;
}


int BaseAgentImpl::processRecdMessages( char* pBuffer, int bytesRead )
{
	int      hasMore = 1 ;
	unsigned char     *pMsg = NULL ;
	unsigned int magicNumber = 0;
	unsigned int flags = 0 ;
	unsigned int payLoadLength = 0 ;
	unsigned int msgLength = 0 ;
	unsigned int bytesToBeProcessed = bytesRead ;
	char  *pBeginning = pBuffer ;

	TPTP_LOG_DEBUG_MSG1(this, "Agent(%d): processRecdMsgs().", getAgentID() ) ;

	do
	{
		/* process one message at a time in the given buffer */

		/* We aren't using the first two elements here.  We're reading them to skip over them. */
		pMsg = readUINTFromBuffer((unsigned char*)pBuffer, &magicNumber);
		pMsg = readUINTFromBuffer(pMsg, &flags);

		/* read the payload length */
		pMsg = readUINTFromBuffer(pMsg, &payLoadLength) ;

		msgLength = payLoadLength + sizeof(tptp_basic_msg_header_t) ;

		if (msgLength <= bytesToBeProcessed)
		{
			/* Save the byte after our message */
			char temp = pBuffer[msgLength];

			/* Provide a zero terminator */
			pBuffer[msgLength] = 0;

			/* Process the message */
			processOneMessage(pBuffer) ;

			/* Restore the byte after our message */
			pBuffer[msgLength] = temp;

			/* advance to the next message in the buffer */
			pBuffer += msgLength ;

			/* the remainder length of data to be processed */
			bytesToBeProcessed -= msgLength ;
			TPTP_LOG_DEBUG_MSG2(this, "Agent(%d): Bytes in msg left to be processed: %d bytes.", getAgentID(), bytesToBeProcessed);
		}
		else
		{
			/* reach the end of the last complete message */
			TPTP_LOG_DEBUG_MSG1(this, "Agent(%d): Too few bytes remain to form complete msg.", getAgentID() );
			hasMore = 0 ;
		}

	} while ((bytesToBeProcessed >= sizeof(tptp_basic_msg_header_t)) &&
		     (hasMore != 0)) ;

	if (bytesToBeProcessed > 0)
	{
		/* move the incomplete message to the beginning of the buffer */
		memmove(pBeginning, pBeginning+bytesRead-bytesToBeProcessed, bytesToBeProcessed) ;
	}

	return ( bytesToBeProcessed ) ;
}

int BaseAgentImpl::processOneMessage( char* buffer )
{
	unsigned int magicNumber;
	unsigned int flags;
	unsigned int payLoadLength;
	MsgBlock* pMsgBlock = new MsgBlock(); //TODO: delete this msgBlock somewhere

	// parse the message we received into its header parts
	unsigned char* pMsg;

	pMsg = readUINTFromBuffer((unsigned char*)buffer, &magicNumber);
	pMsgBlock->setMagicNumber(magicNumber);
	pMsg = readUINTFromBuffer(pMsg, &flags);
	pMsgBlock->setFlags(flags);
	pMsg = readUINTFromBuffer(pMsg, &payLoadLength) ;

	pMsgBlock->setPayLoadLength(payLoadLength);
	pMsgBlock->setMsg(pMsg, payLoadLength);  //makes a copy of the payload part of the msg string

	// Check for special commands that are indicated by flags.
	// Their payloads will not be XML formatted strings like regular commands.
	if (flags)
	{
		TPTP_LOG_DEBUG_MSG2(this, "Agent '%s' handling flag cmd 0x%08x", agentName, flags);
		processFlagCommand(pMsgBlock);
	}
	else
	{
		//Process normal commands whose payload should be in XML format.
		int            ret = -1;
		int            sourceID = 0;
		int            contextID = 0;
		char*          interfaceName = NULL;
		char*          cmdName = NULL;

		tptp_list_t* paramList = NULL;

		CmdBlock* cmdBlock = new CmdBlock();

		TPTP_LOG_DEBUG_MSG1(this,"Agent '%s' handling xml cmd", agentName);		

		ret = parseCommand( (char*)pMsgBlock->getMsg(), &sourceID, &contextID, &interfaceName, &cmdName, &paramList );

		if ( ret == 0 )
		{
			TPTP_LOG_DEBUG_MSG2(this, "Agent '%s' parsed xml cmd %.4096s", agentName, (char*)pMsgBlock->getMsg());
			cmdBlock->setSourceID(sourceID);										
			cmdBlock->setDestID(getAgentID());					
			cmdBlock->setContextID(contextID);					
			cmdBlock->setIID(interfaceName);					
			cmdBlock->setCommandName(cmdName);					
			cmdBlock->setParamList(paramList);
						
			processCommand(cmdBlock);
		}
		else {
			// Note: Not printing the cmd string in the error log because it could be a bad string.
			TPTP_LOG_ERROR_MSG1(this, "Agent '%s' Failed to parse xml cmd", agentName);
		}


		if (paramList != NULL) {
			tptp_list_clear(paramList);
			tptp_free(paramList);
		}

		if (interfaceName != NULL) tptp_free(interfaceName);
		if (cmdName != NULL) tptp_free(cmdName);
			
		delete(cmdBlock);
	}
	delete(pMsgBlock);

	return 0;
}

int BaseAgentImpl::processFlagCommand(MsgBlock *pMsgBlock)
{
	
	if ((pMsgBlock->getFlags() & CONNECTION_COMPLETE) == CONNECTION_COMPLETE)
	{
		int rc;
		unsigned int connId = 0;
		readUINTFromBuffer(pMsgBlock->getMsg(), &connId);
		agentID = (int)connId;

		agentConnected = true;	
		rc = tptp_postSemaphore(&registerAgentConnectedSem);
		if (rc != 0)
		{
			TPTP_LOG_ERROR_MSG(this, "processFlagCommand: Error - failed to post event for registerAgentConnected.");
			return -1;
		}

		TPTP_LOG_DEBUG_MSG2(this, "Agent's (%s) connection id is - %d", agentName, agentID);
	}
	return 0;
} 

// Return value indicates if the command was processed (i.e., recognized).
int BaseAgentImpl::processCommand(CmdBlock* cmd)
{

	int processed = 0;  // Assume we will handle it, set to -1 if we don't.
	int rc;
	char* cmdName = cmd->getCommandName();
	
	if (isEqualString( cmd->getIID(),"org.eclipse.tptp.Agent"))
	{
		if (isEqualString(cmdName, "terminate"))
		{
			this->terminate();
		}
		else if (isEqualString(cmdName, "notifyReference"))
		{
			int                    flags;
			int                    clientID;
			tptp_clientAccessLevel cal;
			tptp_list_t*        paramList = cmd->getParamList();

			if (0 != getIntegerParam( "clientID", paramList, &clientID ) )
			{
				TPTP_LOG_ERROR_MSG(this, "The notifyReference command arrived without a client ID.");
		
				// If we don't have a valid client ID, we can't do anything with this
				return 0;
			}

			if (0 != getIntegerParam( "flags", paramList, &flags ) )
			{
				// If we didn't get a flags parameter, we can assume a default
				flags = TPTP_OBSERVER_ACCESS;
			}

			// There are other flags values, but access level is all we need
			if ( flags & TPTP_CONTROLLER_ACCESS )
			{
				cal = TPTP_CONTROLLER_CAL;
			}
			else
			{
				// Even if TPTP_OBSERVER_ACCESS isn't explicitly set, it's the default
				cal = TPTP_OBSERVER_CAL;
			}

			addClient( clientID, cal );
		}
		else if (isEqualString(cmdName, "notifyDereference"))
		{
			int          clientID;
			tptp_list_t*   paramList = cmd->getParamList();

			if (0 != getIntegerParam( "clientID", paramList, &clientID ) )
			{
				// If we don't have a valid client ID, we can't do anything with this
				return 0;
			}

			removeClient( clientID );
		}
		else  // Unrecognized command
		{
			processed = -1;
		}
	} else if ( isEqualString( cmd->getIID(), AGENT_MANAGER_IID))
	{
		// Handle the agentRegistered response from the agentManager interface
		// which marks the completion of the registerAgent command.
		if (isEqualString(cmdName, "agentRegistered"))
		{
			agentRegistered = true;
				
			rc = tptp_postSemaphore(&registerAgentCompletedSem);
			if (rc != 0)
			{
				TPTP_LOG_ERROR_MSG(this, "BaseAgent processCommand: Error - failed to post event for registerAgentCompleted.");
			}
			//TODO - Agent Configuration reading
			//getStringParam( "configFile", cmd->getParamList(), &(configFile) );
			//if (configFile != 0)
			//{
				//TODO - Uncomment this code later when we enable Agent Configuration
				//loadAgentConfig(this, configFile, &agentConfig);
			//}
		}
		else if (isEqualString(cmdName, "agentDeregistered"))
		{
			agentRegistered = false;
		}
		else  // Unrecognized command
		{
			processed = -1;
		}
	} else	// Unrecognized interface
	{
		processed = -1;
	}

	return processed;

}



// Called by the base command handler when a notifyReference command is received

void BaseAgentImpl::addClient(tptp_int32 clientID, tptp_clientAccessLevel accessLevel)
{
	tptp_int32*  refID = (tptp_int32*)tptp_malloc( sizeof(tptp_int32) );

	if ( 0 == refID )
	{
		TPTP_LOG_ERROR_MSG(this, "Out of memory attempting to add client reference" );
		return;
	}

	*refID = clientID;

	tptp_getWriteLock( &clientListsLock );

	if ( accessLevel == TPTP_CONTROLLER_CAL )
	{
		tptp_list_add( &controllerList, refID );
	}
	else
	{
		tptp_list_add( &observerList, refID );
	}

	tptp_releaseWriteLock( &clientListsLock );
}

// Called by the base command handler when a notifyDeference command is received

void BaseAgentImpl::removeClient(tptp_int32 clientID)
{
	tptp_node_t* node;

	tptp_getWriteLock( &clientListsLock );

	// Look for our object among the controllers 
	for ( node = controllerList.head; node != 0; node = node->next) 
	{
		tptp_int32* cid = (tptp_int32*)node->data;

		if ( *cid == clientID )
		{
			tptp_list_remove( &controllerList, cid );
			tptp_releaseWriteLock( &clientListsLock );
			return;
			// Note: The remove call causes our 'node' variable to be deleted
			//      If we were not returning here, we would need to change
			//      this 'for' loop to a 'while' loop and update 'node'
			//       before doing the remove
		}
	}

	// Look for our object among the observers
	for ( node = observerList.head; node != 0; node = node->next) 
	{
		tptp_int32* cid = (tptp_int32*)node->data;

		if ( *cid == clientID )
		{
			tptp_list_remove( &observerList, cid );
			tptp_releaseWriteLock( &clientListsLock );
			return;
			// Note: The remove call causes our 'node' variable to be deleted
			//      If we were not returning here, we would need to change
			//      this 'for' loop to a 'while' loop and update 'node'
			//       before doing the remove
		}
	}
	
	tptp_releaseWriteLock( &clientListsLock );

	TPTP_LOG_ERROR_MSG(this, "Received notifyDereference for a client which was not in our controller or observer list.");

	// If we get here, this client didn't have access, but there's not
	//   really anything we need to do in that case, so we just return 
}

// Verifies that the client has the specified access to this agent

bool BaseAgentImpl::checkClientAccess(int clientID, tptp_clientAccessLevel accessLevel)
{
	tptp_node_t* node;

	tptp_getReadLock( &clientListsLock );

	// If only observer access is requested, check the observer list
	if ( accessLevel == TPTP_OBSERVER_CAL )
	{
		for ( node = observerList.head; node != 0; node = node->next) 
		{
			tptp_int32* cid = (tptp_int32*)node->data;

			if ( *cid == clientID )
			{
				// The client was in the list, so it has access
				tptp_releaseReadLock( &clientListsLock );
				return true;
			}
		}
	}

	// Check the controller list whether observer or controller access was
	//    requested because controller access includes observer access
	for ( node = controllerList.head; node != 0; node = node->next) 
	{
		tptp_int32* cid = (tptp_int32*)node->data;

		if ( *cid == clientID )
		{
			// The client was in the list, so it has access
			tptp_releaseReadLock( &clientListsLock );
			return true;
		}
	}

	tptp_releaseReadLock( &clientListsLock );

	// We didn't find the client, so it doesn't have the requested level of access
	return false;
}

char* BaseAgentImpl::getAgentName()
{
	return agentName;
}

int BaseAgentImpl::getAgentID()
{
	return agentID;
}

int BaseAgentImpl::getAgentControllerID()
{
	return AGENT_MANAGER;
}

CmdBlock::CmdBlock()
{
	_sourceID = -1;
	_destID = -1;
	_contextID = -1;
	_iid = NULL;			//Interface ID string
	_commandName = NULL;	//Cmd string
	tptp_list_init(&_paramList);
	tptp_list_setNodeDestructor(&_paramList, destroyParamNode);
	tptp_list_setNodeCopier(&_paramList, copyParamNode);
}

CmdBlock::~CmdBlock()
{
	tptp_free(_iid); _iid = NULL;
	tptp_free(_commandName); _commandName = NULL;

	tptp_list_clear(&_paramList);
}

tptp_int32 BaseAgentImpl::logEvent(	tptp_string* subcomponent,
                                    tptp_uint32  instanceId,
                                    tptp_string* filename, 
                                    tptp_int32   lineNum, 
                                    tptp_int32   severity, 
                                    tptp_string* event )
{
	if ( severity < loggingLevel )
	{
		return 0;
	}

	// TODO: Expand the conditions under which we might not want to send this to the AC 
	//       For instance, there might be a configuration option.  We should probably be
	//       calling a virtual function to query for this condition
	if ( getAgentID() == -1 )
	{
		// TODO: Write the event to an agent-specific log file
	}
	else
	{
		char  *cmd = NULL;
		char  cmdFormat[] = "<Cmd src=\"%ld\" dest=\"%ld\" ctxt=\"%ld\"><logEvent iid=\"org.eclipse.tptp.platform.loggingService\"><event>%s</event></logEvent></Cmd>";
		tptp_string* eventStr = 0;

		// Build our logEvent command
		if ( (loggingFormat != TPTP_LOGFORMAT_CBE) ||
		     (0 != tptp_createLogCBE( getAgentName(), subcomponent, instanceId, filename, lineNum, severity, event, &eventStr )) )
		{
			if ( 0 != tptp_normalizeLogMsg( event, &eventStr ) )
			{
				return -1;
			}
		}

		// Calculate length of command.  Assumes max # digits per number is 32.
		cmd = (char*)tptp_malloc(strlen(cmdFormat)+ 32 + 32 + 32 + strlen(eventStr) +1 );
		if (!cmd) return -1;
		sprintf( cmd, cmdFormat, agentID, LOGGING_SERVICE, getNextContext(), eventStr );

#ifdef LOCAL_DEBUG
		printf("\n%s\n", event);
#else
		// Send the command to the Agent Controller's logging service
		sendLogCommand( cmd );
#endif  // LOCAL_DEBUG

		// Free the cbe record
		tptp_free( eventStr );

		if (cmd) tptp_free(cmd);
	}

	return 0;
}

/**
 *********************************************************
 *                                                       
 * @brief                                                
 *    Send a command to Agent Controller's named pipe.
 *
 *	If flags are passed in, body of message need not be XML formatted
 *	as the transport layer typically processes commands that come as flags.
 *
 * @return
 *    0 if success
 *    nonzero if error
 *
 *********************************************************/
int BaseAgentImpl::sendLogCommand(char *pMessage) 
{
	int rc = 0;
	int msgLength = strlen( pMessage );

	char logPipeName[_MAX_PATH];
	char *cmdbuffer = 0;
	int  bufferLength = 0;
	int  bytesSent = 0;
	int  bytesWritten = 0;
	TPTP_HANDLE ACNamedPipeHandle;
	int  retryCount = 0;

	// TODO: Revisit this and make the logging pipe a unique component with its own configuration
	// Assume a pipe name derived from the main master pipe name
	sprintf( logPipeName, "%slog", _raMasterAddress );
	
	//NOTE: Cannot use TPTP_LOG_xxx macros in here as they call
	// this function.  You will get a stack overflow (Windows) or 
	// a deadlock (Linux) if you do.

	// Add the Message header
	addBasicMsgHeader(pMessage, msgLength, &cmdbuffer, &bufferLength, 0) ;

	tptp_getWriteLock( &logPipeLock );

	do
	{
		//Open the named pipe
		ACNamedPipeHandle = openNonCriticalWriteOnlyNamedPipe(RA_PIPE_NAMESPACE, logPipeName, FALSE) ;

		// There is one error that merits retry
		if ( ACNamedPipeHandle == (HANDLE)TPTP_NO_NAMED_PIPE_READER )
		{
			SLEEP( 100 );
			retryCount++;
		}
		else if (ACNamedPipeHandle < 0)
		{
			tptp_releaseWriteLock(&logPipeLock);
			if (cmdbuffer) tptp_free(cmdbuffer);
			return TPTP_AGENT_CANT_OPEN_AC_COMM;
		}
	}
	while ( (ACNamedPipeHandle < 0) && ( retryCount < 5 ) );
	
	// send a message to the agent controller
	bytesWritten = writeToNamedPipe(ACNamedPipeHandle, cmdbuffer, 0, bufferLength, &bytesSent);
	if (bytesSent < 0)
	{
		rc = TPTP_AGENT_CANT_WRITE_AC_COMM;
	}

	if (cmdbuffer) tptp_free(cmdbuffer);

	cleanPipeUp(&ACNamedPipeHandle) ;

	tptp_releaseWriteLock(&logPipeLock);

	return ( rc ) ;
}



void CmdBlock::setSourceID(int srcID)
{
	_sourceID = srcID;
}


void CmdBlock::setDestID(int destID)
{
	_destID = destID;
}


void CmdBlock::setContextID(int contextID)
{
	_contextID = contextID;
}

void CmdBlock::setIID(char* iid)
{
	if (iid == _iid) return;

	if (_iid) tptp_free(_iid);

	if (iid)
	{
		int len = strlen(iid) + 1;
		_iid = (char *) tptp_malloc(len);
		memcpy(_iid, iid, len);
	}
	else
		_iid = NULL;
}


void CmdBlock::setCommandName(char* commandName)
{
	if (commandName == _commandName) return;

	if (_commandName) tptp_free(_commandName);

	if (commandName)
	{
		int len = strlen(commandName) + 1;
		_commandName = (char *) tptp_malloc(len);
		memcpy(_commandName, commandName, len);
	}
	else
		_commandName = NULL;
}

void CmdBlock::setParamList(tptp_list_t* newParamList)
{
	// Clear any existing parameters and copy the new list of
	// parameters into this command.
	tptp_list_clear(&_paramList);
	tptp_list_clone(&_paramList, newParamList);
	return;
}


int CmdBlock::getSourceID()
{
	return _sourceID;
}


int CmdBlock::getDestID()
{
	return _destID;
}

int CmdBlock::getContextID()
{
	return _contextID;
}


char* CmdBlock::getIID()
{
	return _iid;
}


char* CmdBlock::getCommandName()
{
	return _commandName;
}


tptp_list_t* CmdBlock::getParamList()
{
	return &_paramList;
}

MsgBlock::MsgBlock()
{
	_magicNumber = 0;
	_flags = 0;
	_payLoadLength = 0;
	_pMsg = NULL;
}

MsgBlock::~MsgBlock()
{
	tptp_free(_pMsg); _pMsg = NULL;
}

void MsgBlock::setMagicNumber(unsigned int magNum)
{
	_magicNumber = magNum;
}

void MsgBlock::setFlags(unsigned int flags)
{
	_flags = flags;
}

void MsgBlock::setPayLoadLength(unsigned int length)
{
	_payLoadLength = length;
}

void MsgBlock::setMsg(unsigned char* pMsg, int count)
{
	if (_pMsg) tptp_free(_pMsg);

	if (pMsg)
	{
		_pMsg = (unsigned char *) tptp_malloc(count + 1) ;
		memcpy(_pMsg, pMsg, count) ;
		_pMsg[count] = 0;
	}
	else
		_pMsg = NULL;
}
