/*******************************************************************************
 * 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:
 *    Andy Kaylor, Intel - Initial API and Implementation
 *    Kevin O'Leary, Intel - Implementation
 *
 * $Id: AgentCmdHandlers.c,v 1.25 2009/08/26 14:59:58 jwest Exp $
 *
 *******************************************************************************/ 

#include "tptp/TPTPTypes.h"
#include "tptp/TPTPSupportTypes.h"
#include "tptp/TransportSupport.h"
#include "tptp/TPTPBaseTL.h"
#include "tptp/BaseTLLog.h"
#include "tptp/dime.h"
#include "AgentCTL.h"
#include "RACmdHandlers.h"
#include "RACAgentSupport.h"
#include "Connect2AC.h"

/* 
 * This file provides the code to handle commands that are sent from the client
 *   or Agent Controller to specific agents.  Here these commands are translated
 *   into their equivalents in RAC protocol and forwarded to the agent.
 *   Additional handling logic may be implemented here in order to provide
 *   fully transparent behavior at both ends of the connection (that is, so that
 *   the agent appears as a new agent to the client and the TL and so that the
 *   client and AC appear as an old client and a RAC to the agent.
 */

tptp_int32 handleTerminateCommand( tl_state_data_t* stateData, agent_t* agent )
{
	TPTP_LOG_WARNING_MSG1( stateData, "Agent Controller requested that the agent (%s) be terminated, but we can't do that with legacy agents.", agent->agentName.data );
	return 0;
}

tptp_int32 handleNotifyReferenceCommand( tl_state_data_t* stateData, agent_t* agent, tptp_uint32 clientID )
{
	/* Legacy agents only support one client at a time */
	if ( agent->attached || agent->client )
	{
		TPTP_LOG_ERROR_MSG2( stateData, "Agent Controller allowed multiple clients to attach to a legacy agent, agent=%p, pid=%lu.", agent, agent->process->pid );
		return -1;
	}

	/* Take note that this agent has attached to this agent */
	agent->attached = TRUE;
	agent->client = (client_t*)tptp_malloc( sizeof(client_t) );
	if ( agent->client == NULL )
	{
		TPTP_LOG_ERROR_MSG( stateData, "Out of memory while attempting to attach client to agent." );
		return -1;
	}
	agent->client->clientID = clientID;

	/* If we've kept a reference to a previous client, free it now */
	if ( agent->prev_client != NULL )
	{
		tptp_free(agent->prev_client);
		agent->prev_client = NULL;
	}

	/* It might seem like we should notify the agent via RA_ATTACH_TO_AGENT, 
	   but the RAC doesn't and we want to match the RAC's behavior */

	return 0;
}

tptp_int32 handleNotifyDereferenceCommand( tl_state_data_t* stateData, agent_t* agent, tptp_uint32 clientID )
{
	ra_message_t *forwardMessage;
	ra_command_t *forwardCommand;

	if ( agent->client->clientID != clientID )
	{
		TPTP_LOG_WARNING_MSG( stateData, "A dereference was received from a client other than the attached client." );
		/* return -1; */ /* Bug 192980 */
	}

	/* Inform the agent to stop monitoring if necessary*/
	if(agent->logging) 
	{
		ra_message_t *message;
		ra_command_t *stopMonitoringCommand;

		message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
		stopMonitoringCommand=ra_addCommandToMessage(message, NULL);
		stopMonitoringCommand->tag=RA_STOP_MONITORING_AGENT;
		stopMonitoringCommand->info.stop_monitor.processId=agent->process->pid;
		ra_copyRASTRING(&stopMonitoringCommand->info.stop_monitor.agent, &agent->agentName);
		ra_forwardMessage(message, agent);
		ra_destroyMessage(message, TRUE);
		agent->logging=FALSE;
		TPTP_LOG_INFO_MSG(stateData, "Stopping logging because the client detached.");
	}

	/* Formulate the message to forward to the agent */
	forwardMessage=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	forwardCommand=ra_addCommandToMessage(forwardMessage, NULL);

	forwardCommand->tag=RA_DETACH_FROM_AGENT;
	forwardCommand->info.detach.context=0;
	ra_copyRASTRING(&forwardCommand->info.detach.agent, &agent->agentName);
	forwardCommand->info.detach.processId=agent->process->pid;

	/* Forward the message */
	ra_forwardMessage(forwardMessage, agent);
	ra_destroyMessage(forwardMessage, TRUE);

	agent->IPCBufCreated=FALSE;
	//sharedMemRemove(stateData, agent->dataConnectionID); //kevin
	

	/* Disconnect the agent from the client */
	agent->prev_client = agent->client; /* 9693 */ /* 10000 */
	agent->client = NULL;
	agent->attached = FALSE;

	return 0;
}

