/*******************************************************************************
 * Copyright (c) 2005 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 implementation
 *
 * $Id: DumpAgent.cpp,v 1.4 2005/09/26 18:38:26 akaylor Exp $ 
 *******************************************************************************/ 

#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>

#include <WinSock.h>
#include <stdio.h>

#include "RABindings.h"
#include "RAShm.h"
#include "RADataTransfer.h"

// forward declarations
RA_AGENT_HANDLE registerAgent( char* name, char* type );
static void vtMessageHandler(ra_command_t *command);
void waitForCommands();
DWORD WINAPI dataPump( LPVOID param );

// Global data
RA_AGENT_HANDLE       this_agentHandle;
char                  strConnect[64] = "Hello client!  We are not yet connected!";
ra_data_target_hdl_t  targetHdl;
BOOL                  isMonitored = FALSE;

// Process entry point
//int APIENTRY WinMain(HINSTANCE hInstance,
//                     HINSTANCE hPrevInstance,
//                     LPSTR     lpCmdLine,
//                     int       nCmdShow)
int main(int argc, char* argv[])
{
	printf( "DumpAgent starting up.\n" );

 	this_agentHandle = registerAgent( "DumpAgent", "RACTest" );

	waitForCommands();

	return 0;
}

/////////////////////////////////////////////////////////////////////////
//
// registerAgent
//
// This method will initialize the agent and connect it to the RAC
//

RA_AGENT_HANDLE registerAgent( char* name, char* type )
{
	RA_AGENT_HANDLE agentHandle;


	// Ask the RABindings DLL to create and initialize an RA_AGENT_HANDLE struct
	agentHandle = ra_initializeBindings( name, type, vtMessageHandler, FALSE );

	// Connect this agent to the RAC
	ra_startListener( agentHandle, 0 );

	// TODO: Clean-up any mess we made

	return agentHandle;
}

