/*******************************************************************************
 * 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 (Based on the Java
 *                                API implementation by IBM)
 *
 * $Id: Agent.cpp,v 1.32 2009/04/14 20:33:26 jwest Exp $
 *
 *******************************************************************************/ 


#include "tptp/client/ControlMessage.h"
#include "tptp/client/CommandFragment.h"
#include "GetAgentCommand.h"
#include "ConnectionImpl.h"
#include "tptp/client/Constants.h"
#include "tptp/TPTPSupportTypes.h"
#include "tptp/TPTPUtils.h"
#include "tptp/NoLog.h"
#include "tptp/client/Agent.h"

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

using namespace std;
using namespace TPTP::Client;

AgentCommandHandler::AgentCommandHandler()
{
	this->responseReceived = -1;
	responseCommand = NULL;
	int rc = tptp_initializeSemaphore(&responseReceivedSem);
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("AgentCommandHandler: Error - Exiting, failed to create sync event for response receive.");		
	};
}

AgentCommandHandler::~AgentCommandHandler()
{
	if (responseCommand)
	{ 
		delete(responseCommand);
	}
	tptp_deleteSemaphore(&responseReceivedSem);
}				

void AgentCommandHandler::incomingCommand(INode* node, CommandElement* command)
{
	TPTP_LOG_DEBUG_MSG1("AgentCommandHandler: Incoming command from Agent : %s", command->getCommand());

	responseCommand = new CommandFragment();
	responseCommand->setContext(command->getContext());
	responseCommand->setSource(command->getSource());
	responseCommand->setDestination(command->getDestination());
	responseCommand->setCommand(command->getCommand());

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

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

	if (isEqualString(cmdName, "dataPathEstablished"))
	{
		responseReceived = 1;
	}
	//else if(isEqualString(cmdName, "agentRunning"))
	//{
	//	responseReceived = 2;
	//}
	else if(isEqualString(cmdName, "controlGranted"))
	{
		responseReceived = 3;
	}
	else if(isEqualString(cmdName, "listenerAccepted"))
	{
		responseReceived = 4;
	}
	else if(isEqualString(cmdName, "listenerRemoved"))
	{
		responseReceived = 5;
	}
	//else if(isEqualString(cmdName, "VariableList"))
	//{
	//	responseReceived = 6;
	//}
	//else if(isEqualString(cmdName, "VariableGroupList"))
	//{
	//	responseReceived = 7;
	//}
	else if(isEqualString(cmdName, "VariableGroupList"))
	{
		responseReceived = 8;
	}
	else if(isEqualString(cmdName, "VariableList"))
	{
		responseReceived = 9;
	}
	else if(isEqualString(cmdName, "setVariableGroupSuccess"))
	{
		responseReceived = 10;
	}
	else if(isEqualString(cmdName, "setVariableSuccess"))
	{
		responseReceived = 11;
	}
	else if(isEqualString(cmdName, "setVariableFailed"))
	{
		responseReceived = 12;
	}
	else if(isEqualString(cmdName, "setVariableGroupFailed"))
	{
		responseReceived = 13;
	}
	else if(isEqualString(cmdName, "getVariableFailed"))
	{
		responseReceived = 14;
	}
	else if(isEqualString(cmdName, "getVariableGroupFailed"))
	{
		responseReceived = 15;
	}

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

	// Free space allocated by parseCommand()
	if (ret != -1)
	{
		if (interfaceName)tptp_free(interfaceName);
		if(cmdName)tptp_free(cmdName);
		if(paramList)
		{
			tptp_list_clear(paramList);
			tptp_free(paramList);
		}
	}
}


int AgentCommandHandler::getResponseReceived()
{
	return this->responseReceived;
}


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


Agent::Agent()
{
	_agentId = -1;
	_agentName = (char *) tptp_malloc(1);
	_agentName[0] = '\0';
	dataConnectionID = -1;
	_acProxy = 0;
}

Agent::Agent(int id, char* name)
{
	_agentId = id;
	if (name)
	{
		int len = strlen(name) + 1;
		_agentName = (char *) tptp_malloc(len);
		memcpy(_agentName, name, len);
	}
	else
	{
		_agentName = (char *) tptp_malloc(1);
		*_agentName = '\0';
	}

	dataConnectionID = -1;
	_acProxy = 0;
}

