/*
	Test-ZTaskStream.cpp
	Author: James Russell <jcrussell@762studios.com>

	Purpose: Unit Test the ZTaskStream class.

	Changelog:
	12/18/2011 - Removed dependency on ZString (crertel)
	2011/04/13 - creation (jcrussell)
*/

#include "ZUnitTest.hpp"

#include <ZUtil/ZTaskStream.hpp>
#include <SST/SST_Atomic.h>

static const char* testSingleTask();
static const char* testTaskStream();
static const char* testFuture();


//List of unit tests
ZUnitTest ZTaskStreamUnitTests[] =
{
	{
		"Single task and wait",
		testSingleTask
	},
	{
		"Test TaskStream",
		testTaskStream
	},
	{
		"Test Future",
		testFuture
	}
};

//Now declare the ZUnitTestBlock associated with this.
DECLARE_ZTESTBLOCK(ZTaskStream);

/*************************************************************************/

static volatile int Task1Val = 0;
static volatile int Task2Val = 0;

//Wrapper Class
class ZTestTask : public ZTask
{
	virtual ZTaskReturnStatus Execute( void *_arg ) 
	{
		URFP(_arg);
		return ZTRS_FAILURE;
	}

	virtual void OnTaskFailed( void *_taskArgument ) 
	{
		URFP(_taskArgument);
		SST_Atomic_Inc(&Task2Val);
	}
};

//Should be pointer to int as arg
static ZTaskReturnStatus TaskMethod1(ZTaskStream *_stream, ZTask *_task, void* _arg)
{
	URFP(_stream);
	URFP(_task);

	//Arg is pointer to int
	int lim  = *(int*)_arg;

	for (int i = 0; i < lim; i++)
		SST_Atomic_Inc(&Task1Val);
		
	return ZTRS_SUCCESS;
}

//Should be pointer to bool as arg
static ZTaskReturnStatus TaskMethod2(ZTaskStream *_stream, ZTask *_task, void* _arg)
{
	URFP(_stream);
	URFP(_task);

	//Arg is pointer to bool
	bool val = *(bool*)_arg;

	if (val)
		SST_Atomic_Inc(&Task1Val);
	else
		SST_Atomic_Inc(&Task2Val);

	return ZTRS_SUCCESS;
}

//Takes integer argument, returns string with parsed int
static const char* FutureMethod(int _arg)
{
	char* buffer = new char[65];

	
	sprintf(buffer, "String%d", _arg);
	
	buffer[64] = ZBASICSTRING_NULL_TERMINATOR;
	return buffer;
}

//Takes an integer amount, sleeps that long, returns string with parsed int
static const char* FutureMethod2(int _arg)
{
	ZConcurrency::SleepThread(_arg);

	return FutureMethod(_arg);
}

static ZTaskReturnStatus SingleTaskMethod(ZTaskStream *_stream, ZTask *_task, void* _arg)
{
	URFP(_stream);
	URFP(_task);

	int* arg = (int*)_arg;
	
	*arg = 0xaabbccdd;
	return ZTRS_SUCCESS;
}

//////////////////////////////
/////// ACTUAL TESTS /////////
//////////////////////////////
static const char* testSingleTask()
{
	ZTaskStream stream;
	unsigned int k = 0;

	stream.PushTask(ZPtr<ZTask>(new ZTask(SingleTaskMethod, &k)));

	stream.ExecuteTasks(500);

	TASSERT(k == 0xAABBCCDD, "Task did not set variable to correct value.");

	return ZTEST_SUCCESS;
}

static const char* testTaskStream()
{
	//To Test Shutdown, We Scope
	{
		ZTaskStream TaskStream;

		TaskStream.SetTaskThreadCount(2);

		Task1Val = 0;
		Task2Val = 0;

		int i = 1000;
		int j = 10000;

		bool trueVal = true;
		bool falseVal = false;

		TaskStream.PushTask(ZPtr<ZTask>(new ZTask(TaskMethod1, &i)));
		TaskStream.PushTask(ZPtr<ZTask>(new ZTask(TaskMethod1, &j)));
		TaskStream.PushTask(ZPtr<ZTask>(new ZTask(TaskMethod2, &trueVal)));
		TaskStream.PushTask(ZPtr<ZTask>(new ZTask(TaskMethod2, &falseVal)));
		TaskStream.PushTask(ZPtr<ZTask>(new ZTestTask()));

		TaskStream.ExecuteTasks(10000);

		TASSERT(Task1Val == 11001, "TaskVal1 Not Set Correctly!");
		TASSERT(Task2Val == 2, "TaskVal2 Not Set Correctly!");
	}

	return ZTEST_SUCCESS;
}

static const char* testFuture()
{
	ZTaskStream TaskStream;

	TaskStream.SetTaskThreadCount(2);

	ZPtr< ZFuture<const char*, int> > testFuture1;
	ZPtr< ZFuture<const char*, int> > testFuture2;
	ZPtr< ZFuture<const char*, int> > testFuture3;
	ZPtr< ZFuture<const char*, int> > testFuture4;
	
	testFuture1 = new ZFuture<const char*, int>(FutureMethod, 1);
	testFuture2 = new ZFuture<const char*, int>(FutureMethod, 2);
	testFuture3 = new ZFuture<const char*, int>(FutureMethod2, 1000);
	testFuture4 = new ZFuture<const char*, int>(FutureMethod2, 2000);

	TaskStream.PushFuture(testFuture3);
	TaskStream.PushFuture(testFuture4);
	TaskStream.PushFuture(testFuture1);
	TaskStream.PushFuture(testFuture2);

	TASSERT(testFuture1->IsComplete() == false, "Test Future 1 completed waaaaaaay too soon!");
	TASSERT( strcmp(testFuture2->GetValue(),"String2") == 0, "Test Future 2 returned invalid value");
	TASSERT( strcmp(testFuture3->GetValue(), "String1000") == 0, "Test Future 3 returned invalid value");
	TASSERT( strcmp(testFuture4->GetValueRef(), "String2000") == 0, "TestFuture3 returned invalid value reference");

	TASSERT(testFuture1->IsComplete(), "Test Future 1 not registering as complete!");
	TASSERT(testFuture2->IsComplete(), "Test Future 2 not registering as complete!");
	TASSERT(testFuture3->IsComplete(), "Test Future 3 not registering as complete!");
	TASSERT(testFuture4->IsComplete(), "Test Future 4 not registering as complete!");

	return ZTEST_SUCCESS;
}
