/** @file luadep_unittest.cpp
* Lua dependency loader unit test
* @author Nick Beatty (nbeatty@ea.com)
* (c) 2011 Electronic Arts Inc.
*/
#if EA_RUNTIME_TESTING

#include "EABaseTypes/factory.h"
#include "EABaseTypes/stringtable.h"
#include "EABaseTypes/callermap.h"
#include "EABaseTypes/metatypes.h"
#include "EABaseTypes/functor.h"
#include "EABaseTypes/functioncallwrappers.h"
#include "EABaseTypesAssetStream/assetstreamfilehandler.h"
#include "EABaseTypesLua/EABaseTypesLua.h"
#include "EABaseTypesLua/stackcheck.h"
#include "ux_base/trace.h"
#include "EABaseTypesJson/jsonloader.h"
#include "ux_eatrace/tracerfactory.h"

#include "unit/unit.h"
#include "coreunit/interface.h"
#include "coreallocator/icoreallocatormacros.h"

#include "luatester.h"

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

class UXLuaDepTests : public EATest::AsyncTestSuite
{
	public:

		UXLuaDepTests()
		{

		}

		virtual void Initialize()
		{
			SuiteName("UXLuaDepTests");
			EATEST_REGISTER_ASYNC("TestSingleFile", "Testing Single File", UXLuaDepTests, TestSingleFile);
			EATEST_REGISTER_ASYNC("TestRecursive", "Testing Recursive Dependencies ", UXLuaDepTests, TestRecursive);
			EATEST_REGISTER_ASYNC("TestMultiFile", "Testing Multi File ", UXLuaDepTests, TestMultiFile);
		}

		void Setup(void* data)
		{
			CoreUnit::UnitTestInfo* info = static_cast<CoreUnit::UnitTestInfo*>(data);
			EA::Allocator::ICoreAllocator* allocator = info->mAllocator;

			mAllocator = allocator;

			mStringTable = CORE_NEW(allocator, "EA::StringTable", EA::Allocator::MEM_PERM) EA::StringTable(allocator);
			mCallerMap = CORE_NEW(allocator, "EA::Types::CallerMap", EA::Allocator::MEM_PERM) EA::Types::CallerMap(allocator);
			mMetaTypes = CORE_NEW(allocator, "EA::Types::MetaTypes", EA::Allocator::MEM_PERM) EA::Types::MetaTypes();
			mFactory = CORE_NEW(allocator, "EA::Types::Factory", EA::Allocator::MEM_PERM) EA::Types::Factory(allocator, mStringTable, mCallerMap, mMetaTypes);

			UX::Trace::ITracerFactoryRef tracerFactory = CORE_NEW(mAllocator, "UX::EATrace::TracerFactory", EA::Allocator::MEM_PERM) UX::EATrace::TracerFactory(mAllocator);
			UX::Trace::Initialize(mAllocator, tracerFactory);
			
			mFileHandler = CORE_NEW(mAllocator, "AssetStreamFileHandler", EA::Allocator::MEM_TEMP) EA::Types::AssetStreamFileHandler(mAllocator, ".");
			
			mJsonLoader = CORE_NEW(mAllocator, "JsonLoader", EA::Allocator::MEM_PERM) EA::Types::JsonLoader(mFactory, mFileHandler);

			mL = EA::Lua::NewState(mAllocator, mAllocator, mFactory, mFileHandler, mJsonLoader, EA::Types::FunctionIn(NULL), EA::Types::FunctionIn(NULL));
			
			mFinishTest = false;
		}

		virtual void Idle();
		void Teardown(void* data)
		{
			lua_close(mL);
			mL = NULL;
			
			CORE_DELETE(mAllocator, mJsonLoader);
			mJsonLoader = NULL;

			mFileHandler = NULL;
	
			UX::Trace::Shutdown();
			
			mMetaTypes->Unload();

			CORE_DELETE(mAllocator, mFactory);
			CORE_DELETE(mAllocator, mMetaTypes);
			CORE_DELETE(mAllocator, mCallerMap);
			CORE_DELETE(mAllocator, mStringTable);
		}

		void TestSingleFile(void* data);
		void TestRecursive(void* data);
		void TestMultiFile(void* data);

private:

	void FinishTest();
	
	EA::Allocator::ICoreAllocator*	mAllocator;
	EA::StringTable*				mStringTable;
	EA::Types::CallerMap*			mCallerMap;
	EA::Types::MetaTypes*			mMetaTypes;
	EA::Types::Factory*				mFactory;
	lua_State*						mL;
	EA::Types::IFileHandlerRef		mFileHandler;
	EA::Types::JsonLoader*			mJsonLoader;
	bool							mFinishTest;
};

// make the unit test suite instance
UXLuaDepTests UXLuaDepTestsInstance;

// this declaration is so that main.cpp in the game can pretend to use it hence keeping
// this entire translation unit from being excluded from the build
EATest::TestSuite* gUXLuaDepTests = &UXLuaDepTestsInstance;

// *********************************************************************************************
// The test function
// *********************************************************************************************

void UXLuaDepTests::TestSingleFile(void* data)
{
	printf("\n\n");
	
	EA::Types::FunctionRef callback = mFactory->Function(this, &UXLuaDepTests::FinishTest);
	UX::LuaTester::Run(mL, callback, "data/lua/luadep/dpone.lua");
	
	printf("\n\n");
}

void UXLuaDepTests::TestRecursive(void* data)
{
	printf("\n\n");
	
	EA::Types::FunctionRef callback = mFactory->Function(this, &UXLuaDepTests::FinishTest);
	UX::LuaTester::Run(mL, callback, "data/lua/luadep/dptwo.lua");
	
	printf("\n\n");
}

class MultiFileCallback : public EA::Types::Functor<void>
{
public:
	
	MultiFileCallback(EA::Types::Factory* factory, EA::Types::FunctionIn callback, unsigned int numFiles):
		EA::Types::Functor<void>(factory, static_cast<MultiFileCallback*>(NULL), &MultiFileCallback::Do),
		mCallback(callback),
		mNumFiles(numFiles)
	{
		SetObject(this);
	}
	
private:

	void Do()
	{
		mNumFiles--;
		
		if(mNumFiles == 0)
		{
			mCallback->Call<void>();
		}
	}
	
	EA::Types::FunctionRef	mCallback;
	unsigned int			mNumFiles;
};

void UXLuaDepTests::TestMultiFile(void* data)
{
	printf("\n\n");
	
	EA::Types::FunctionRef callback = mFactory->Function(this, &UXLuaDepTests::FinishTest);
	EA::Types::FunctionRef join = new (mFactory) MultiFileCallback(mFactory, callback, 2);
	
	UX::LuaTester::Run(mL, join, "data/lua/luadep/dpone.lua");
	UX::LuaTester::Run(mL, join, "data/lua/luadep/dptwo.lua");
	
	printf("\n\n");
}

void UXLuaDepTests::Idle()
{
	mFileHandler->Update();
	if(mFinishTest == true)
	{
		mFinishTest = false;
		EATESTContinue();
	}
}

void UXLuaDepTests::FinishTest()
{
	mFinishTest = true;
}

#endif