Agent::Agent(char* name)
{
	_agentId = -1;

	if (name)
	{
		int len = strlen(name) + 1;
		_agentName = (char *) tptp_malloc(len);
		memcpy(_agentName, name, len);
	}
	else
	{
		_agentName = (char *) tptp_malloc(1);
		*_agentName = '\0';
	}

	dataConnectionID = -1;
	_acProxy = 0;
}



Agent::~Agent()
{
	if (_agentName)
	{
		tptp_free(_agentName);
	}
	_agentName = 0;
}

char* Agent::getAgentName()
{
	return _agentName;
}

void Agent::setAgentName(char* name)
{
	if (_agentName) tptp_free(_agentName);

	if (name)
	{
		int len = strlen(name) + 1;
		_agentName = (char *) tptp_malloc(len);
		memcpy(_agentName, name, len);
	}
	else
	{
		_agentName = (char *) tptp_malloc(1);
		*_agentName = '\0';
	}
}

int Agent::getAgentID()
{
	return _agentId;
}

void Agent::setAgentID(int agentID)
{
	_agentId = agentID; 
}

int Agent::getAgentTokenID()
{
	return _agentTokenID;
}

void Agent::setAgentTokenID(int agentTokenID)
{
	_agentTokenID = agentTokenID; 
}

long Agent::getAgentProcessID()
{
	return _agentProcessID;
}

void Agent::setAgentProcessID(long agentProcessID)
{
	_agentProcessID = agentProcessID; 
}


void Agent::setACProxy(AgentController* acProxy)
{
	_acProxy = acProxy;
}

void Agent::sendCommand(char* cmd, ICommandHandler* handler)
{

	this->_acProxy->sendCommand(cmd, this->_agentId, handler);
}



void Agent::releaseAgent()
{
	char	releaseAgentCmd[1024];
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending releaseAgent command");

	sprintf(releaseAgentCmd, "<releaseAgent iid=\"org.eclipse.tptp.agentManager\"><agentID>%d</agentID></releaseAgent>", this->_agentId);

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	_acProxy->sendCommand(releaseAgentCmd, AGENT_MANAGER, agentCmdHandler);
	delete(agentCmdHandler);

	destroyDataConnection();

	return;

}

bool Agent::requestControl(int flags)
{
	char	requestControlCmd[1024];
	bool controlGranted = false;
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending requestControl command");

	sprintf(requestControlCmd, "<requestControl iid=\"org.eclipse.tptp.agentManager\"><agentID>%d</agentID><flags>%d</flags></requestControl>", this->_agentId, flags);

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();

	_acProxy->sendCommand(requestControlCmd, AGENT_MANAGER, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
	};

	if (agentCmdHandler->getResponseReceived() == 3)
	{
		controlGranted = true;
	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);

	return controlGranted;
}

void Agent::releaseControl()
{
	char	releaseControlCmd[1024];
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending releaseControl command");

	sprintf(releaseControlCmd, "<releaseControl iid=\"org.eclipse.tptp.agentManager\"><agentID>%d</agentID></releaseControl>", this->_agentId);

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();

	_acProxy->sendCommand(releaseControlCmd, AGENT_MANAGER, agentCmdHandler);

	delete(agentCmdHandler);

	return;

}


long Agent::addEventListener(char* interfaceid,  ICommandHandler* listener)
{

	char	tempbuf[1024];
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending addEventListener command");

	long listenerid = this->_acProxy->getConnection()->getNextContextID();

	sprintf(tempbuf, "<addEventListener iid=\"org.eclipse.tptp.eventProvider\"><interfaceID>%s</interfaceID><listenerID>%ld</listenerID></addEventListener>", interfaceid, listenerid);

	((ConnectionImpl*)(this->_acProxy->getConnection()))->addContext(listenerid, listener);

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();

	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
		return -1;
	};

	if (agentCmdHandler->getResponseReceived() == 4)
	{

	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return listenerid;
}



void Agent::removeEventListener(char* interfaceid, long listenerid)
{
	char	tempbuf[1024];
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending removeEventListener command");
	sprintf(tempbuf, "<removeEventListener iid=\"org.eclipse.tptp.eventProvider\"><interfaceID>%s</interfaceID><listenerID>%ld</listenerID></removeEventListener>", interfaceid, listenerid);
	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();

	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
	};

	if (agentCmdHandler->getResponseReceived() == 5)
	{
	
	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);

	return;
}