tptp_int32 handleEstablishDataPathCommand( tl_state_data_t* stateData, agent_t* agent, tptp_uint32 partnerID, tptp_uint32 flags, tptp_uint32 clientID, tptp_uint32 contextID )
{
	int rc;

	/* Most of the work here is done in response to the dataConnectionBound which is the response to our bind message */
	rc = bindDataConnections(stateData, agent, partnerID, flags, clientID, contextID);

	return rc;
}

tptp_int32 startMonitor( tl_state_data_t* stateData, agent_t* agent, tptp_int32 contextID )
{

	ra_message_t*         message;
	ra_command_t*         command;
	static int ra_shm_buf_num = 0;
#define RA_SHM_BUF_NAME_ROOT  "rabuffer"
	char buffname[256];

	sprintf(buffname, "%s%d", RA_SHM_BUF_NAME_ROOT, agent->agentID);
	/*  Create message to send to Agent */
	message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command=ra_addCommandToMessage(message, NULL);
	command->tag=RA_START_MONITORING_AGENT_LOCAL;
	command->info.start_monitor_local.context=0;
	command->info.start_monitor_local.processId=agent->process->pid;
	ra_copyRASTRING(&command->info.start_monitor_local.agent, &agent->agentName);
	ra_createRASTRING(&command->info.start_monitor_local.file, buffname);

	ra_forwardMessage(message, agent);

	return 0;
}

tptp_int32 handleDataConnectionsBoundCommand( tl_state_data_t* stateData, agent_t* agent, tptp_int32 contextID )
{
	int rc;

	/* Send reply back to client that data path is established */
	rc = dataPathEstablished(stateData, agent, agent->agentID, contextID);

	return rc;
}

tptp_int32 handleReleaseDataPathCommand( tl_state_data_t* stateData, agent_t* agent, tptp_uint32 partnerID )
{
	ra_message_t *message;
	ra_command_t *stopMonitoringCommand;

	message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	stopMonitoringCommand=ra_addCommandToMessage(message, NULL);
	stopMonitoringCommand->tag=RA_STOP_MONITORING_AGENT;
	stopMonitoringCommand->info.stop_monitor.processId=agent->process->pid;
	ra_copyRASTRING(&stopMonitoringCommand->info.stop_monitor.agent, &agent->agentName);
	ra_forwardMessage(message, agent);
	ra_destroyMessage(message, TRUE);
	agent->logging=FALSE;

	return 0;
}

tptp_int32 handleConnectionInfoReceived( tl_state_data_t* stateData, agent_t* agent, tptp_string* ci, tptp_int32 context )
{
	actl_pmi_context_t* pmiContextData;
	tptp_int32          ret;

	ret = baseTL_getContextData( stateData, context, (void**)&pmiContextData );
	if ( (ret != 0) || (pmiContextData == NULL) )
	{
		TPTP_LOG_ERROR_MSG( stateData, "Unable to find context data to handle pmiResponse." );
		return -1;
	}

	ret = getXmlFragmentElementInt( ci, "sourceConnectionInfo", "port", &pmiContextData->port );
	if ( ret != 0 )
	{
		TPTP_LOG_ERROR_MSG( stateData, "Received connectionInfo but it contained no port information." );
		pmiContextData->status = -1;
		return -1;
	}

	pmiContextData->status = 0;

	tptp_postSemaphore( &pmiContextData->completionSemaphore );

	return 0;
}

