/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation and others.
 * 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
 *    IBM - Portions of processing code moved from older RAC (by Andy Kaylor)
 *
 * $Id: RACAgentSupport.c,v 1.18 2009/04/14 20:33:29 jwest Exp $
 *
 *******************************************************************************/

#include <string.h>

#include "tptp/TPTPConfig.h"
#include "tptp/TPTPTypes.h"
#include "tptp/TPTPSupportTypes.h"
#include "tptp/TPTPSupportUtils.h"
#include "tptp/TransportSupport.h"
#include "tptp/BaseTLLog.h"
#include "tptp/TPTPBaseTL.h"
#include "tptp/compatibility/RACSupport.h"
#include "tptp/ProcCtlUtils.h"
#include "AgentCTL.h"
#include "AgentCmdHandlers.h"
#include "RACAgentSupport.h"

/* Table for maintaining process information, including agents associated
   with the process */
static process_list_t *process_list;
static ra_string_t     _nodeUUID;

/** GET_ACTIVE_PROCESS_LIST  ***************************************************
  * Retrieves the global process list the represents all the processes the
  * server knows about.
  */
process_list_t* ra_getActiveProcessList() {
	return process_list;
}

/** PROCESS_LIST_CREATE  *******************************************************
  * Allocates the memory required to create a process_list_t with no entries.
  * ie:  The head and tail are NULL
  */
process_list_t* ra_processListCreate() {
	process_list_t *list=(process_list_t*)tptp_malloc(sizeof(process_list_t));
	list->head=list->tail=NULL;
	list->process_count=0;
	return list;
}

/** INITIALIZE_DATA_STRUCTURES **************************************
  * Creates and initializes the global "process_list".  All the pointers
  * will be initialized correctly to allow error free iteration through
  * both the process list and the associated agent list for each of the
  * processes in the process list.
  */
void ra_initializeDataStructures() {
	ra_generateUUID(&_nodeUUID);
	process_list=ra_processListCreate();
//	ra_newAgentControllerConfiguration();
//	_network_list=ra_createNetworkList();
	ra_mutexCreate(&process_list->lock);
}


ra_string_t* getNodeUUID()
{
	return &_nodeUUID;
}

/** PROCESS_CREATE *************************************************************
  * Allocates the memory for a new process_t and appends this process to a
  * specific process list.  The new process_t structure will always be referenced
  * by list->process->tail.
  */
process_t* ra_processCreate(process_list_t *list) {
	/* Allocate the process_node, process, client list, and the agent list
	   for this process */
	process_t *process;
	process=(process_t*)tptp_malloc(sizeof(process_t));
	process->node=(process_list_node_t*)tptp_malloc(sizeof(process_list_node_t));


	/* Generate a UUID */
	ra_generateUUID( &process->uuid );

	/* Initialize the application name */
	process->processName.length=0;
	process->processName.data=NULL;

	/* Initialize the agent count */
	process->active_agent_count=0;
	process->active_agents.head=process->active_agents.tail=NULL;
	process->waiting_agent_count=0;
	process->waiting_clients.head=process->waiting_clients.tail=NULL;

	/* Insert the process in the process_list */
	process->node->next=NULL;
	process->node->previous=list->tail;
	process->node->process=process;
	process->node->list=list;

	/* RKD:  To make the modification atomic we should use an atomic compare and swap here */
	list->tail=process->node;

	/* If this not the first entry we must set the link in the previous tail */
	if(process->node->previous) {
		process->node->previous->next=process->node;
	}
	else {
		list->head=process->node;
	}

	list->process_count++;

	return process;
}

/** AGENT_CREATE ***************************************************************
  * Allocates the memory for a new agent_t and appends this agent to a
  * specific process.  The new agent_t structure will always be referenced
  * by process->agents->->tail->agent.
  */
extern agent_t* ra_agentCreate(process_t *process,
							   BOOL active) {
	/* Allocate memory for both the agent and the holder for the agent
	   in the agent list structure */
	agent_list_t *list;
	agent_t *agent;
	agent=(agent_t*)tptp_malloc(sizeof(agent_t));
	agent->node=(agent_list_node_t*)tptp_malloc(sizeof(agent_list_node_t));

	agent->process=process;

	/* This agent doesn't refer to a client yet */
	agent->client=NULL;
	agent->prev_client=NULL; /* 10000 */
	agent->attached=FALSE;
	agent->logging=FALSE;
	agent->IPCBufCreated=FALSE;
	agent->IPCBufSize=0;
	agent->agentName.data=NULL;
	agent->agentName.length=0;
	agent->agentUUID.data=NULL;
	agent->agentUUID.length=0;
	agent->agentType.data=NULL;
	agent->agentType.length=0;
//	agent->connection=(ra_connection_t*)malloc(sizeof(ra_connection_t));
//	BZERO(agent->connection, sizeof(ra_connection_t));
	agent->pipe = (HANDLE)-1;
//	agent->connection->type=RA_AGENTPIPE_CONNECTION;
//	agent->connection->target.agent=agent;
	agent->dataConnectionID = 0;
	agent->isProcessingData = FALSE;
	tptp_initializeSemaphore( &agent->dataSemaphore );

	/* Insert the agent in the appropiate agent list */
	agent->node->agent=agent;
	agent->node->next=NULL;

	if(active) {
		list=&process->active_agents;
		process->active_agent_count++;
		agent->node->list=&process->active_agents;
	}
	else {
		list=&process->waiting_clients;
		process->waiting_agent_count++;
		agent->node->list=&process->waiting_clients;
	}
	agent->node->previous=list->tail;

	/* RKD:  To make the modification atomic we should use an atomic compare and swap here */
	list->tail=agent->node;

	/* If this not the first entry we must set the link in the previous tail */
	if(agent->node->previous) {
		agent->node->previous->next=agent->node;
	}
	else {
		list->head=agent->node;
	}
	return agent;
}

