/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

#include "glmPODArray.h"
#include "glmPODArrayOfArray.h"
#include "glmArray.h"
#include "glmMap.h"
#include "glmSet.h"
#include "glmList.h"
#include <map>
#include <set>
#include "glmDeque.h"
#include <utility>
#include "glmUuid.h"
#include "glmStaticArray.h"
#include "glmStringOperators.h"
#include "glmVector2.h"
#include "glmVector3.h"
#include "glmVector4.h"
#include "glmAngle.h"
#include "glmQuaternion.h"
#include "glmRotationMatrix.h"
#include "glmCharOperators.h"

#include "glmMutex.h"
#include "glmScopedLock.h"

#define DECLARE_CLASS_TYPE_RTTI_ACCESSOR( ClassType ) \
	template <>	GLM_CORE_API void RTTIAccessor<ClassType>::createClassRTTI() ; \
	template <>	GLM_CORE_API void RTTIAccessor<const ClassType>::createClassRTTI() ; \
	template <>	GLM_CORE_API void RTTIAccessor<ClassType>::destroyClassRTTI() ; \
	template <>	GLM_CORE_API void RTTIAccessor<const ClassType>::destroyClassRTTI() ; \
	template <>	GLM_CORE_API void RTTIAccessor<ClassType>::setRTTI(RTTI* rtti) ; \
	template <>	GLM_CORE_API void RTTIAccessor<const ClassType>::setRTTI(RTTI* rtti) ; \
	template <>	GLM_CORE_API RTTI* RTTIAccessor<ClassType>::unsetRTTI() ; \
	template <>	GLM_CORE_API RTTI* RTTIAccessor<const ClassType>::unsetRTTI() ; \
	template <>	GLM_CORE_API const RTTI& RTTIAccessor<ClassType>::getClassRTTI() ; 
// all other classes are calling standard specializations const ClassType&, ClassType*, const ClassType* etc.

#define DECLARE_BASE_TYPE_RTTI_ACCESSOR( ClassType ) \
	DECLARE_CLASS_TYPE_RTTI_ACCESSOR( ClassType ) \
	template <>	GLM_CORE_API const RTTI& RTTIAccessor<const ClassType>::getClassRTTI() ; 
// this version adds const type as specialized getClassRTTI, only basic types <const T> makes sense, and const T specialization is forbidden for basic types.
// all other classes are calling standard specializations const T&, T*, const T* etc.

namespace glm
{
	class RTTI;
	typedef Uuid TypeID;

	// Accessor to a class RTTI, useful to access primitive types and user types RTTI in a uniform way
	template< class T >
	class RTTIAccessor
	{
	public:

		typedef T type;

		virtual ~RTTIAccessor();
		static const RTTI& getClassRTTI();
		static void createClassRTTI();
		static void destroyClassRTTI();
		static void setRTTI(RTTI* rtti); // RTTI MUST not be built in a template (RTTI::create does not check if it exists in other modules before creation), this is OK for base types, used only for base types (untemplated) RTTI
		static RTTI* unsetRTTI();

	private:

		RTTIAccessor();
		RTTIAccessor(const RTTIAccessor& rtti);
		const RTTIAccessor& operator=(const RTTIAccessor& rtti);

		static RTTI*	_rtti;
	};
	template< class T > RTTI*	RTTIAccessor<T>::_rtti(NULL);

	//-------------------------------------------------------------------------
	// init base types RTTI
	//-------------------------------------------------------------------------
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(bool)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(char)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(short)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(int)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(long)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(unsigned char)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(unsigned short)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(unsigned int)
	//	DECLARE_BASE_TYPE_RTTI_ACCESSOR( unsigned long )
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(uint64_t)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(float)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(double)
	DECLARE_BASE_TYPE_RTTI_ACCESSOR(glm::GlmString)

	DECLARE_CLASS_TYPE_RTTI_ACCESSOR(Uuid)
	DECLARE_CLASS_TYPE_RTTI_ACCESSOR(Vector2)
	DECLARE_CLASS_TYPE_RTTI_ACCESSOR(Vector3)
	DECLARE_CLASS_TYPE_RTTI_ACCESSOR(Vector4)
	DECLARE_CLASS_TYPE_RTTI_ACCESSOR(Quaternion)
	DECLARE_CLASS_TYPE_RTTI_ACCESSOR(RotationMatrix)

	//-------------------------------------------------------------------------
	template < class T >
	inline const TypeID& getTypeID()
	{
		return RTTIAccessor<T>::getClassRTTI().getTypeID();
	}

	//-------------------------------------------------------------------------
	template < class T >
	inline const char* getTypeName()
	{
		return RTTIAccessor<T>::getClassRTTI().getTypeName();
	}

	//-------------------------------------------------------------------------
	template < class T >
	RTTIAccessor< T >::RTTIAccessor()
	{}

