/*******************************************************************************
 * Copyright (c) 2005, 2006 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
 *    IBM - Portions of code moved from older RAC (by Andy Kaylor)
 *
 * $Id: RACmdHandlers.c,v 1.18 2006/11/02 18:58:08 akaylor Exp $
 *
 *******************************************************************************/ 

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

/* 
 * This file provides the code to handle commands that are sent from the agent
 *   to the RAC.  The handlers here emulate the behavior of the RAC in their
 *   communications with the agent while translating these commands into the
 *   necessary equivalents to provide the same functionality using the
 *   new Agent Controller.
 */

BOOL handleAgentScopingCommand( tl_state_data_t* stateData, ra_command_t *command )
{
	int result;
	process_t* process;
	agent_t *agent;
	HANDLE pipe_result;

	TPTP_LOG_INFO_MSG3( stateData, "Incoming connection request from agent %s in process %lu with uuid %s", command->info.agent_scoping_information.agent.data, command->info.agent_scoping_information.processId, command->info.agent_scoping_information.agentUUID.data);
	process=ra_processFind(ra_getActiveProcessList(), command->info.agent_active.processId);
	/* Create the agent object */
	if(!process) {
		process=ra_processCreate(ra_getActiveProcessList());
		process->pid=command->info.agent_scoping_information.processId;
/* BEGIN:  235649 */
#if defined __linux__
		process->pid2=command->info.agent_scoping_information.messageProcessId;
#endif
/* END: 235649 */
		process->isActive=TRUE;

		/* Was a processUUID provided by the agent? */
		if(command->info.agent_active.processUUID.length) {
			ra_copyRASTRING(&process->uuid, &command->info.agent_scoping_information.processUUID);
		}
		/* Create the agent */
		agent=ra_agentCreate(process, TRUE);
		if ( agent == NULL )
		{
			TPTP_LOG_ERROR_MSG1( stateData, "Error creating agent object for agent: %s.", command->info.agent_scoping_information.agent.data );
			return FALSE;
		}
		agent->attached=FALSE;
	}
	else {
/* BEGIN:  235649 */
#if defined __linux__
		process->pid2=command->info.agent_scoping_information.messageProcessId;
#endif
/* END: 235649 */

		/* Look for the agent object in the inactive list for the process */
		agent=ra_agentFindByProcess(process, &command->info.agent_scoping_information.agent, FALSE);

		/* If the agent exists, make it active */
		if(agent) {
			ra_agentMakeActive(agent);
		}
		else {
			/* Create the agent */
			agent=ra_agentCreate(process, TRUE);
			if ( agent == NULL )
			{
				TPTP_LOG_ERROR_MSG1( stateData, "Error creating agent object for agent: %s.", command->info.agent_scoping_information.agent.data );
				return FALSE;
			}
			agent->attached=FALSE;
			ra_copyRASTRING(&agent->agentName, &command->info.agent_scoping_information.agent);
			ra_copyRASTRING(&agent->agentUUID, &command->info.agent_scoping_information.agentUUID);
			ra_copyRASTRING(&agent->agentType, &command->info.agent_scoping_information.agentType);
		}
	}
	ra_copyRASTRING(&agent->agentName, &command->info.agent_scoping_information.agent);
	ra_copyRASTRING(&agent->agentUUID, &command->info.agent_scoping_information.agentUUID);
	ra_copyRASTRING(&agent->agentType, &command->info.agent_scoping_information.agentType);
	TPTP_LOG_INFO_MSG1( stateData, "Registered Agent %s.", agent->agentUUID.data );

	/* Open the named pipe provided by the agent */
	/* RJD - open the pipe only once on connect from the agent, and keep trying
	   as long as the agent process is alive */ 
	/* AK - The following loop doesn't actually loop if the open pipe call fails, because
	        pipe_result is a HANDLE and thus can never be less than zero.  We could fix it
	        by cast pipe_result as an int before we do the comparison, but we want to
	        duplicate the RAC behavior so I didn't fix it.  We may want to revisit this */
	while ( (pipe_result=openWriteOnlyNamedPipe(RA_PIPE_NAMESPACE, agent->agentUUID.data, FALSE)) < 0 
			&& isProcessActive(process->pid) 
#if defined __linux__
			&& isProcessActive(process->pid2)
#endif
			) {
		SLEEP(100); /* sleep before connect reattempt so as not to create tightly spinning loop
						that consumes CPU */ 
	}

	/* set the agent's pipe to be the one we just opened */ 
	agent->pipe = pipe_result; 

	/* If we could not connect to the agent's pipe, we will go back into waiting state */
	if(pipe_result < 0) { /* 46651 */
		TPTP_LOG_SEVERE_MSG2( stateData, "Error connecting to agent %s platform specific error is %d", agent->agentUUID.data, pipe_result );
		return FALSE;
	}

	/* Create a connection with the Agent Controller corresponding to this agent */
	result = registerAgent( stateData, agent );
	if ( result < 0 )
	{
		/* Any errors were already logged by our registerAgent function */
		return FALSE;
	}

	/* AK -- The processing will continue in handleAgentRegistered (when the AC responds). */
	/*       The RA_AGENT_CONFIGURATION command will be sent at that time                  */

	return TRUE;
}

