/** @file string_unittest.cpp
 *  Functionality for testing EABaseType Strings
 *	@author Chris Burns (cburns@ea.com)
 *
 *	EABaseTypes
 *	(c) 2011 Electronic Arts Inc. 
 */

#if EA_RUNTIME_TESTING

#include "EABaseTypes/factory.h"
#include "EABaseTypes/datatypes.h"
#include "EABaseTypes/ptruserdata.h"
#include "EABaseTypes/autorefuserdata.h"
#include "EABaseTypes/safecomuserdata.h"
#include "EABaseTypes/poduserdata.h"

#include "SafeCom/RefCounted.h"

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

#include "ppmalloc/eageneralallocator.h"
#include "ppmalloc/eageneralallocatordebug.h"

class UserDataUnitTests : public EATest::TestSuite
{
	public:

		UserDataUnitTests()
		{
		}

		virtual void Initialize()
		{
			SuiteName("UserDataUnitTests");
			EATEST_REGISTER("TestPtr", "Testing PtrUserData", UserDataUnitTests, TestPtr);
			EATEST_REGISTER("TestAutoRef", "Testing AutoRefUserData", UserDataUnitTests, TestAutoRef);
			EATEST_REGISTER("TestSafeCom", "Testing SafeComUserData", UserDataUnitTests, TestSafeCom);
		}

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

			mFactory = EA::Types::CreateDefaultFactory(mAllocator);
		}

		void Teardown(void* data)
		{
			CORE_DELETE(mAllocator, mFactory);
		}

		void TestPtr(void* data);
		void TestAutoRef(void* data);
		void TestSafeCom(void* data);
        void TestPOD(void* data);

private:
		
		EA::Allocator::ICoreAllocator*	mAllocator;
		EA::Types::Factory*				mFactory;
};  

// make the unit test suite instance
UserDataUnitTests UserDataUnitTestsInstance;

// 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* gUserDataUnitTests = &UserDataUnitTestsInstance;

// *********************************************************************************************
// The test functions
// *********************************************************************************************

void UserDataUnitTests::TestPtr( void* data )
{
	EA::Types::BaseTypeRef u = mFactory->BaseType(this);
	EATESTAssert(this == u->As<UserDataUnitTests*>(), "Unexpected result!");
}

void UserDataUnitTests::TestAutoRef( void* data )
{
	EA::Types::ObjectRef o = mFactory->Object();

	EA::Types::BaseTypeRef u = EA::Types::AutoRefUserData<EA::Types::Object>::Create(mFactory, o);
	EATESTAssert(o == EA::Types::AutoRefUserData<EA::Types::Object>::AsRef(u), "Unexpected result!");

	EA::Types::BaseTypeRef v = EA::Types::AutoRefUserDataCreate(mFactory, EA::Types::ObjectIn(o));
	EATESTAssert(o == EA::Types::AutoRefUserData<EA::Types::Object>::AsRef(v), "Unexpected result!");
    
    EA::Types::BaseTypeRef n;
    EATESTAssert(NULL == EA::Types::AutoRefUserData<EA::Types::Object>::AsRef(n), "Unexpected result!");
}

SAFECOM_FORWARD_DECLARATION(class, MyInterface)

class MyInterface : public EA::SafeCom::VRefCounted<EA::SafeCom::IUnknown, int>
{
public:

	MyInterface(EA::Allocator::ICoreAllocator* allocator):EA::SafeCom::VRefCounted<EA::SafeCom::IUnknown, int>(), mAllocator(allocator) {}

	virtual void DeleteThis() const
	{
		CORE_DELETE(mAllocator, const_cast<MyInterface*>(this));
	}

	SAFECOM_IMPLEMENTATION_DECLARATION(0x990A3EC1)
	SAFECOM_QUERYINTERFACE(MyInterface, MyInterface)

private:

	EA::Allocator::ICoreAllocator*	mAllocator;
};

void UserDataUnitTests::TestSafeCom( void* data )
{
	MyInterfaceRef i(CORE_NEW(mAllocator, "MyInterface", EA::Allocator::MEM_TEMP) MyInterface(mAllocator), EA::SafeCom::DoNotCallAddRef);

	EA::Types::BaseTypeRef u = mFactory->BaseType(EA::SafeCom::IUnknownRef(i));
	EATESTAssert(i == u->As<EA::SafeCom::IUnknownRef>().Cast<MyInterface>(), "Unexpected result!");
    
	EA::Types::BaseTypeRef v = mFactory->BaseType(i);
    MyInterfaceRef ref = v->As<MyInterfaceRef>();
	EATESTAssert(i == ref, "Unexpected result!");
    
    EA::Types::BaseTypeRef n;
    
    ref = n->As<MyInterfaceRef>();
    EATESTAssert(ref == NULL, "Unexpected result!");
}

struct MyPOD 
{
    MyPOD():i(0) {}
    
	int i;
    
    bool operator==(const MyPOD& rhs)
    {
        return i == rhs.i;
    }
};

void UserDataUnitTests::TestPOD( void* data )
{
	MyPOD p;

	EA::Types::BaseTypeRef u = mFactory->BaseType(p);
	EATESTAssert(p == u->As<MyPOD>(), "Unexpected result!");
    
	u = mFactory->BaseType(&p);
    MyPOD* q = u->As<MyPOD*>();
	EATESTAssert(&p == q, "Unexpected result!");
    
    EA::Types::BaseTypeRef n;
    EATESTAssert(NULL == n->As<int*>(), "Unexpected result!");
}
#endif