	//-------------------------------------------------------------------------
	template < class T >
	RTTIAccessor< T >::~RTTIAccessor()
	{}

	//-------------------------------------------------------------------------
	template < class T >
	const RTTI& RTTIAccessor< T >::getClassRTTI()
	{
		return T::getClassRTTI();
	}

	//-------------------------------------------------------------------------
	template < class T >
	void RTTIAccessor< T >::createClassRTTI()
	{
		// force init of parent RTTI to be able to access it via getClassRTTI
		T::createClassRTTI(false); // do not addAttributes when creating a RTTI for an RTTIAccessor, this would cause cyclic dependencies.
	}

	//-------------------------------------------------------------------------
	template < class T >
	void RTTIAccessor< T >::destroyClassRTTI()
	{
		// force init of parent RTTI to be able to access it via getClassRTTI
		T::destroyClassRTTI(false);
	}
}
#include "glmRTTI.h"

// //-------------------------------------------------------------------------
// usage  : GLM_RTTIACCESSOR_WITH_BASECLASS(T,"e15e0d58-0e2a-4544-a85d-88104f4881a5", "%s&") 
#define GLM_RTTIACCESSOR_WITH_BASECLASS(baseType, Additional_Uuid_String, typeNameString) \
	public: \
		typedef baseType type; \
		virtual ~RTTIAccessor() {} \
		static void createClassRTTI() \
		{ \
			if (_rtti == NULL) \
			{ \
				RTTIAccessor<baseType>::createClassRTTI(); \
				TypeID typeID = Uuid(Additional_Uuid_String) + getTypeID<baseType>(); \
				const char* typeName = getTypeName<baseType>(); \
				char rttiName[GLM_MAX_PATH]; \
				glm::sprintfSafe(rttiName, GLM_ARRAY_COUNT(rttiName), typeNameString, typeName); \
				_rtti = glm::RTTI::checkBeforeCreate(typeID, rttiName); \
				if (_rtti == NULL) \
				{ \
					_rtti = RTTI::create(typeID, rttiName); \
					_rtti->addBaseClass<baseType>(); \
				} \
				else\
				{ \
					RTTIAccessor<baseType>::destroyClassRTTI();\
				} \
			} \
			_rtti->incrementCounter(); \
		} \
		static void destroyClassRTTI() \
		{ \
			if(_rtti != NULL) \
						{ \
				GLM_DEBUG_ASSERT(_rtti->getCounter() > 0); \
				_rtti->decrementCounter(); \
				if (_rtti->getCounter() == 0) \
								{ \
					_rtti->isRemoving(true); \
					_rtti->addBaseClass<baseType>(); \
					_rtti->isRemoving(false); \
					GLM_SAFE_DELETE(_rtti); \
					RTTIAccessor<baseType>::destroyClassRTTI(); \
								} \
						} \
		} \
		static const RTTI& getClassRTTI() \
		{ \
			GLM_DEBUG_ASSERT(_rtti != NULL); \
			return *_rtti; \
		} \
	private: \
		RTTIAccessor() {} \
		RTTIAccessor(const RTTIAccessor& rtti); \
		const RTTIAccessor& operator=(const RTTIAccessor& rtti); \
		static RTTI* _rtti;

// //-------------------------------------------------------------------------
// usage  : GLM_RTTIACCESSOR_CONTAINER_T(T, "f7ef84a0-6994-497f-9b90-4d37d2ef7670", "Array<%s>") , type must be defined before calling this macro, such as typedef glm::Array<T> type;
#define GLM_RTTIACCESSOR_CONTAINER_T(baseType, Additional_Uuid_String, typeNameString) \
	public:\
		virtual ~RTTIAccessor() {} \
		static void createClassRTTI() \
		{ \
			RTTIAccessor<baseType>::createClassRTTI(); \
			TypeID typeID = Uuid(Additional_Uuid_String) + getTypeID<baseType>(); \
			const char* typeName = getTypeName<baseType>(); \
			char rttiName[GLM_MAX_PATH]; \
			glm::sprintfSafe(rttiName, GLM_ARRAY_COUNT(rttiName), typeNameString, typeName); \
			RTTI* rtti = glm::RTTI::checkBeforeCreate(typeID, rttiName); \
			if (rtti == NULL) \
			{ \
				rtti = RTTI::create(typeID, rttiName, new RTTICreatorT< type >()); \
			} \
			else\
			{ \
				RTTIAccessor<baseType>::destroyClassRTTI();\
			} \
			rtti->incrementCounter(); \
		} \
		static void destroyClassRTTI() \
		{ \
			RTTI* rtti = touchClassRTTI(); \
			GLM_DEBUG_ASSERT(rtti != NULL); \
			if(rtti != NULL) \
			{ \
				GLM_DEBUG_ASSERT(rtti->getCounter() > 0); \
				rtti->decrementCounter(); \
				if (rtti->getCounter() == 0) \
				{ \
					GLM_SAFE_DELETE(rtti); \
					RTTIAccessor<baseType>::destroyClassRTTI(); \
				} \
			}\
		} \
		static const RTTI& getClassRTTI() \
		{ \
			const RTTI* rtti = touchClassRTTI(); \
			GLM_DEBUG_ASSERT(rtti != NULL); \
			return *rtti; \
		} \
	private: \
	 	RTTIAccessor() {} \
		RTTIAccessor(const RTTIAccessor& rtti); \
		const RTTIAccessor& operator=(const RTTIAccessor& rtti); \
		static RTTI* touchClassRTTI() \
		{ \
			TypeID typeID = Uuid(Additional_Uuid_String) + getTypeID<T>(); \
			const char* typeName = getTypeName<T>(); \
			char rttiName[GLM_MAX_PATH]; \
			glm::sprintfSafe(rttiName, GLM_ARRAY_COUNT(rttiName), typeNameString, typeName); \
			RTTI* rtti = glm::RTTI::checkBeforeCreate(typeID, rttiName); \
			return rtti; \
		}