BOOL handleAgentInactive( tl_state_data_t* stateData, ra_command_t* command )
{
	agent_t *agent;

	agent=ra_agentFindByUUID(ra_getActiveProcessList(), &command->info.agent_inactive.agentUUID, TRUE);

	/* Did the active agent exist? */
	if(agent==NULL) {
		return FALSE;
	}

	/* If we're still processing data from this agent, wait here until that finishes */
	if ( agent->isProcessingData ) {
		tptp_waitSemaphore( &agent->dataSemaphore );
		/* The waitForDataFlush handler may be waiting too */
		tptp_postSemaphore( &agent->dataSemaphore );
	}

	TPTP_LOG_INFO_MSG1( stateData, "Deregistering agent %s.", agent->agentUUID.data);

	/* Call our handler to notify the AC */
	/* The AC will notify the CCTL which does the other half of the clean up */
	deregisterAgent( stateData, agent );

	cleanPipeUp( &(agent->pipe) );

	/* Make the agent inactive */
	ra_agentMakeInactive(agent); /* Bug 71586 */

	/* If there are no more active agents for this process, determine if the process exited */
	if(agent->process->active_agent_count==0) {
		TPTP_LOG_DEBUG_MSG1( stateData, "No more active agents in process %d so scrub it", agent->process->pid );
	}

	return TRUE;
}

BOOL handleGetPropertyList( tl_state_data_t* stateData, ra_command_t* command )
{ 
	char        cmdFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><getPropertyList iid=\"org.eclipse.tptp.agentManager\"><name>%s</name><type>%s</type></getPropertyList></Cmd>";
	char*       cmd;
	int         cmdSize;
	tptp_uint32 context;
	actl_get_proplist_context_t* getPropertyListContextData;
	agent_t*    agent;

	agent=ra_agentFindByUUID(ra_getActiveProcessList(), &command->info.query_property_list.agentUUID, TRUE);

	if ( agent == NULL )
	{
		return FALSE;
	}

	/* Calculate the size of the command we're building */
	cmdSize = strlen(cmdFormat) + 24 
	            + command->info.query_property_list.name.length 
	            + command->info.query_property_list.type.length;

	/* Allocate memory for the command */
	cmd	= tptp_malloc( cmdSize );
	if ( cmd == NULL )
	{
/*		sendErrorMessage("RAC003",
						 "Memory allocation error",
						 command->info.query_property_list.context,
						 agent); */
		return FALSE;
	}

	/* Format the command */
	context = baseTL_getNextContextID();
	sprintf( cmd, cmdFormat, agent->agentID, AGENT_MANAGER, context, 
		                     command->info.query_property_list.name.data,
	                         command->info.query_property_list.type.data );

	/* Save our context relative to the client */
	getPropertyListContextData = (actl_get_proplist_context_t*)tptp_malloc(sizeof(actl_get_proplist_context_t));
	if ( getPropertyListContextData == NULL )
	{
		tptp_free(cmd);
/*		sendErrorMessage("RAC003",
						 "Memory allocation error",
						 command->info.query_property_list.context,
						 agent); */
		return FALSE;
	}
	getPropertyListContextData->contextDataType = GET_PROPLIST_CONTEXT_TYPE;
	getPropertyListContextData->agentContext = command->info.query_property_list.context;
	baseTL_storeContextData( stateData, context, (void*)getPropertyListContextData );

	/* Send the command, when we get a response, we will send the response to the client */
	forwardXmlCommand( stateData, cmd );

	return TRUE;
}


