/*******************************************************************************
 * Copyright (c) 2005, 2009 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:
 *    Vishnu K Naikawadi, Intel - Sample implementation of a Collector Agent.
 *
 * $Id: TimeCollector.cpp,v 1.16 2009/07/10 00:11:31 jwest Exp $
 *
 *******************************************************************************/ 


#include "TimeCollector.h"
#include <time.h>
#include <iostream>


using namespace std;

TimeCollector::TimeCollector(char* name) : BaseCollectorImpl(name), BaseAgentImpl(name)
{	
	startTime = 0;
	endTime = 0;
	elapsedTime = 0;
	dataListenerID = 0; 
	running = false;
}


TimeCollector::~TimeCollector()
{
}

int TimeCollector::getCurrentTime(CmdBlock* cmdBlock)
{
	time_t currTime;
	struct tm* locTime;
	char* currTimeStr;

	// Create a time string for the current system time.
	time(&currTime);
	locTime = localtime(&currTime);
	currTimeStr = asctime(locTime);

	// Build the xml fragment for the response command "currentTime", with one
	// parameter "time" whose value is the current time string.
	// The source of the original cmd becomes the destination for this reply.
	// The context value is returned unchanged as it may be used by the client
	// to determine what command this response is for.
	char   commandFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"> <currentTime iid=\"org.eclipse.tptp.Client\"><time>%s</time></currentTime></Cmd>";
	char   command[1024];
	sprintf( command, commandFormat, getAgentID(), cmdBlock->getSourceID(),
			 cmdBlock->getContextID(), currTimeStr);

	int ret = BaseAgentImpl::sendCommand(command);

	return ret;
}


int TimeCollector::getElapsedTime(CmdBlock* cmdBlock)
{
	// If stopped, calculate the elapsed time for the last start/stop.
	// If running, calculate elapsed time from start until now.
	// Return 0 for elapsedTime if have any problem calculating it.

	// First make sure we have a useful start time value.
	if (startTime <= 0)
	{
		// Run was never called to set the start time.
		elapsedTime = 0;
	}
	else if (!running)
	{
		if (endTime < startTime)
		{
			// Had an error in the runThread loop which caused it to exit
			// before getting a stop request,
			elapsedTime = 0;
		} 
		else
		{
			// Have valid start and stop times.
			elapsedTime = (long)difftime(endTime, startTime);
		}
	} 
	else
	{
		// Have a valid start time and are still running,
		// so use current time.
#ifdef _WIN64
		time_t currTime;
#elif defined _AIX
		time_t currTime;
#else
		long currTime;
#endif
		time(&currTime);
#ifdef _WIN64
		elapsedTime = difftime(currTime, startTime);
#else
		elapsedTime = (long)difftime(currTime, startTime);
#endif
	}


	// Build the xml fragment for the response command "elapsedTime", with one
	// parameter "time" whose value is the elapsed time integer.
	// The source of the original cmd becomes the destination for this reply.
	// The context value is returned unchanged as it may be used by the client
	// to determine what command this response is for.

	char   commandFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"> <elapsedTime iid=\"org.eclipse.tptp.TimeCollector\"><time>%ld</time></elapsedTime></Cmd>";
	char   command[1024];
	sprintf( command, commandFormat, getAgentID(), cmdBlock->getSourceID(),
			 cmdBlock->getContextID(), elapsedTime);

	int ret = BaseAgentImpl::sendCommand(command);

	return ret;
}


int TimeCollector::run(CmdBlock* cmdBlock)
{
	TID tid;

	// Store the starting time for this run and clear the end time.
	time(&startTime);
	endTime = 0;

	struct tm* currTime = localtime(&startTime);
	cout<<endl<<"Start Time is - "<<asctime(currTime)<<endl;

	// Set the ID for who will be sent the data generated by
	// this run.
	// The current API does not allow specification of data listeners.
	// Here we assume whoever sent the run command is the listener.
	// Note: The API will change to remedy this.
	dataListenerID = cmdBlock->getSourceID();

	running = true; // set our internal state variable

	// Start a thread to perform our collection action. This sample
	// is not actually collecting anything - it just sends msgs over the data
	// channel. 
#ifdef _WIN32
	HANDLE handle = CreateThread(NULL,                /* default security attributes       */
								 0,                   /* same stack size as current thread */
								 runThreadWin32,      /* Thread entry point                */
								 (LPVOID)this,        /* flusher info params               */
								 0,                   /* start executing immediately       */
								 &tid );              /* the thread ID                     */
	CLOSE_THREAD_HANDLE(handle);
#else
	pthread_create( &tid, NULL, runThreadNonWin32, (void *)this );
#endif

	// Build the xml fragment for the response command "agentRunning", with no
	// parameters.
	// The source of the original cmd becomes the destination for this reply.
	// The context value is returned unchanged as it may be used by the client
	// to determine what command this response is for.

	char   commandFormat[] = "<Cmd src=\"%ld\" dest=\"%ld\" ctxt=\"%ld\"> <agentRunning iid=\"org.eclipse.tptp.Collector\"></agentRunning></Cmd>";
	char   command[1024];
	sprintf( command, commandFormat, getAgentID(), cmdBlock->getSourceID(),
			 cmdBlock->getContextID());

	int ret = BaseAgentImpl::sendCommand(command);

	return ret;
}