// //-------------------------------------------------------------------------
// usage  : GLM_RTTIACCESSOR_CONTAINER_T0_T1(T0, T1, "f7ef84a0-6994-497f-9b90-4d37d2ef7670", "Array<%s>") , type must be defined before calling this macro, such as typedef glm::Array<T> type;
#define GLM_RTTIACCESSOR_CONTAINER_T0_T1(baseType0, baseType1, Additional_Uuid_String, typeNameString) \
	public:\
		virtual ~RTTIAccessor() {} \
		static void createClassRTTI() \
		{ \
			RTTIAccessor<baseType0>::createClassRTTI(); \
			RTTIAccessor<baseType1>::createClassRTTI(); \
			TypeID typeID = Uuid(Additional_Uuid_String) + getTypeID<baseType0>() + getTypeID<baseType1>(); \
			const char* typeName0 = getTypeName<baseType0>(); \
			const char* typeName1 = getTypeName<baseType1>(); \
			char rttiName[GLM_MAX_PATH]; \
			glm::sprintfSafe(rttiName, GLM_ARRAY_COUNT(rttiName), typeNameString, typeName0, typeName1); \
			RTTI* rtti = glm::RTTI::checkBeforeCreate(typeID, rttiName); \
			if (rtti == NULL) \
			{ \
				rtti = RTTI::create(typeID, rttiName, new RTTICreatorT< type >()); \
			} \
			else\
			{ \
				RTTIAccessor<baseType0>::destroyClassRTTI(); \
				RTTIAccessor<baseType1>::destroyClassRTTI(); \
			} \
			rtti->incrementCounter(); \
		} \
		static void destroyClassRTTI() \
		{ \
			RTTI* rtti = touchClassRTTI(); \
			GLM_DEBUG_ASSERT(rtti != NULL); \
			if(rtti != NULL) \
			{ \
				GLM_DEBUG_ASSERT(rtti->getCounter() > 0); \
				rtti->decrementCounter(); \
				if (rtti->getCounter() == 0) \
				{ \
					GLM_SAFE_DELETE(rtti); \
					RTTIAccessor<baseType1>::destroyClassRTTI(); \
					RTTIAccessor<baseType0>::destroyClassRTTI(); \
				} \
			}\
		} \
		static const RTTI& getClassRTTI() \
		{ \
			const RTTI* rtti = touchClassRTTI(); \
			GLM_DEBUG_ASSERT(rtti != NULL); \
			return *rtti; \
		} \
	private: \
	 	RTTIAccessor() {} \
		RTTIAccessor(const RTTIAccessor& rtti); \
		const RTTIAccessor& operator=(const RTTIAccessor& rtti); \
		static RTTI* touchClassRTTI() \
		{ \
			TypeID typeID = Uuid(Additional_Uuid_String) + getTypeID<baseType0>() + getTypeID<baseType1>(); \
			const char* typeName0 = getTypeName<baseType0>(); \
			const char* typeName1 = getTypeName<baseType1>(); \
			char rttiName[GLM_MAX_PATH]; \
			glm::sprintfSafe(rttiName, GLM_ARRAY_COUNT(rttiName), typeNameString, typeName0, typeName1); \
			RTTI* rtti = glm::RTTI::checkBeforeCreate(typeID, rttiName); \
			return rtti; \
		}

namespace glm
{
	void createRTTIBaseTypeAccessors();
	void destroyRTTIBaseTypeAccessors();

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor<T&>
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < T& >
	{
		GLM_RTTIACCESSOR_WITH_BASECLASS(T, "e15e0d58-0e2a-4544-a85d-88104f4881a5", "%s&")
	};

