#include "EABaseTypes/metatypes.h"
#include "EABaseTypes/datatypes.h"

#if !defined(EA_FINAL)

#include "EABaseTypes/factory.h"
#include "EABaseTypes/typechecking.h"

#include "coreallocator/icoreallocatormacros.h"
#include "EAAssert/eaassert.h"

namespace EA
{
	namespace Types
	{
		MetaTypes::MetaTypes()
			: mTypes()
		{
		}

		MetaTypes::~MetaTypes()
		{
			EA_ASSERT(mTypes == NULL);
		}

		void MetaTypes::Load(ArrayIn dataArr)
		{
			if(mTypes == NULL)
			{
				mTypes = dataArr->GetFactory()->Object();
			}

			// loop thru the array of types and insert in the type storage
			// 1. new types are added to the object
			// 2. existing types are replaced if a new definition is loaded
			// 3. existing types remain if no new definition is found

			Array::const_iterator it = dataArr->begin();
			Array::const_iterator end = dataArr->end();
			for(; it != end; ++it)
			{
				ObjectRef entry = (*it)->AsObject();
				EA_ASSERT_MESSAGE(entry->contains("name"), ("metatypes must have a name"));
				EA_ASSERT_MESSAGE(entry->contains("type"), ("metatypes must have a type entry"));
				
				const EA::String& name = *((*entry)["name"]->AsString());
				BaseTypeRef type = (*entry)["type"];
				EA_ASSERT_FORMATTED(IsType(this, type), ("%s does not define a valid type", name.c_str()));
				
				mTypes->insert(name).first->second = type;
			}
		}

		void MetaTypes::Unload()
		{
			mTypes = NULL;
		}

		bool MetaTypes::IsMetaType(const char* type)
		{
			bool result = false;
			if(mTypes != NULL && mTypes->contains(type)) 
			{
				result = true;
			}
			return result;
		}

		bool MetaTypes::CheckMetaType(BaseTypeIn data, const char* type, EA::RawString& message)
		{
			bool result = false;
			if(mTypes != NULL && mTypes->contains(type))
			{
				result = CheckType(this, data, (*mTypes)[type], message);
			}

			// should we assert here?
			return result;
		}

		ConstObjectReturn MetaTypes::GetTypes()
		{
			return mTypes;
		}

	}
}

#else // !defined(EA_FINAL)

namespace EA
{
	namespace Types
	{
		MetaTypes::MetaTypes()
			: mTypes(NULL)
		{
		}

		MetaTypes::~MetaTypes()
		{
		}

		void MetaTypes::Load(ArrayIn dataArr)
		{
		}

		void MetaTypes::Unload()
		{
		}

		bool MetaTypes::IsMetaType(const char* type)
		{
			return true;
		}

		//lint -efunc(1764, EA::Types::MetaTypes::CheckMetaType)
		bool MetaTypes::CheckMetaType(BaseTypeIn data, const char* type, EA::RawString& message)
		{
			return true;
		}

		ConstObjectReturn MetaTypes::GetTypes()
		{
			return ConstObjectReturn(NULL);
		}
	}
}

#endif // defined(EA_FINAL)