BOOL handleAgentRequestMonitor(tl_state_data_t* stateData, ra_command_t* command )
{
	agent_t*   agent;
	process_t* process;
	char       requestFormat[] = "<Cmd src=\"%u\" dest=\"%u\" ctxt=\"%u\"><agentRequestMonitor iid=\"org.eclipse.tptp.ac.peerMonitoring\"><sourceConnectionInfo><transportType>TPTP_CCTL</transportType><host>%u.%u.%u.%u</host><port>%d</port></sourceConnectionInfo><sourceAgentInfo><agentName>%s</agentName><pid>%u</pid></sourceAgentInfo></agentRequestMonitor></Cmd>";
	char*      requestCmd;
	tptp_int32 requestSize;
	tptp_int32 contextID = baseTL_getNextContextID();
	actl_peer_monitoring_context_t* contextData;

	/* Find the agent that sent this request */
	process = ra_processFind(ra_getActiveProcessList(), command->info.agent_request_monitor.processId);

	if(!process) 
	{
		TPTP_LOG_ERROR_MSG1( stateData, "Could not find process %d.  The model may be missing information.", command->info.agent_request_monitor.processId);
		return FALSE;
	}

	agent=ra_agentFindByProcess(process, &command->info.agent_request_monitor.agent, TRUE);

	/* when agent becomes inactive, it is removed from the active list */
	if(agent == NULL) 
	{
		agent=ra_agentFindByProcess(process, &command->info.agent_request_monitor.agent, FALSE);
		if ( agent == NULL )
		{
			TPTP_LOG_ERROR_MSG1( stateData, "Could not find agent %s.  The model may be missing information.", command->info.agent_request_monitor.agent.data );
			return FALSE;
		}
	}


	/* The costants below are for the 3 Cmd attributes (33), the IP address (15), the port (11) and the PID (11) */
	requestSize = strlen(requestFormat) + 33 + 15 + 11 + 11 + agent->agentName.length + 1;
	requestCmd = (char *)tptp_malloc( requestSize );

	if ( requestCmd == NULL )
	{
		/* TODO: Send an error response */
		TPTP_LOG_ERROR_MSG( stateData, "Out of memory while processing agent Peer Monitoring request." );
		return FALSE;
	}

	if ( command->tag == RA_AGENT_REQUEST_MONITOR )
	{
		/* The compiler had trouble convert data[n] (signed char) to and unsigned int for the sprintf, so we're giving it a hint */
		unsigned int data0 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[0];
		unsigned int data1 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[1];
		unsigned int data2 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[2];
		unsigned int data3 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[3];

		sprintf( requestCmd, requestFormat, agent->agentID, AGENT_MANAGER, contextID, 
											data0,
											data1,
											data2,
											data3,
											10002,
											command->info.agent_request_monitor.peerAgent.data,
											command->info.agent_request_monitor.peerProcessId );
	}
	else
	{
		/* The compiler had trouble convert data[n] (signed char) to and unsigned int for the sprintf, so we're giving it a hint */		unsigned int data0 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[0];
		unsigned int data1 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[1];
		unsigned int data2 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[2];
		unsigned int data3 = (unsigned int)(unsigned char)command->info.agent_request_monitor.peerNode.data[3];

		sprintf( requestCmd, requestFormat, agent->agentID, AGENT_MANAGER, contextID, 
											data0,
											data1,
											data2,
											data3,
											command->info.agent_request_monitor_port.port,
											command->info.agent_request_monitor_port.peerAgent.data,
											command->info.agent_request_monitor_port.peerProcessId );
	}

	/* Save the context so we can handle an error response */
	contextData = (actl_peer_monitoring_context_t*)tptp_malloc( sizeof(actl_peer_monitoring_context_t) );
	contextData->sourceAgent = agent;
	contextData->command = ra_cloneCommand( command );
	contextData->contextDataType = REQUEST_MONITOR_CONTEXT_TYPE;

	baseTL_storeContextData( stateData, contextID, (void*)contextData );

	forwardXmlCommand( stateData, requestCmd );

	tptp_free( requestCmd );

	return TRUE;
}