/** PROCESS_FIND ***************************************************************
  * Find a process based upon its PID (key) in a specific process list.
  * @returns      NULL  - process is not in the list.
  *           otherwise - the address of the process_t structure
  */
process_t* ra_processFind(process_list_t *list,
						  PID processId) {
	process_list_node_t *current=list->head;

	while(current != NULL) {
		if(current->process->pid == processId) {
			return current->process;
		}
		current=current->next;
	}
	return NULL;
}

/** AGENT_FIND_BY_PROCESS ******************************************************
  * Find a agent based upon its name (key) in a specific process.
  * @returns      NULL  - agent is not associated with the process.
  *           otherwise - the address of the agent_t structure
  */
agent_t* ra_agentFindByProcess(process_t *process,
							   ra_string_t *agentName,
							   BOOL active) {
	agent_list_node_t *current;
	if(active) {
		current=process->active_agents.head;
	}
	else {
		current=process->waiting_clients.head;

	}

	while(current != NULL) {
		if(strcmp(current->agent->agentName.data, agentName->data) == 0) {
			return current->agent;
		}
		current=current->next;
	}
	return NULL;

}

/** AGENT_FIND_BY_UUID  ********************************************************
  *
  */
agent_t* ra_agentFindByUUID(process_list_t *list,
						   ra_string_t *agentUUID,
						   BOOL active) {

	process_list_node_t *processEntry=list->head;

	while(processEntry != NULL) {
		agent_list_node_t *current;
		if(active) {
			current=processEntry->process->active_agents.head;
		}
		else {
			current=processEntry->process->waiting_clients.head;

		}

		while(current != NULL) {
			if(strcmp(current->agent->agentUUID.data, agentUUID->data) == 0) {
				return current->agent;
			}
			current=current->next;
		}


		processEntry=processEntry->next;
	}
	return NULL;


}

void ra_agentMakeActive(agent_t *agent) {
	agent_list_node_t *node;

	node=agent->node;

	if(&agent->process->waiting_clients==agent->node->list) {
		/* Remove node from current list */
		if(node->previous && node->next) { /* Neither head nor tail */
			node->previous->next=node->next;
			node->next->previous=node->previous;
		}
		else if(node->next) { /* head */
			node->next->previous=NULL;
			agent->process->waiting_clients.head=node->next;
		}
		else if(node->previous) { /* tail */
			node->previous->next=NULL;
			agent->process->waiting_clients.tail=node->previous;
		}
		else if(node->list->head==node->list->tail) { /* only entry */
			agent->process->waiting_clients.tail=NULL;
			agent->process->waiting_clients.head=NULL;
		}

		/* Add the node to the active list */
		node->next=NULL;
		node->previous=agent->process->active_agents.tail;
		agent->process->active_agents.tail=node;

		/* If this not the first entry we must set the link in the previous tail */
		if(node->previous) {
			node->previous->next=node;
		}
		else {
			agent->process->active_agents.head=node;
		}
		agent->process->active_agent_count++;
		agent->process->waiting_agent_count--;
/* 201349 begin */
		/* Make the agent's current list the active list */
		node->list = &agent->process->active_agents;
/* 201349 end */
	}
}

void ra_agentMakeInactive(agent_t *agent) {
	agent_list_node_t *node=agent->node;

	if(&agent->process->active_agents==agent->node->list) {
		/* Remove node from current list */
		if(node->previous && node->next) { /* Neither head nor tail */
			node->previous->next=node->next;
			node->next->previous=node->previous;
		}
		else if(node->next) { /* head */
			node->next->previous=NULL;
			agent->process->active_agents.head=node->next;
		}
		else if(node->previous) { /* tail */
			node->previous->next=NULL;
			agent->process->active_agents.tail=node->previous;
		}
		else if(agent->process->active_agents.head==agent->process->active_agents.tail) { /* only entry */
			agent->process->active_agents.tail=NULL;
			agent->process->active_agents.head=NULL;
		}

		/* Add the node to the waiting list */
		node->next=NULL;
		node->previous=agent->process->waiting_clients.tail;
		agent->process->waiting_clients.tail=node;

		/* If this not the first entry we must set the link in the previous tail */
		if(node->previous) {
			node->previous->next=node;
		}
		else {
			agent->process->waiting_clients.head=node;
		}
		agent->process->active_agent_count--;
		agent->process->waiting_agent_count++;
/* 201349 begin */
		/* Make the agent's current list the waiting list */
		node->list = &agent->process->waiting_clients;
/* 201349 end */
	}
}

