/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation, IBM.
 * 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: ProcessImpl.cpp,v 1.21 2009/05/20 03:35:12 kchan Exp $
 *
 *******************************************************************************/

#include "tptp/TPTPSupportTypes.h"
#include "ProcessImpl.h"
#include "tptp/TPTPCommand.h"
#include "tptp/NoLog.h"
#include "tptp/TPTPCommon.h"
#include "tptp/TPTPUtils.h"
#include "ConsoleDataProcessor.h"
#include <stdlib.h>

using namespace TPTP::Client;
using namespace std;

	
ProcessImpl::ProcessImpl(AgentController* ac, char* executable, char* parameters)
{
	_ac = ac;
	//_node = ac->getNode();
	_processControllerID = -1;
	_console = 0;
	_processInfo = new ProcessInfo();
	_processInfo->setExecutable(executable);
	_processInfo->setParameters(parameters);
}


ProcessImpl::~ProcessImpl()
{
	_listeners.clear();
	_variables.clear();
	if (_console) delete(_console);
	if (_processInfo) delete(_processInfo);
}



void ProcessImpl::launch() 
{
	ConsoleDataProcessor* consoleProcessor = 0;

	if(_ac==NULL)
	{
		return;
		//throw new NotConnectedException();
	}

	
	if (_processControllerID == -1)
	{
		Agent* processController = new Agent("ProcessController");
		_ac->getAgent(processController, TPTP_CONTROLLER_ACCESS);
		_processControllerID = processController->getAgentID();
	}
	
	
	TPTPCommand* LaunchProcessCommand = new TPTPCommand();
	LaunchProcessCommand->setSourceID(_ac->getConnection()->getConnectionId());
	LaunchProcessCommand->setDestID(this->_processControllerID);
	LaunchProcessCommand->setContextID(_ac->getConnection()->getNextContextID());
	LaunchProcessCommand->setIID("org.eclipse.tptp.processController");
	LaunchProcessCommand->setCommandName("startProcess");
	//Add environment
	// Create launchInfo string
	// Currently just take the entire set of cmdLineArgs and treats it as a group.
	// Env vars are being ignored.
	// TODO: Provide interface for treating args and env vars as individuals and specifying position
	char launchStr[2048];  //TODO: Need to make sure exe+loc+args+env will fit
	if (_processInfo && _processInfo->getParameters())
	{
		char argsStr[1024]; //TODO: Need to make sure value will fit..
		sprintf(argsStr, "<Parameter position=\"append\" value=\"%s\" />",
						 _processInfo->getParameters());
		sprintf(launchStr, "<Application executable=\"%s\" location=\"%s\"> %s </Application>",
						_processInfo->getExecutable(), _processInfo->getWorkingDirectory(), argsStr);
	} else
	{
		sprintf(launchStr, "<Application executable=\"%s\" location=\"%s\"></Application>",
						_processInfo->getExecutable(), _processInfo->getWorkingDirectory());
	}

	LaunchProcessCommand->addStringParam("launchInfo", launchStr);
//	LaunchProcessCommand->addStringParam("applicationName", _exe);
//	LaunchProcessCommand->addStringParam("commandLineArgs", _param);
//	LaunchProcessCommand->addStringParam("workingDir", _location);	
	//LaunchProcessCommand->addStringParam("environmentVars", "");
	int consoleDataConnectionID = _ac->getConsoleDataConnection();

	//LaunchProcessCommand->addStringParam("keepRunning", "No");
	//LaunchProcessCommand->addStringParam("noNotices", "No");
	if (_console != NULL && _console->getDataProcessor() != NULL)
	{
		_console->setDataConnectionID(consoleDataConnectionID);
		consoleProcessor = (ConsoleDataProcessor*)_ac->getConsoleDataProcessor();		
		_ac->addDataListener(consoleDataConnectionID, consoleProcessor);
		LaunchProcessCommand->addIntegerParam("consoleConnectID", consoleDataConnectionID);
	}

	PCCommandHandler* pcCommandHandler = new PCCommandHandler();

	try 
	{
		_ac->getConnection()->sendMessage(LaunchProcessCommand, pcCommandHandler);

	}
	catch(exception e) 
	{
		// Should probably handle this one
		//cout<<"Exception in getAgent():"<<e<<endl;
	}

	int rc = tptp_waitSemaphore(pcCommandHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Error - wait for command response failed.");
	};
	if (pcCommandHandler->checkResponseReceived() == 4)
	{
		//TODO:Throw an exception here
		delete(LaunchProcessCommand);
		delete(pcCommandHandler);
		return;
	}

	if(pcCommandHandler->checkResponseReceived() == 1)
	{
		

		CommandElement* cmdElement = pcCommandHandler->getResponseCommand();

		int            ret = -1;
		int            sourceID;
		int            contextID;
		char*          interfaceName;
		char*          cmdName;
		tptp_list_t*   paramList;

		ret = parseCommand(cmdElement->getCommand(), &sourceID, &contextID, &interfaceName, &cmdName, &paramList );

		getStringParam("processID", paramList, &(this->_processId));

		// Free space allocated by parseCommand()
		tptp_free(interfaceName);
		tptp_free(cmdName);
		tptp_list_clear(paramList);
		tptp_free(paramList);

		if (_console != NULL && _console->getDataProcessor() != NULL)
		{
			_console->setProcessID(atol(_processId));			
			consoleProcessor->addDataProcessor(atol(_processId), _console->getDataProcessor());
		}
	}

}