	template< class T > RTTI* RTTIAccessor<T&>::_rtti(NULL);

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor<const T&>
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < const T& >
	{
		GLM_RTTIACCESSOR_WITH_BASECLASS(T, "7f18b132-84e3-4284-a123-2be7989e0848", "const %s&")
	};

	template< class T > RTTI* RTTIAccessor<const T&>::_rtti(NULL);
	
	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor<T*>
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < T* >
	{
		GLM_RTTIACCESSOR_WITH_BASECLASS(T, "ee83e2b7-4c36-4965-9b02-5146e3ea72d0", "%s*")
	};

	template< class T > RTTI* RTTIAccessor<T*>::_rtti(NULL);
	
	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor<const T*>
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < const T* >
	{
		GLM_RTTIACCESSOR_WITH_BASECLASS(T, "6af6f9e5-292f-41ec-956c-a5eed421d056", "const %s*")
	};

	template< class T > RTTI* RTTIAccessor<const T*>::_rtti(NULL);
	
	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor<T* const>
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < T* const >
	{
		GLM_RTTIACCESSOR_WITH_BASECLASS(T, "108c67b0-3ac6-44d7-b737-ca77d57b63ea", "%s* const")
	};

	template< class T > RTTI* RTTIAccessor<T* const>::_rtti(NULL);

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::Array<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < glm::Array<T> >
	{
	public:
		typedef glm::Array<T> type;

		GLM_RTTIACCESSOR_CONTAINER_T(T, "f7ef84a0-6994-497f-9b90-4d37d2ef7670", "Array<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::PODArray<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < glm::PODArray<T> >
	{
	public:
		typedef glm::PODArray<T> type;
		GLM_RTTIACCESSOR_CONTAINER_T(T, "d82fbb17-65dd-4579-a031-d68f04d44b5c", "PODArray<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::PODArrayOfArray<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < glm::PODArrayOfArray<T> >
	{
	public:
		typedef glm::PODArrayOfArray<T> type;
		GLM_RTTIACCESSOR_CONTAINER_T(T, "475cd26a-836e-42e6-a486-d870d622b1b9", "PODArrayOfArray<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::GlmDeque<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < glm::GlmDeque<T> >
	{
	public:
		typedef glm::GlmDeque<T> type;
		GLM_RTTIACCESSOR_CONTAINER_T(T, "0006b8b8-6c6a-4659-9501-cb1944fee342", "deque<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::GlmList<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < glm::GlmList<T> >
	{
	public:

		typedef glm::GlmList<T> type;
		GLM_RTTIACCESSOR_CONTAINER_T(T, "47852afd-bdaa-47bc-ab03-d42d64eafc65", "list<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< std::set<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < std::set<T> >
	{
	public:
		typedef std::set<T> type;
		GLM_RTTIACCESSOR_CONTAINER_T(T, "6fe4730d-49f9-402a-a4a6-4de2f25f69a9", "set<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< std::map<T0, T1> >
	//-------------------------------------------------------------------------
	template< class T0, class T1 >
	class RTTIAccessor < std::map<T0, T1> >
	{
	public:

		typedef std::map<T0, T1> type;
		GLM_RTTIACCESSOR_CONTAINER_T0_T1(T0, T1, "3681cd89-d006-4841-8a54-f9c3dc12834b", "map<%s,%s>")
	};


	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::GlmSet<T> >
	//-------------------------------------------------------------------------
	template< class T >
	class RTTIAccessor < glm::GlmSet<T> >
	{
	public:

		typedef glm::GlmSet<T> type;
		GLM_RTTIACCESSOR_CONTAINER_T(T, "89a149a9-e5cd-4d5d-8f7d-5f7a2c62beb3", "GlmSet<%s>")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< glm::GlmMap<T0, T1> >
	//-------------------------------------------------------------------------
	template< class T0, class T1 >
	class RTTIAccessor < glm::GlmMap<T0, T1> >
	{
	public:

		typedef glm::GlmMap<T0, T1> type;
		GLM_RTTIACCESSOR_CONTAINER_T0_T1(T0, T1, "812c7245-c0cf-4918-9bbe-a257b553e850", "GlmMap<%s,%s>*")
	};

	//-------------------------------------------------------------------------
	// partial specialization RTTIAccessor< std::pair<T0, T1> >
	//-------------------------------------------------------------------------
	template< class T0, class T1 >
	class RTTIAccessor < std::pair<T0, T1> >
	{
	public:

		typedef std::pair<T0, T1> type;
		GLM_RTTIACCESSOR_CONTAINER_T0_T1(T0, T1, "ac77798c-5da6-401b-9e87-ffe02cfe183a", "pair<%s,%s>*")
	};
}