/* 215066 */
void ra_startHeadlessMonitor( tl_state_data_t* stateData, agent_t *agent) {

	ra_message_t *message;
	ra_command_t *command;
	unsigned int         contextID = baseTL_getNextContextID();
	tptp_int32 rc;

	if (!agent->attached) {
		agent->attached = TRUE;
	}

	rc = startMonitor(stateData, agent, contextID);

	/* If race condition still exist, make sure the flusher thread is ready before going beyond this point */
	/* kevin... the flusher thread is started in the call to createLogFile..
	 * which should have been done before this routine is called */

	/* Resume monitoring */
	message = ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command = ra_addCommandToMessage(message, NULL);

	command->tag = RA_CUSTOM_COMMAND;
	command->info.custom_command.context = agent->context;
	command->info.custom_command.processId = agent->process->pid;
	ra_copyRASTRING(&command->info.custom_command.agent, &agent->agentName);
	ra_createRASTRING(&command->info.custom_command.message, "RESUME");

	ra_forwardMessage(message, agent);

	/* Send a custom message indicating the headless client of the passed agent */
	ra_createRASTRING(&command->info.custom_command.message, "HEADLESS");
	ra_forwardMessage(message, agent);
	ra_destroyMessage(message, TRUE);

}

/** FORWARD_MESSAGE  *************************************************************
  * Sends the message to the agent provided.
  * AK - Note: The function signature is modified from the RAC code which
  *               took a connection where we have an agent.
  */
int ra_forwardMessage( ra_message_t *message, agent_t *agent)
{
	char*   buffer; /* Bug 60961 */
	int     bytesWritten;
	int     msgLength; /* Bug 60961 */
	int     result;
	int     bufLength;

	msgLength = ra_determineMessageLength(message);
	if(msgLength < 1024) {
		msgLength = 1024;
	}
	buffer = (char*)tptp_malloc(sizeof(char) * (msgLength + 1));
	BZERO(buffer, msgLength + 1);
	bufLength = ra_writeMessageToBuffer((unsigned char*)buffer, msgLength + 1, message);
	result = writeToNamedPipe(agent->pipe, buffer, 0, bufLength, &bytesWritten);
	tptp_free(buffer);

	return result;
}

/**  Get configured ports *****************************************************
 *
 */
ra_uint_t ra_getConfiguredServerPort(void)
{
	// TODO: Call getRemoteMonitoringInfo in AC to get this
	return 10002;
}

static void deleteAgentList(agent_list_t *list) {
	agent_list_node_t *current;
	char pipeFullName[_MAX_PATH], *pipeName;

	if(!list) {
		return;
	}
	current=list->head;

	strcpy(pipeFullName, RA_PIPE_NAMESPACE);
	pipeName = pipeFullName + strlen(RA_PIPE_NAMESPACE);

	while(current != NULL) {
		agent_list_node_t *temp=current;

		disconnectFromNamedPipe(current->agent->pipe);

		if (current->agent->agentUUID.data != NULL) {
			strcpy(pipeName, current->agent->agentUUID.data);
			destroyNamedPipe(pipeFullName);
		}

		//ra_destroyRASTRING(&current->agent->agentName);
		//ra_destroyRASTRING(&current->agent->agentUUID);
		//ra_destroyRASTRING(&current->agent->agentType);

		cleanPipeUp(&(current->agent->pipe));

		//tptp_free(current->agent);
		current->agent=NULL;

		current=current->next;
		tptp_free(temp);
	}
}

/** PROCESS_REMOVE **************************************************************
  * Remove an process from a specific process list.
  */
void ra_processDestroy(process_t *process) {

	process_list_node_t *node=process->node;

	/* Change the links */
	if(node->previous && node->next) { /* Neither head nor tail */
		node->previous->next=node->next;
		node->next->previous=node->previous;
	}
	else if(node->next) { /* head */
		node->next->previous=NULL;
		node->list->head=node->next;
	}
	else if(node->previous) { /* tail */
		node->previous->next=NULL;
		node->list->tail=node->previous;
	}
	else if(node->list->head==node->list->tail) { /* only entry */
		node->list->tail=NULL;
		node->list->head=NULL;
	}
	/* decrement the process count */
	node->list->process_count--;

	/* Clean up the application name */
	ra_destroyRASTRING(&process->processName);
	process->processName.data=NULL;
	process->processName.length=0;

	/* We need to walk all the clients and agents, deleting each */
	deleteAgentList(&process->active_agents);
	deleteAgentList(&process->waiting_clients);

	tptp_free(process);
	tptp_free(node);
}