int getPeerMonitoringPort( tl_state_data_t* stateData, agent_t* agent )
{
	char  getPmiFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><getPeerMonitoringInfo iid=\"org.eclipse.tptp.ac.peerMonitoring\"><connectionType>sourceConnectionInfo</connectionType></getPeerMonitoringInfo></Cmd>";
	char  getPmiCmd[256];
	int   context = baseTL_getNextContextID();
	actl_pmi_context_t* pmiContextData;
	int   port;

	pmiContextData = (actl_pmi_context_t*)tptp_malloc( sizeof(actl_pmi_context_t) );
	if ( pmiContextData == NULL )
	{
		/* In case of error, fall back on default behavior */
		return ra_getConfiguredServerPort();
	}

	/* Setup our context data, including a default port value which will be replaced on success */
	pmiContextData->contextDataType = GET_PMI_CONTEXT_TYPE;
	pmiContextData->status          = BASETL_STATUS_PENDING;
	pmiContextData->timeout         = 2000;
	pmiContextData->port            = ra_getConfiguredServerPort();
	tptp_initializeSemaphore( &pmiContextData->completionSemaphore );

	/* Create the command to request peer monitoring info */
	sprintf( getPmiCmd, getPmiFormat, agent->agentID, AGENT_MANAGER, context );

	baseTL_storeContextData( stateData, context, (void*)pmiContextData );

	/* Send the command */
	forwardXmlCommand( stateData, getPmiCmd );

	/* Start the timeout thread */
	baseTL_startTimeoutThread( (generic_wait_context_t*)pmiContextData );

	/* Wait for the command to complete */
	tptp_waitSemaphore( &pmiContextData->completionSemaphore );
	
	/* Save the port value */
	port = pmiContextData->port;

	/* Only free the context data in the timeout case  */
	/* Otherwise, the timeout thread itself will do it */
	if ( pmiContextData->status == BASETL_STATUS_TIMEOUT )
	{
		/* Free the context data  */
		tptp_deleteSemaphore( &pmiContextData->completionSemaphore );

		/* Remove the context data from the table, this will delete the data */
		baseTL_releaseContextData( stateData, context );

	}

	return port;
}

tptp_int32 configAgentMetadata(tptp_string* logFile, tptp_string* dataChannelSize, tptp_string* client, agent_t* agent) 
{
	if ((logFile && dataChannelSize && client) == 0) {
		return -1;
	}

	if(strcmp("HEADLESS", client)==0) {
		agent->clientMode = RA_HEADLESS;
	}
	else {
		agent->clientMode = RA_DYNAMIC;
	}

	if (strcmp(dataChannelSize, "NONE") != 0) {
		int lastcharoffs = strlen(dataChannelSize) - 1;
		int index = strspn(dataChannelSize,"0123456789");
		ra_uint_t tmpsize;

		if (index < lastcharoffs) {
			tmpsize = 0; /* an invalid value was specified so set the size to zero */
		}
		else if (*(dataChannelSize + lastcharoffs) == 'M') {
			*(dataChannelSize + lastcharoffs) = '\0'; /* calculate the number of bytes from the megabyte value specified */
			tmpsize = atoi(dataChannelSize) << 20;
		}
		else if (*(dataChannelSize + lastcharoffs) == 'K') {
			*(dataChannelSize + lastcharoffs) = '\0'; /* calculate the number of bytes from the kilobyte value specified */
			tmpsize = atoi(dataChannelSize) << 10;
		}
		else if (index == lastcharoffs) {
			tmpsize = 0; /* some other non-numeric character is the last character so set the size to zero */
		}
		else {
			tmpsize = atoi(dataChannelSize);
		}
		agent->IPCBufSize = tmpsize;
	}
	else {
		agent->IPCBufSize = 0;
	}
	/* logFile */
	if (strcmp(logFile, "NONE") != 0) {
		ra_createRASTRING(&agent->logFile, logFile);
	} else {
		agent->logFile.data = NULL;
		agent->logFile.length = 0;
	}
	return 0;
}

