#include <assert.h>

#include <algorithm>

#include "utils/objectmanager.h"
#include "utils/logger.h"

ObjectManager g_ObjectManager;

ObjectManager::ObjectManager()
{
}

ObjectManager::~ObjectManager()
{
}

void ObjectManager::Register( const RuntimeClass* _pRuntimeClass, IObjectConstructor* _pObjectConstructor )
{
	// Find already registered object.

	std::vector<ObjectRegistryEntry>::iterator it = std::find_if( 
		m_objectRegistry.begin(), 
		m_objectRegistry.end(), 
		[=]( const ObjectRegistryEntry& objectEntry )
		{
			return objectEntry.pRuntimeClass == _pRuntimeClass;
		}
	);

	assert( it == m_objectRegistry.end() );

	ObjectRegistryEntry entry = {};
	entry.pRuntimeClass = _pRuntimeClass;
	entry.pObjectConstructor = _pObjectConstructor;

	m_objectRegistry.push_back( entry );

	Msg( "ObjectManager: Registered object %s", _pRuntimeClass->m_classname );
}

IObjectBase* ObjectManager::Create( const RuntimeClass* _pRuntimeClass )
{
	// Find already registered object.
	std::vector<ObjectRegistryEntry>::iterator it = std::find_if( 
		m_objectRegistry.begin(), 
		m_objectRegistry.end(), 
		[=]( const ObjectRegistryEntry& objectEntry )
		{
			return objectEntry.pRuntimeClass == _pRuntimeClass;
		}
	);

	assert( it != m_objectRegistry.end() );

	IObjectConstructor* pObjectConstructor = it->pObjectConstructor;
	assert( pObjectConstructor );

	IObjectBase* pObject = pObjectConstructor->Create();
	assert( pObject );

	// Add reference
	//pObject->AddRef();

	return pObject;
}

IObjectBase* ObjectManager::CreateByName( const char* _pClassName )
{
	// Find already registered object.
	std::vector<ObjectRegistryEntry>::iterator it = std::find_if( 
		m_objectRegistry.begin(), 
		m_objectRegistry.end(), 
		[=]( const ObjectRegistryEntry& objectEntry )
		{
			return strcmp( objectEntry.pRuntimeClass->Name(), _pClassName ) == 0;
		}
	);

	assert( it != m_objectRegistry.end() );

	IObjectConstructor* pObjectConstructor = it->pObjectConstructor;
	assert( pObjectConstructor );

	IObjectBase* pObject = pObjectConstructor->Create();
	assert( pObject );

	// Add reference
	pObject->AddRef();

	return pObject;
}

/////////////////////////////////////////////////////////////////////
// Object System Test

class TestObject1 : public IObjectBase
{
	DECLARE_RTTI_ROOT( TestObject1 );
public:
	TestObject1();
	virtual ~TestObject1();

	virtual void Foo();
};

IMPLEMENT_RTTI_ROOT( TestObject1 );

TestObject1::TestObject1()
{
	Msg( "TestObject1: ctor called" );
}

TestObject1::~TestObject1()
{
	Msg( "TestObject1: dtor called" );
}

void TestObject1::Foo()
{
	Msg( "TestObject1::Foo() called" );
}

class TestObject2 : public TestObject1
{
	DECLARE_RTTI(TestObject2, TestObject1);
public:
	TestObject2();
	~TestObject2();

	void Foo() override;
};

IMPLEMENT_RTTI(TestObject2, TestObject1);

TestObject2::TestObject2()
{
	Msg("TestObject2: ctor called");
}

TestObject2::~TestObject2()
{
	Msg("TestObject2: dtor called");
}

void TestObject2::Foo()
{
	Msg("TestObject2::Foo() called");
}

void TestTPtr()
{
	TPtr< TestObject1 > testObjectPtr = CreateObject<TestObject1>();
	testObjectPtr->Foo();

	TPtr< TestObject2 > testObject2 = CreateObject<TestObject2>();
	testObject2->Foo();
}

void ObjectSystemTest()
{
	RegisterObject< TestObject1 >();
	RegisterObject< TestObject2 >();

	Msg( "Creating object" );

	IObjectBase* pObject = g_ObjectManager.Create( RUNTIME_CLASS( TestObject1 ) );
	pObject->AddRef();

	if ( TestObject1* pTestObject = DynamicCast< TestObject1 >( pObject ) )
	{
		Msg( "DynamicCast for TestObject1 was successful" );

		pTestObject->Foo();
	}

	Msg( "Releasing object" );
	
	pObject->Release();

	TestTPtr();

}