/*
 * Creates a data connection between the Client application and Agent Controller 
 * Returns the data connection id received from AC
 */
int Agent::createDataConnection(int direction)
{
	char	tempbuf[1024];
	int agentDataPathDirection = -1;

	if ( _acProxy == NULL )
		return -1;

	if (dataConnectionID == -1)
	{
		dataDirection = direction;
		dataConnectionID = this->_acProxy->createDataConnection(direction);
	}

	//Send the establishDataPath command
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending establishDataPath() command");
	if (direction == (int)TPTP_DATA_PATH_SEND)
	{
		agentDataPathDirection = TPTP_DATA_PATH_RECEIVE;
	}
	else if (direction == (int)TPTP_DATA_PATH_RECEIVE)
	{
		agentDataPathDirection = TPTP_DATA_PATH_SEND;
	}
	else if (direction == (int)TPTP_DATA_PATH_TWOWAY)
	{
		agentDataPathDirection = TPTP_DATA_PATH_TWOWAY;
	}

	sprintf(tempbuf, "<establishDataPath iid=\"org.eclipse.tptp.dataProvider\"><dataConnectionID>%d</dataConnectionID><flags>%d</flags></establishDataPath>", dataConnectionID, agentDataPathDirection);	

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();

	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
		return -1;
	};

	if (agentCmdHandler->getResponseReceived() == 1)
	{
		
	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);

	return dataConnectionID;
}


int Agent::destroyDataConnection()
{

	char          releaseDataPathFormat[] = "<releaseDataPath iid=\"org.eclipse.tptp.dataProvider\"><dataConnectionID>%d</dataConnectionID></releaseDataPath>";
	char          releaseDataPathCommand[8192];
	int			  _dataConnectionID;

	_dataConnectionID = dataConnectionID;
	if (dataConnectionID > 0) {
		sprintf(releaseDataPathCommand, releaseDataPathFormat,  dataConnectionID);
		sendCommand(releaseDataPathCommand, NULL);
		if (_acProxy != NULL) _acProxy->destroyDataConnection(dataConnectionID);
		dataConnectionID = -1;		
	}

	return _dataConnectionID;
}


int Agent::addDataListener(IDataProcessor* dataProcessor)
{
	int returnCode = -1;

	if ( _acProxy == NULL )
		return -1;
	
	returnCode = this->_acProxy->addDataListener(dataConnectionID, dataProcessor);

	return returnCode;
}


int Agent::removeDataListener(IDataProcessor* dataProcessor)
{
	int returnCode = -1;
	
	returnCode = this->_acProxy->removeDataListener(dataConnectionID, dataProcessor);	

	return returnCode;
}


/*
void Agent::startMonitoring(int dataConnID)
{
	char	tempbuf[1024];

	// Monitoring is started when a data path has been established,
	// hence, we issue an establishDataPath command here.
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending establishDataPath command for startMonitoring");
	sprintf(tempbuf, "<establishDataPath iid=\"org.eclipse.tptp.dataProvider\"><dataConnectionID>%d</dataConnectionID><flags>%d</flags></establishDataPath>", dataConnID, dataDirection);

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();

	this->sendCommand(tempbuf, agentCmdHandler);

	while (agentCmdHandler->getResponseReceived() != 1)
	{
		Sleep(1000);
	}

	delete(agentCmdHandler);
}
*/

int Agent::sendData(char buffer[], int bufferLength)
{
	int dimeLength;
	DIME_HEADER_PTR_T dimeHeader;

	dimeHeader = (DIME_HEADER_PTR_T) tptp_malloc(sizeof(DIME_HEADER_T));
	dimeLength = sizeof(DIME_HEADER_T);
	INIT_DIME_HEADER(dimeHeader);
	dimeHeader->data_length = bufferLength;
	MAKE_DIME_HEADER((char*)dimeHeader);

	int bytesSent = this->sendData(buffer, bufferLength, (char*)dimeHeader, dimeLength);
	
	TPTP_LOG_DEBUG_MSG1("Client Agent: The number of bytes sent - %d", bytesSent);

	tptp_free(dimeHeader); dimeHeader = NULL;

	return bytesSent;
}