tptp_int32 handleAgentRegistered( tl_state_data_t* stateData, agent_t* agent, const tptp_list_t* propertyList, const tptp_string* agentConfigData )
{
	int                   result;
	ra_message_t*         message;
	ra_command_t*         command;

	/* Bug 96496 begins */
	int   portNum;
	char *port; /* String indicating the RAC server port used */
	char *portName; /* AgentConfigurationEntry name */
	char *portType; /* AgentConfigurationEntry type */
	char *portValue; /* AgentConfigurationEntry value */
	tptp_string* uuid;

	portNum = getPeerMonitoringPort( stateData, agent );

	port = (char*)tptp_malloc(sizeof(char) * 16);
	BZERO(port, 16);
	itoa(portNum, port, 10);
	#ifdef MVS
		portName = (char*)tptp_malloc(strlen("acPort") + 1);
		portType = (char*)tptp_malloc(strlen("configuration") + 1);
		portValue = (char*)tptp_malloc(strlen(port) + 1);

		strcpy(portName, "acPort");
		strcpy(portType, "configuration");
		strcpy(portValue, port);
	#else
		native2unicode(&portName, "acPort", 6);
		native2unicode(&portType, "configuration", 13);
		native2unicode(&portValue, port, strlen(port));
	#endif
	tptp_free(port);
	/* Bug 96496 ends */																										 
	/* Send the configuration information to the agent */
	message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command=ra_addCommandToMessage(message, NULL);
	command->tag=RA_AGENT_CONFIGURATION;
	command->info.agent_configuration.context=0;
	command->info.agent_configuration.processId=agent->process->pid;

	//Ask the process controller for a uuid for this process.
	getProcessUUID(stateData, agent, agent->process->pid, &uuid);

	//If we are being passed agent config information then we parse it.
	//Otherwise we default to the standard agent configuration.
	if (agentConfigData != NULL)
	{
		tptp_string * logFile;
		tptp_string * dataChannelSize;
		tptp_string * client;

		tptp_int32 ret;

		ret = getXmlFragmentElementString( agentConfigData, "agentConfigData", "logFile", &logFile );
		ret = getXmlFragmentElementString( agentConfigData, "agentConfigData", "dataChannelSize", &dataChannelSize );
		ret = getXmlFragmentElementString( agentConfigData, "agentConfigData", "client", &client );

		ret = configAgentMetadata(logFile, dataChannelSize, client, agent);
		if (ret == -1) { // Then use the default configuration.
			agent->IPCBufSize = 0;   /* 198757 */
			agent->clientMode=RA_DYNAMIC; /* 215066 */
			agent->logFile.data = NULL;
			agent->logFile.length = 0;
		}
	} else {
		TPTP_LOG_INFO_MSG1(stateData, "Agent not found %s \n", agent->agentName.data);
		agent->IPCBufSize = 0;   /* 198757 */
		agent->clientMode=RA_DYNAMIC; /* 215066 */
		agent->logFile.data = NULL;
		agent->logFile.length = 0;
	}

	if (agent->logFile.length != 0) 
	{
		//start shared memory log file
		createLogfile(stateData, agent);
	}

	ra_createRASTRING(&command->info.agent_configuration.processUUID, uuid);
	tptp_free(uuid);
	ra_copyRASTRING(&command->info.agent_configuration.agent, &agent->agentName);
	ra_copyRASTRING(&command->info.agent_configuration.agentUUID, &agent->agentUUID);
	ra_copyRASTRING(&command->info.agent_configuration.agentType, &agent->agentType);
	ra_copyRASTRING(&command->info.agent_configuration.nodeUUID, getNodeUUID() ); /* AK - The nodeUUID was embedded in ra_getApplicationModel, but for the compatibility TL, I moved it out */

	/* Convert the properties to the format the agen expects */
	command->info.agent_configuration.configuration.length=propertyList->count;
	if(command->info.agent_configuration.configuration.length) {
		tptp_node_t* node;
		unsigned int i;
		int numBasicNVPairs = 1; /* Number of basic name-value pairs, for now just one for port number */

#ifdef _AIX
		(command->info.agent_configuration.configuration.data)=(ra_agentConfigEntry_t**)tptp_malloc(sizeof(ra_agentConfigEntry_t*) * (command->info.agent_configuration.configuration.length + numBasicNVPairs)); /* one more for RAC port number */
#elif _HPUX || MVS || __OS400__
		(command->info.agent_configuration.configuration.data)=(void**)tptp_malloc(sizeof(ra_agentConfigEntry_t*) * (command->info.agent_configuration.configuration.length + numBasicNVPairs));
#else
		command->info.agent_configuration.configuration.data = (ra_agentConfigEntry_t**)tptp_malloc(sizeof(ra_agentConfigEntry_t*) * (command->info.agent_configuration.configuration.length + numBasicNVPairs));
#endif
		i = 0;
		for ( node = propertyList->head; node != NULL; node = node->next ) {
			char *name;
			char *type;
			char *value;

			if ( 0 == parsePropertyListEntry( (char*)node->data, &name, &type, &value ) )
			{
				((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]=(ra_agentConfigEntry_t*)tptp_malloc(sizeof(ra_agentConfigEntry_t));
				ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]->type, type);
				ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]->name, name);
				ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]->value, value);
				i++;

				tptp_free( name );
				tptp_free( type );
				tptp_free( value );
			}
		}

		/* Add the basic name-value pairs */
		/* 0: RAC port */
		((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]=(ra_agentConfigEntry_t*)tptp_malloc(sizeof(ra_agentConfigEntry_t));
		ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]->type, portType);
		ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]->name, portName);
		ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[i]->value, portValue);
		i++;

		/* Reset the array length (in case any of our parses failed) */
		command->info.agent_configuration.configuration.length=i;
	}
	else {
		/* If no agent specific config defined, just pass the basic name-value pairs such as RAC port */
		command->info.agent_configuration.configuration.length = 1;
#ifdef _AIX
		(command->info.agent_configuration.configuration.data)=(ra_agentConfigEntry_t**)tptp_malloc(sizeof(ra_agentConfigEntry_t*)); /* one more for RAC port number */
#elif _HPUX || MVS || __OS400__
		(command->info.agent_configuration.configuration.data)=(void**)tptp_malloc(sizeof(ra_agentConfigEntry_t*));
#else
		command->info.agent_configuration.configuration.data = (ra_agentConfigEntry_t**)tptp_malloc(sizeof(ra_agentConfigEntry_t*));
#endif
		/* 0: RAC port */
		((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[0]=(ra_agentConfigEntry_t*)tptp_malloc(sizeof(ra_agentConfigEntry_t));
		ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[0]->type, portType);
		ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[0]->name, portName);
		ra_createRASTRING(&((ra_agentConfigEntry_t**)command->info.agent_configuration.configuration.data)[0]->value, portValue);

	}

	result = ra_forwardMessage( message, agent );
	if(result<=0) {
		TPTP_LOG_SEVERE_MSG2( stateData, "Failed writing configuration to agent %s.  Platform error=%d", agent->agentUUID.data, result);
	}
	else {
		TPTP_LOG_DEBUG_MSG1( stateData, "Wrote configuration to agent %s", agent->agentUUID.data);
	}


	/* Close the connection to the agent */
	/* RJD- do not close the named pipe to the agent - we leave it open for the duration
	   of the agent's lifetime */ 
	/* ra_closeNamedPipe(connection->connection.handle.pipe); */ 

	/* If there is a client attached to this new agent inform it is active */
	if(agent->attached && agent->client) {
		/* TODO: Notify the client that this agent is available -- AK compatibility work - will this happen? */
	}
	else if(agent->clientMode==RA_HEADLESS && !agent->attached) { /* 215066 */
		ra_startHeadlessMonitor(stateData, agent);
	} /* 215066 */

	return 0;
}