void ProcessImpl::kill() 
{
	if(_ac == NULL)
	{
		return;
	}
	
	try
	{

		TPTPCommand* KillProcessCommand = new TPTPCommand();
		KillProcessCommand->setSourceID(_ac->getConnection()->getConnectionId());
		KillProcessCommand->setDestID(_processControllerID);
		KillProcessCommand->setContextID(_ac->getConnection()->getNextContextID());
		KillProcessCommand->setIID("org.eclipse.tptp.processController");
		KillProcessCommand->setCommandName("stopProcess");
		//Add environment
		KillProcessCommand->addStringParam("processID", _processId);		

		PCCommandHandler* pcCommandHandler = new PCCommandHandler();

		_ac->getConnection()->sendMessage(KillProcessCommand, pcCommandHandler);

		int rc = tptp_waitSemaphore(pcCommandHandler->getResponseReceivedSem());
		if (rc != 0)
		{
			TPTP_LOG_ERROR_MSG("Error - wait for command response failed.");
		};
		//TODO: IF Process Killed set the status of the process to be killed.
		if (pcCommandHandler->checkResponseReceived() == 4)
		{
			//TODO:Throw an exception here
			delete(KillProcessCommand);
			delete(pcCommandHandler);
			return ;
		}

		//CommandElement* cmdElement = pcCommandHandler->getResponseCommand();

		
	}
	catch(exception e)
	{ 

	}
}




bool ProcessImpl::validateProcessToLaunch() 
{
	bool processValid = false;

	if(_ac == NULL)
	{
		//TODO Throw Exception
		return false;
	}
	

	TPTPCommand* ValidateProcessToLaunchCommand = new TPTPCommand();
	ValidateProcessToLaunchCommand->setSourceID(_ac->getConnection()->getConnectionId());
	ValidateProcessToLaunchCommand->setDestID(AGENT_MANAGER);
	ValidateProcessToLaunchCommand->setContextID(_ac->getConnection()->getNextContextID());
	ValidateProcessToLaunchCommand->setIID("org.eclipse.tptp.ProcessController");
	ValidateProcessToLaunchCommand->setCommandName("validateProcessToLaunch");
	//Add environment
	ValidateProcessToLaunchCommand->addStringParam("applicationName", _processInfo->getExecutable());	
	ValidateProcessToLaunchCommand->addStringParam("workingDir", _processInfo->getWorkingDirectory());
	

	PCCommandHandler* pcCommandHandler = new PCCommandHandler();

	try 
	{
		_ac->getConnection()->sendMessage(ValidateProcessToLaunchCommand, pcCommandHandler);

	}
	catch(exception e) 
	{
		// Should probably handle this one
		//cout<<"Exception in getAgent():"<<e<<endl;
	}

	int rc = tptp_waitSemaphore(pcCommandHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Error - wait for command response failed.");
	};
	if (pcCommandHandler->checkResponseReceived() == 4)
	{
		//TODO:Throw an exception here
		delete(ValidateProcessToLaunchCommand);
		delete(pcCommandHandler);
		return processValid;
	}
	if(pcCommandHandler->checkResponseReceived() == 3)
	{
		CommandElement* cmdElement = pcCommandHandler->getResponseCommand();
		int            ret = -1;
		int            sourceID;
		int            contextID;
		char*          interfaceName;
		char*          cmdName;
		int			   validApp = 0;
		int			   validWorkingDir = 0;
		tptp_list_t*   paramList;
	
		ret = parseCommand(cmdElement->getCommand(), &sourceID, &contextID, &interfaceName, &cmdName, &paramList );

		getIntegerParam("validApp", paramList, &validApp);
		getIntegerParam("validWorkingDir", paramList, &validWorkingDir);

		if (validApp == 1 && validWorkingDir == 1)
		{
			processValid = true;
		}

		// Free space allocated by parseCommand()
		tptp_free(interfaceName);
		tptp_free(cmdName);
		tptp_list_clear(paramList);
		tptp_free(paramList);
		return processValid;
	}
	return processValid;
}



void ProcessImpl::addEventListener(char* interfaceID, ICommandHandler* listener) 
{
	char	tempbuf[1024];
	
	//Send the addEventListener command
	TPTP_LOG_DEBUG_MSG("Sending addEventListener command");
	sprintf(tempbuf, "<addEventListener iid=\"org.eclipse.tptp.platform.Agent\"><interfaceID>%s</interfaceID></addEventListener>", interfaceID);
	_ac->sendCommand(tempbuf, _srcID, listener);

}