int Agent::sendData(char buffer[], int bufferLength, char dimeHeader[], int dimeLength)
{

	char *new_buffer = (char *)tptp_malloc(dimeLength + bufferLength);

	memcpy(new_buffer, dimeHeader, dimeLength);
	memcpy(new_buffer+dimeLength, buffer, bufferLength);

	int bytesSent = this->_acProxy->sendData(dataConnectionID, new_buffer, bufferLength+dimeLength);
	tptp_free(new_buffer); new_buffer = NULL;

	TPTP_LOG_DEBUG_MSG1("Client Agent: The number of bytes sent - %d", bytesSent);
	return bytesSent;
}




int Agent::listVariables()
{
	char *tempbuf = "<listVariables iid=\"org.eclipse.tptp.variableProvider\"></listVariables>";
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending listVariables command");

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
		return -1;
	};

	if (agentCmdHandler->getResponseReceived() == 9)
	{

	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return 0;

}


int Agent::listVariableGroups()
{
	char *tempbuf = "<listVariableGroups iid=\"org.eclipse.tptp.variableProvider\"></listVariableGroups>";
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending listVariables command");

	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
		return -1;
	};

	if (agentCmdHandler->getResponseReceived() == 8)
	{

	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return 0;
}

Variable* Agent::getVariable(int varID)
{
	char	tempbuf[1024];
	Variable* agentVar = 0;
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending listVariables command");

	sprintf(tempbuf, "<getVariable iid=\"org.eclipse.tptp.variableProvider\"><variableID>%d</variableID></getVariable>", varID);
	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
	};

	if (agentCmdHandler->getResponseReceived() == 8)
	{
		//Extract the Agent Var info

	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return agentVar;

}

VariableGroup* Agent::getVariableGroup(int varGroupID)
{
	char	tempbuf[1024];
	VariableGroup* agentVarGroup = 0;
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending listVariables command");

	sprintf(tempbuf, "<getVariableGroup iid=\"org.eclipse.tptp.variableProvider\"><variableGroupID>%d</variableGroupID></getVariableGroup>", varGroupID);
	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	sendCommand(tempbuf, agentCmdHandler);

	int rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
	};

	if (agentCmdHandler->getResponseReceived() == 9)
	{
		//Extract the Agent Var Group info
	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return agentVarGroup;
}


int Agent::setVariable(Variable* varNode)
{

	char	tempbuf[1024];
	char* varBuf;
	int rc = 0;
	
	TPTP_LOG_DEBUG_MSG("Client Agent: Sending listVariables command");

	varBuf = Variable::createVariableString(varNode);

	sprintf(tempbuf, "<setVariable iid=\"org.eclipse.tptp.variableProvider\">%s</setVariable>", varBuf);
	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	sendCommand(tempbuf, agentCmdHandler);

	rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
		return -1;
	};

	if (agentCmdHandler->getResponseReceived() == 12)
	{
		rc = -1;
		TPTP_LOG_ERROR_MSG("Client Agent: Error - setVariable failed.");
	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return rc;
}

int Agent::setVariableGroup(VariableGroup* varGrp)
{
	char	tempbuf[1024];
	int rc = 0;

	TPTP_LOG_DEBUG_MSG("Client Agent: Sending listVariables command");

	char* varBuf = VariableGroup::createVarGroupString(varGrp);

	sprintf(tempbuf, "<setVariableGroup iid=\"org.eclipse.tptp.variableProvider\">%s</setVariableGroup>", varBuf);
	AgentCommandHandler* agentCmdHandler = new AgentCommandHandler();
	sendCommand(tempbuf, agentCmdHandler);

	rc = tptp_waitSemaphore(agentCmdHandler->getResponseReceivedSem());
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG("Client Agent: Error - wait for command response failed.");
		return -1;
	};

	if (agentCmdHandler->getResponseReceived() == 13)
	{
		rc = -1;
		TPTP_LOG_ERROR_MSG("Client Agent: Error - setVariableGroup failed.");
	}
	// TODO: Check for a failure response and report error.

	delete(agentCmdHandler);
	return rc;

}