int TimeCollector::stop(CmdBlock* cmdBlock)
{
	// Store the stopping time.
	time(&endTime);
	struct tm* currTime = localtime(&endTime);

	cout<<endl<<"End Time is - "<<asctime(currTime)<<endl;


	// Set the internal state variable which will cause the
	// runThread to stop.
	running = false;

	// Build the xml fragment for the response command "agentStopped", with no
	// parameters.
	// The source of the original cmd becomes the destination for this reply.
	// The context value is returned unchanged as it may be used by the client
	// to determine what command this response is for.

	char   commandFormat[] = "<Cmd src=\"%ld\" dest=\"%ld\" ctxt=\"%ld\"> <agentStopped iid=\"org.eclipse.tptp.Collector\"></agentStopped></Cmd>";
	char   command[1024];
	sprintf( command, commandFormat, getAgentID(), cmdBlock->getSourceID(),
			 cmdBlock->getContextID());

	int ret = BaseAgentImpl::sendCommand(command);

	return ret;
}

// Static method for the thread entry point.
#ifdef _WIN32
DWORD WINAPI TimeCollector::runThreadWin32(LPVOID param)
#else
void *TimeCollector::runThreadNonWin32(void* param)
#endif
{
	TimeCollector* pThis = (TimeCollector*)param;
	pThis->runThread();
	return 0;
}

void TimeCollector::runThread()
{
	int  count = 0;
	char buffer[1024];
	int  ret = -1;

	while (running)
	{
		sprintf(buffer, "Hello from Time Collector Agent - Count %d", count);
		ret = sendData(dataListenerID, buffer, strlen(buffer)+1);
		if (ret)
		{
			cout<<"ERROR: unable to send hello msg across data channel, collector is stopping"<<endl<<endl;
			running = false;
		}
		Sleep(100); // pause briefly before sending another message
		count++;
	}
}


int TimeCollector::processCommand(CmdBlock* cmdBlock)
{

	int handled = 0;

	// Pass the incoming command to the base class so that
	// it can take care of commands common to all collectors
	// and all agents.
	handled = BaseCollectorImpl::processCommand(cmdBlock);

	// If the base class handled the command, we are done.
	// 0 indicates command was handled, otherwise -1.
	if (handled == 0) return handled; 

	// See if this command is part of our interface.
	if (!isEqualString(cmdBlock->getIID(), "org.eclipse.tptp.TimeCollector"))
	{
		sendErrorCommand(cmdBlock->getSourceID(),
						 cmdBlock->getContextID(),
						 TPTP_INTERFACE_UNKNOWN,
						 cmdBlock->getIID());
		return -1;
	}

	// Handle the commands for our interface.
	char* cmdName = cmdBlock->getCommandName();
	if (isEqualString(cmdName, "getElapsedTime"))
	{
		getElapsedTime(cmdBlock);
	}
	else if (isEqualString(cmdName, "getCurrentTime"))
	{
		getCurrentTime(cmdBlock);
	}
	else
	{
		// This command is not in our interface.
		sendErrorCommand(cmdBlock->getSourceID(),
						 cmdBlock->getContextID(),
						 TPTP_CMD_UNKNOWN,
						 cmdName);
		return -1; 
	}

	return 0;
}


int TimeCollector::receiveData(int sourceID, char buffer[], int bytesRead, DIME_HEADER_PTR_T dimeHeader)
{

	// Display the incoming data, skipping past the header info.
	int headerLength = dimeHeader->id_length+dimeHeader->options_length+dimeHeader->type_length;
	cout<<"Received data: "<<buffer+headerLength<<endl;
	return 0;
}

int main(int argc, char* argv[])
{

	char* collectorName = "org.eclipse.tptp.TimeCollector";

	TimeCollector* TimeCollectorAgent = new TimeCollector(collectorName);
	
	// Check if been given an alternate location for serviceconfig.xml on
	// the command line.
	TimeCollectorAgent->processCommandLine(argc, argv);

	// Tell the Agent Controller this is an agent and establish its personal
	// command channel with the Agent Controller.
	// A command handler thread is started which listens on this channel and
	// calls the processCommand() method of this agent for all commands coming
	// through this channel.
	TimeCollectorAgent->registerAgent();

	// The command handler thread and processCommand() are doing all of
	// the work for this collector now that it has established itself.
	// The main thread now just waits for the Agent Controller to tell this
	// collector to terminate.
	TimeCollectorAgent->waitForTermination();

	return 0;
}