void ProcessImpl::removeEventListener(char* interfaceID, ICommandHandler* listener) 
{
	char	tempbuf[1024];
	
	//Send the removeEventListener command
	TPTP_LOG_DEBUG_MSG("Sending removeEventListener command");
	sprintf(tempbuf, "<addEventListener iid=\"org.eclipse.tptp.platform.Agent\"><interfaceID>%s</interfaceID></addEventListener>", interfaceID);
	_ac->sendCommand(tempbuf, _srcID, listener);

	return;
}



INode* ProcessImpl::getNode() 
{
	return _node;
}


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



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


void ProcessImpl::setName(char* name) 
{
	_processInfo->setProcessName(name);
}


char* ProcessImpl::getName() 
{
	return _processInfo->getProcessName();
}


char* ProcessImpl::getUUID() 
{
	return _UUID;
}


void ProcessImpl::setExecutable(char* exe) 
{
	_processInfo->setExecutable(exe);
}


char* ProcessImpl::getExecutable() 
{
	return _processInfo->getExecutable();
}


char* ProcessImpl::getProcessId() 
{
	return _processId;
}

void ProcessImpl::setProcessId(char* processId)
{
	_processId = processId;
}


bool ProcessImpl::isActive() 
{
	return _isActive;
}


IConsole* ProcessImpl::getConsole() 
{
	if (_console == 0)
	{
		_console = new ConsoleImpl();
		_console->setAC(this->_ac);
	}

	return _console;
}


void ProcessImpl::setEnvironmentVariable(char* name, char* value) 
{
	_processInfo->addEnvironmentVariable(name, value);
}



char* ProcessImpl::getEnvironmentVariable(char* name) 
{
	char* envVal = (char*)_processInfo->getEnvironmentVariable(name);
	
	return envVal;
}



void ProcessImpl::removeEnvironmentVariable(char* name)
{
	_processInfo->removeEnvironmentVariable(name);

	return;
}


//Enumeration ProcessImpl::getEnvironment() 
//{
//	return _variables.elements();
//}


void ProcessImpl::setParameter(char* parameters) 
{
	_processInfo->setParameters(parameters);
}


char* ProcessImpl::getParameter() 
{
	return _processInfo->getParameters();
}


void ProcessImpl::setLocation(char* location) 
{
	_processInfo->setWorkingDirectory(location);
}



char* ProcessImpl::getLocation() 
{
	return _processInfo->getWorkingDirectory();
}



ProcessInfo* ProcessImpl::getProcessInfo() 
{
	return _processInfo;
}



void ProcessImpl::setProcessInfo(ProcessInfo* pInfo) 
{
	if (_processInfo) delete (_processInfo);
	_processInfo = pInfo;
}

void ProcessImpl::setSource(int clientSrcID)
{
	_srcID = clientSrcID;	
}



void ProcessImpl::connectionClosed(IConnection* connection) 
{
	// TODO implement the event handler
	return;
}



void ProcessImpl::processLaunched(IProcess* process) {	
	//TODO implement the event handler
	
}



void ProcessImpl::processExited(IProcess* process) {
	//TODO implement the event handler
}


PCCommandHandler::PCCommandHandler()
{
	this->responseReceived = -1;
	responseCommand = NULL;
	int rc = tptp_initializeSemaphore(&responseReceivedSem);
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("PCCommandHandler: Error - failed to create synch event for responseReceived.");		
	}
}

PCCommandHandler::~PCCommandHandler()
{
	tptp_deleteSemaphore(&responseReceivedSem);
}

CommandElement* PCCommandHandler::getResponseCommand()
{
	return this->responseCommand;
}

void PCCommandHandler::incomingCommand(INode* node, CommandElement* command)
{
	int            ret = -1;
	int            sourceID;
	int            contextID;
	char*          interfaceName;
	char*          cmdName;
	tptp_list_t*   paramList = NULL;

	TPTP_LOG_DEBUG_MSG1("PCCommandHandler: Incoming command: %s", command->getCommand());
	responseCommand = command;

	ret = parseCommand(command->getCommand(), &sourceID, &contextID, &interfaceName, &cmdName, &paramList );

	TPTP_LOG_DEBUG_MSG1("PCCommandHandler: Command name: %s", cmdName);

	if (isEqualString(cmdName, "processStarted"))
	{
		responseReceived = 1;
	}
	if (isEqualString(cmdName, "processExitedEvent"))
	{
		responseReceived = 2;
	}
	if (isEqualString(cmdName, "processValidationResults"))
	{
		responseReceived = 3;
	}
	if (isEqualString(cmdName, "TPTP_Error"))
	{
		responseReceived = 4;
		char *errStr=NULL;
		getStringParam("ErrInfo", paramList, &errStr);
		cout<<"Received Error Response:"<<errStr<<endl;
		if (errStr)tptp_free(errStr);
	}

	int rc = tptp_postSemaphore(&responseReceivedSem);
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Error - failed to post event for responseReceived event.");
	}

	// Free space allocated by parseCommand()
	tptp_free(interfaceName);
	tptp_free(cmdName);
	tptp_list_clear(paramList);
	tptp_free(paramList);
}


int PCCommandHandler::checkResponseReceived()
{
	return responseReceived;
}

Semaphore_t* PCCommandHandler::getResponseReceivedSem()
{
	return &responseReceivedSem;
}