static void vtMessageHandler(ra_command_t *command)
{
	char strDump[512] = "";

	if ( command == NULL )
		return;

	switch ( command->tag )
	{
		// These are commands which may have come from the client
		case RA_AUTHENTICATE:
            strcpy( strDump, "RA_AUTHENTICATE" );
            break;
		case RA_LAUNCH_PROCESS:
            strcpy( strDump, "RA_LAUNCH_PROCESS" );
            break;
		case RA_KILL_PROCESS:
            strcpy( strDump, "RA_KILL_PROCESS" );
            break;
		case RA_QUERY_PROCESS_LIST:
            strcpy( strDump, "RA_QUERY_PROCESS_LIST" );
            break;
		case RA_QUERY_AGENT_LIST:
            strcpy( strDump, "RA_QUERY_AGENT_LIST" );
            break;
		case RA_QUERY_AGENT_DETAILS:
            strcpy( strDump, "RA_QUERY_AGENT_DETAILS" );
            break;
		case RA_REGISTER_AGENT_NOTIFICATION:
            strcpy( strDump, "RA_REGISTER_AGENT_NOTIFICATION" );
            break;
		case RA_ATTACH_TO_AGENT:
            strcpy( strDump, "RA_ATTACH_TO_AGENT" );
            break;
		case RA_DETACH_FROM_AGENT:
            strcpy( strDump, "RA_DETACH_FROM_AGENT" );
            break;
		case RA_START_MONITORING_AGENT_REMOTE:
			if ( !isMonitored )
			{
	            strcpy( strDump, "RA_START_MONITORING_AGENT_REMOTE" );

                targetHdl.dtarget = RA_SOCKET;
                int result = ra_connectToTCPServer( command->info.start_monitor_remote.ip,
                                                    (unsigned short)command->info.start_monitor_remote.port,
                                                    &targetHdl.dtargetHdl.socketFD );
				if ( result < 0 )
				{
					strcpy( strConnect, "Failed to connect to socket!" );
				}
				else
				{
					strcpy( strConnect, "Hello Hyades!  We are connected via socket." );
					CreateThread( NULL, 0, dataPump, &targetHdl, 0, NULL );
					isMonitored = TRUE;
				}
			}
            break;
		case RA_START_MONITORING_AGENT_LOCAL:
			if ( !isMonitored )
			{
	            sprintf( strDump, "RA_START_MONITORING_AGENT_LOCAL: agent = %s, file = %s, context = %ld, processID = %ld", 
					              command->info.start_monitor_local.agent.data,
								  command->info.start_monitor_local.file.data,
								  (long)command->info.start_monitor_local.context,
								  (long)command->info.start_monitor_local.processId );

                targetHdl.dtarget = RA_SHAREDMEMORY;
	            int result = ra_attachToShm( command->info.start_monitor_local.file.data,
		                                     &targetHdl.dtargetHdl.shmHdl);
				if ( result < 0 )
				{
					strcpy( strConnect, "Failed to connect to shared memory!" );
				}
				else
				{
					strcpy( strConnect, "Hello Hyades!  We are connected via shared memory." );
					CreateThread( NULL, 0, dataPump, &targetHdl, 0, NULL );
					isMonitored = TRUE;
				}
			}
			else
			{
				strcpy( strConnect, "Hello Hyades!  We were already connected." );
			}
            break;
		case RA_STOP_MONITORING_AGENT:
            strcpy( strDump, "RA_STOP_MONITORING_AGENT" );
			isMonitored = FALSE;
			if( targetHdl.dtarget==RA_SOCKET ) 
			{
				ra_closeSocket(targetHdl.dtargetHdl.socketFD);
			}
			else if(targetHdl.dtarget==RA_SHAREDMEMORY) 
			{
				ra_stopFlushingShm(&targetHdl.dtargetHdl.shmHdl);
			}
            break;
		case RA_SET_NAME_VALUE_PAIR:
            strcpy( strDump, "RA_SET_NAME_VALUE_PAIR" );
            break;
		case RA_CUSTOM_COMMAND:
			{
				ra_message_t *responseMessage;
				ra_command_t *responseCommand;

				// This could be RA_ACK, but let's try it this way
				responseMessage=ra_createMessage(RA_CONTROL_MESSAGE, 0);

				// Fill out a custom command
				responseCommand=ra_addCommandToMessage(responseMessage, NULL);
				responseCommand->tag=RA_CUSTOM_COMMAND;
				responseCommand->info.custom_command.context=command->info.custom_command.context;
				responseCommand->info.custom_command.processId=command->info.custom_command.processId;
				ra_copyRASTRING(&responseCommand->info.custom_command.agent, &this_agentHandle->agentName);
				ra_createRASTRING( &responseCommand->info.custom_command.message, strConnect );

				// Send the message
				ra_sendMessage( this_agentHandle, responseMessage );

				// log our activity
				sprintf( strDump, "RA_CUSTOM_COMMAND: message = %s", command->info.custom_command.message.data );

			}
            break;

		// These aren't actual commands, but are simulated by the binding services
		case RA_AGENT_CONTROLER_AVAILABLE:
            strcpy( strDump, "RA_AGENT_CONTROLER_AVAILABLE" );
            break;
		case RA_AGENT_CONTROLER_UNAVAILABLE:
            strcpy( strDump, "RA_AGENT_CONTROLER_UNAVAILABLE" );
            break;

		default:
			sprintf( strDump, "Unexpected command: %ld", command->tag );
			break;
	}

	strcat( strDump, "\n" );
	printf( strDump );
}

void waitForCommands()
{
	HANDLE killEvent = CreateEvent( NULL, TRUE, FALSE, "DumpAgentKillEvent" );

	// The actuall command processing takes place in a thread started by the
	//   RA binding layer.  We're just waiting to be told we're done.
	WaitForSingleObject( killEvent, INFINITE );
}

DWORD WINAPI dataPump( LPVOID param )
{
	unsigned short idx;
	unsigned short length = 26;
	unsigned char *buffer = ra_allocateMessageBlock( length );

	for ( idx = 0; idx < length; idx++ )
	{
		buffer[idx] = (unsigned char)('a' + idx);
	}

	ra_data_target_hdl_t *pTarget = (ra_data_target_hdl_t *)param;

	idx = 0;
	while ( isMonitored )
	{
		ra_writeMessageBlock( pTarget, RA_BINARY_DATA, buffer, length);
		Sleep( 10 );

		idx++;

		if ( idx > 5000 )
		{
			idx = 0;

			ra_message_t *eventMessage;
			ra_command_t *eventCommand;

			// This could be RA_ACK, but let's try it this way
			eventMessage=ra_createMessage(RA_CONTROL_MESSAGE, 0);

			// Fill out a custom command
			eventCommand=ra_addCommandToMessage(eventMessage, NULL);
			eventCommand->tag=RA_CUSTOM_COMMAND;
			eventCommand->info.custom_command.context=0;
			eventCommand->info.custom_command.processId=ra_getProcessId();
			ra_copyRASTRING(&eventCommand->info.custom_command.agent, &this_agentHandle->agentName);
			ra_createRASTRING( &eventCommand->info.custom_command.message, "Event message" );

			// Send the message
			ra_sendMessage( this_agentHandle, eventMessage );
		}
	}

	return 0;
}
