/*H************************************************************************************************/
/*!
  \File lua_unittest.cpp

  \Description

  \Notes
  None.

  \Copyright
  (c) 2011	 Electronic Arts Inc.

  \Version    1.0     04/27/2011 (CB) First version
  */
/************************************************************************************************H*/

#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 "EABaseTypesAssetStream/assetstreamfilehandler.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 "coreunit/interface.h"

#include "EABaseTypesLua/EABaseTypesLua.h"

#include "luatester.h"

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

class UXLuaUnitTests : public EATest::AsyncTestSuite
{
	public:

		UXLuaUnitTests()
		{

		}

		virtual void Initialize()
		{
			SuiteName("UXLuaUnitTests");
			EATEST_REGISTER_ASYNC("TestTableUtil", "Testing Table Util", UXLuaUnitTests, TestTableUtil);
			EATEST_REGISTER_ASYNC("TestSerializer", "Testing Serializer", UXLuaUnitTests, TestSerializer);
		}

		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_PERM) 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));
			
			lua_pushlightuserdata(mL, this);
			lua_pushcclosure(mL, TestAssert, 1);
			lua_setglobal(mL, "testassert");
			
			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 TestTableUtil(void* data);
		void TestSerializer(void* data);

private:

	static int TestAssert(lua_State* L)
	{
		int numArgs = lua_gettop(L);  // number of arguments
		if (numArgs > 0)
		{
			if(lua_toboolean(L, 1) == 0)
			{
				const char *msg = "";
				
				if(numArgs > 1)
				{
					msg = lua_tostring(L, 2);
				}

				EATest::TestSuite* testSuite = 
					reinterpret_cast<EATest::TestSuite*>(lua_touserdata(L, lua_upvalueindex(1)));
		
				// not grabbing the LUA line and file right now
				testSuite->SignalError(msg, __LINE__,__FILE__);
			}
		}

		return 1;
	}

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

// make the unit test suite instance
UXLuaUnitTests UXLuaUnitTestsInstance;

// 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* gUXLuaUnitTests = &UXLuaUnitTestsInstance;

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

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

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

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

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

#endif
