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

#pragma once
#include <deque>
#include <set>
#include "glmQueue.h"
#include <map>
#include <utility>
#include "glmDataItem.h"
#include "glmMap.h"
#include "glmSet.h"
#include "glmLog.h"
#include "glmTypes.h"

//-------------------------------------------------------------------------
#define XML_SERIALIZE_ATTRIBUTE( Attribute ) \
	void read( const DataItem& configurationItem, Attribute& value, bool = false ) { doReadAttribute( configurationItem, value, "value" ) ; } \
	void write( DataItem& configurationItem, Attribute value ) { doWriteAttribute( configurationItem, value, "value" ) ;	} \
	XML_SERIALIZE_POINTER( Attribute )

//-------------------------------------------------------------------------
#define XML_SERIALIZE_POINTER( Pointer ) \
	void read( const DataItem& configurationItem, Pointer*& value ) { doRead( configurationItem, value ) ; } \
	void write( DataItem& configurationItem, Pointer* value ) { doWrite( configurationItem, value ) ; } \
	void read( const DataItem& configurationItem, const Pointer*& value ) { doRead( configurationItem, value ) ; } \
	void write( DataItem& configurationItem, const Pointer* value ) { doWrite( configurationItem, value ) ; }

//-------------------------------------------------------------------------
#define XML_SERIALIZE_POINTER_T( Pointer ) \
	template <class T> void read( const DataItem& configurationItem, Pointer<T>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T> void write( DataItem& configurationItem, Pointer<T>* value ) { doWrite( configurationItem, value ) ; } \
	template <class T> void read(  const DataItem& configurationItem, const Pointer<T>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T> void write( DataItem& configurationItem, const Pointer<T>* value ) { doWrite( configurationItem, value ) ; }

//-------------------------------------------------------------------------
#define XML_SERIALIZE_POINTER_T0_T1( Pointer ) \
	template <class T0, class T1> void read( const DataItem& configurationItem, Pointer<T0, T1>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T0, class T1> void write( DataItem& configurationItem, Pointer<T0, T1>* value ) { doWrite( configurationItem, value ) ; } \
	template <class T0, class T1> void read(  const DataItem& configurationItem, const Pointer<T0, T1>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T0, class T1> void write( DataItem& configurationItem, const Pointer<T0, T1>* value ) { doWrite( configurationItem, value ) ; }

//-------------------------------------------------------------------------
#define XML_SERIALIZE_POINTER_T0_T1_T2( Pointer ) \
	template <class T0, class T1, class T2> void read( const DataItem& configurationItem, Pointer<T0, T1, T2>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T0, class T1, class T2> void write( DataItem& configurationItem, Pointer<T0, T1, T2>* value ) { doWrite( configurationItem, value ) ; } \
	template <class T0, class T1, class T2> void read(  const DataItem& configurationItem, const Pointer<T0, T1, T2>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T0, class T1, class T2> void write( DataItem& configurationItem, const Pointer<T0, T1, T2>* value ) { doWrite( configurationItem, value ) ; }

//-------------------------------------------------------------------------
#define XML_SERIALIZE_POINTER_T0_T1_T2_T3( Pointer ) \
	template <class T0, class T1, class T2, class T3> void read( const DataItem& configurationItem, Pointer<T0, T1, T2, T3>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T0, class T1, class T2, class T3> void write( DataItem& configurationItem, Pointer<T0, T1, T2, T3>* value ) { doWrite( configurationItem, value ) ; } \
	template <class T0, class T1, class T2, class T3> void read(  const DataItem& configurationItem, const Pointer<T0, T1, T2, T3>*& value ) { doRead( configurationItem, value ) ; } \
	template <class T0, class T1, class T2, class T3> void write( DataItem& configurationItem, const Pointer<T0, T1, T2, T3>* value ) { doWrite( configurationItem, value ) ; }

namespace glm
{
	class Vector2;
	class Vector3;
	class Vector4;
	template <class T> class Array;
	template <class T> class PODArray;
	template <class T> class PODArrayOfArray;

	template <class T> class SmartPointer;
	class ReferenceCounter;

	/**
	* \brief XMLStream for XML serialization
	*
	* XMLStream aggregates a stringstream and read/write the result of classes serialization as text.
	*
	* \ingroup serialization
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_CORE_API XMLStream
	{
	public:

		XMLStream();
		~XMLStream();

		DataItem& getRoot();
		bool isFirstPass();
		void setFirstPass(bool firstPass); //pack and unpack are two-pass algorithms, this method is used for testing and debugging

		template <class Type> void unpack(const DataItem& configurationItem, Type& inspectableObject);
		template <class Type> void pack(DataItem& configurationItem, const Type& inspectableObject);

		template <class Type> void unpack(const DataItem& configurationItem, glm::PODArray< Type* >& inspectableObjects);
		template <class Type> void pack(DataItem& configurationItem, const glm::PODArray< const Type* >& inspectableObjects);

		SaveXMLDataFileErrorCode::Value  exportTo(const char* fileName);
		void importFrom(const char* fileName);

		XML_SERIALIZE_ATTRIBUTE(bool);
		XML_SERIALIZE_ATTRIBUTE(short);
		XML_SERIALIZE_ATTRIBUTE(unsigned short);
		XML_SERIALIZE_ATTRIBUTE(int);
		XML_SERIALIZE_ATTRIBUTE(unsigned int);
		XML_SERIALIZE_ATTRIBUTE(long);
		XML_SERIALIZE_ATTRIBUTE(uint64_t);
		XML_SERIALIZE_ATTRIBUTE(float);
		XML_SERIALIZE_ATTRIBUTE(double);
		XML_SERIALIZE_ATTRIBUTE(glm::GlmString);

		void read(const DataItem& configurationItem, char& value, const char* attributeName = "value");
		void write(DataItem& configurationItem, char value, const char* attributeName = "value");
		void read(const DataItem& configurationItem, const char*& value, const char* attributeName = "value");
		void write(DataItem& configurationItem, const char* value, const char* attributeName = "value");
		void read(const DataItem& configurationItem, Uuid& uuid, const char* attributeName = "value");
		void write(DataItem& configurationItem, const Uuid& uuid, const char* attributeName = "value");
		XML_SERIALIZE_POINTER(Uuid);

		template<class FirstType, class SecondType>	void read(const DataItem& configurationItem, std::pair<FirstType, SecondType> & value);
		template<class FirstType, class SecondType>	void write(DataItem& configurationItem, const std::pair<FirstType, SecondType> & value);
		XML_SERIALIZE_POINTER_T0_T1(std::pair);

		template <class T> void read(const DataItem& configurationItem, glm::Array<T>& value);
		template <class T> void read(const DataItem& configurationItem, glm::PODArray<T>& value);
		template <class T> void read(const DataItem& configurationItem, glm::PODArrayOfArray<T>& value);
		template <class T> void write(DataItem& configurationItem, const glm::Array<T>& value);
		template <class T> void write(DataItem& configurationItem, const glm::PODArray<T>& value);
		template <class T> void write(DataItem& configurationItem, const glm::PODArrayOfArray<T>& value);
		XML_SERIALIZE_POINTER_T(glm::Array);
		XML_SERIALIZE_POINTER_T(glm::PODArray);
		XML_SERIALIZE_POINTER_T(glm::PODArrayOfArray);

		template <class T> void read(const DataItem& configurationItem, glm::GlmDeque<T>& value);
		template <class T> void write(DataItem& configurationItem, const glm::GlmDeque<T>& value);
		XML_SERIALIZE_POINTER_T(glm::GlmDeque);

		template <class T> void read(const DataItem& configurationItem, glm::GlmList<T>& value);
		template <class T> void write(DataItem& configurationItem, const glm::GlmList<T>& value);

		XML_SERIALIZE_POINTER_T(glm::GlmList);

		// --- Vector

		void read(const DataItem& configurationItem, Vector2& value);
		void read(const DataItem& configurationItem, Vector3& value);
		void read(const DataItem& configurationItem, Vector4& value);
		void read(const DataItem& configurationItem, Quaternion& value);

		void write(DataItem& configurationItem, const Vector2& value);
		void write(DataItem& configurationItem, const Vector3& value);
		void write(DataItem& configurationItem, const Vector4& value);
		void write(DataItem& configurationItem, const Quaternion& value);

		void read(const DataItem& configurationItem, Vector2*& value);
		void read(const DataItem& configurationItem, Vector3*& value);
		void read(const DataItem& configurationItem, Vector4*& value);
		void read(const DataItem& configurationItem, Quaternion*& value);

		void write(DataItem& configurationItem, Vector2* value);
		void write(DataItem& configurationItem, Vector3* value);
		void write(DataItem& configurationItem, Vector4* value);
		void write(DataItem& configurationItem, Quaternion* value);

		void read(const DataItem& configurationItem, const Vector2*& value);
		void read(const DataItem& configurationItem, const Vector3*& value);
		void read(const DataItem& configurationItem, const Vector4*& value);
		void read(const DataItem& configurationItem, const Quaternion*& value);

		void write(DataItem& configurationItem, const Vector2* value);
		void write(DataItem& configurationItem, const Vector3* value);
		void write(DataItem& configurationItem, const Vector4* value);
		void write(DataItem& configurationItem, const Quaternion* value);

		template <class Type, class Comparator>	void read(const DataItem& configurationItem, std::set<Type, Comparator> & value);
		template <class Type, class Comparator>	void write(DataItem& configurationItem, const std::set<Type, Comparator>& value);
		XML_SERIALIZE_POINTER_T0_T1(std::set);

		template <class KeyType, class Type, class Comparator> void read(const DataItem& configurationItem, std::map<KeyType, Type, Comparator> & value);
		template <class KeyType, class Type, class Comparator> void write(DataItem& configurationItem, const std::map<KeyType, Type, Comparator>& value);
		XML_SERIALIZE_POINTER_T0_T1_T2(std::map);

		template <class Type>	void read(const DataItem& configurationItem, glm::GlmSet<Type> & value);
		template <class Type>	void write(DataItem& configurationItem, const glm::GlmSet<Type>& value);
		XML_SERIALIZE_POINTER_T(glm::GlmSet);

		template <class KeyType, class Type> void read(const DataItem& configurationItem, glm::GlmMap<KeyType, Type> & value);
		template <class KeyType, class Type> void write(DataItem& configurationItem, const glm::GlmMap<KeyType, Type>& value);
		XML_SERIALIZE_POINTER_T0_T1(glm::GlmMap);

		template <class TPointeeType> void read(const DataItem& configurationItem, SmartPointer<TPointeeType>& value);
		template <class TPointeeType> void write(DataItem& configurationItem, const SmartPointer<TPointeeType>& value);

		template <class TPointeeType> void read(const DataItem& configurationItem, TPointeeType*& value);
		template <class TPointeeType> void write(DataItem& configurationItem, TPointeeType* value);

		template <class TPointeeType> void read(const DataItem& configurationItem, const TPointeeType*& value);
		template <class TPointeeType> void write(DataItem& configurationItem, const TPointeeType* value);

		template <class Type> void read(const DataItem& configurationItem, Type& value);
		template <class Type> void write(DataItem& configurationItem, const Type& value, bool isValue = false);

		static void init();

		glm::GlmMap<unsigned int, void*> _idToPointerMap;

		void pushValueInFifo(void*);
		void* popValuefromFifo();

	private:

		GLM_DISALLOW_COPY_AND_ASSIGN(XMLStream);

		template <class TValue > void doReadAttribute(const DataItem& configurationItem, TValue& value, const char* attributeName = "value");
		template <class TValue > void doWriteAttribute(DataItem& configurationItem, TValue value, const char* attributeName = "value");

		template <class TPointeeType> void doRead(const DataItem& configurationItem, TPointeeType*& value);
		template <class TPointeeType> void doWrite(DataItem& configurationItem, TPointeeType* value);

		template <class TPointeeType> void doRead(const DataItem& configurationItem, const TPointeeType*& value);
		template <class TPointeeType> void doWrite(DataItem& configurationItem, const TPointeeType* value);

		void clear(); //clear the maps, set the firstPass flag to true

		glm::GlmMap<const void*, unsigned int> _pointerToIdMap;
		glm::GlmMap<void*, ReferenceCounter*> _idToSmartPointerMap;
		glm::GlmSet<const void*> _inspectablesPointersSet;
		DataItem _configurationItem;
		bool _firstPass;
		unsigned int _pointerId;
		static const char* dimensionName[4];
		glm::GlmQueue< void* > _queueOfAccessorAttributeValues; // used between two passes to keep containers allocated instead of reallocating everything.
	};
}

#include "glmArray.h"
#include "glmPODArray.h"
#include "glmPODArrayOfArray.h"
#include "glmDataItem.h"
#include "glmSmartPointer.h"
#include "glmVector2.h"
#include "glmVector3.h"
#include "glmVector4.h"
#include "glmAngle.h"

namespace glm
{
	//-------------------------------------------------------------------------
	inline DataItem& XMLStream::getRoot()
	{
		return _configurationItem;
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::unpack(const DataItem& configurationItem, Type& inspectableObject)
	{
		//clear();
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& child = *configurationItem.getChild(i);
			const RTTI* childRTTI = RTTI::getRTTI(RTTI::getTypeID(child.getAttribute<GlmString>("typeName", getTypeName<Type>()).c_str()));
			if ((childRTTI != NULL) && (childRTTI->isDerivedFrom(Type::getClassRTTI())))
			{
				read(child, inspectableObject);
				_pointerId = 1000000;
				_firstPass = false;
				read(child, inspectableObject);
				_pointerId = 1000000;
				_firstPass = true;
				return;
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::pack(DataItem& configurationItem, const Type& inspectableObject)
	{
		//clear();
		DataItem& child = configurationItem.addChild();
		child.setName("golaemObject");

		write(child, inspectableObject);
		_firstPass = false;
		write(child, inspectableObject);
		_firstPass = true;

		if (child.getAttribute("typeName") == NULL)
		{
			child.addAttribute<glm::GlmString>("typeName", inspectableObject.getRTTI().getTypeName());
		}
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::unpack(const DataItem& configurationItem, glm::PODArray< Type* >& inspectableObjects)
	{
		//clear()
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& child = *configurationItem.getChild(i);
			const RTTI* childRTTI = RTTI::getRTTI(RTTI::getTypeID(child.getAttribute<GlmString>("typeName", getTypeName<Type>()).c_str()));
			if ((childRTTI != NULL) && (childRTTI->isDerivedFrom(Type::getClassRTTI())))
			{
				Type* serializableObject = NULL;
				read(child, serializableObject);
			}
		}

		_pointerId = 1000000;
		_firstPass = false;

		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& child = *configurationItem.getChild(i);
			const RTTI* childRTTI = RTTI::getRTTI(RTTI::getTypeID(child.getAttribute<GlmString>("typeName", getTypeName<Type>()).c_str()));
			if ((childRTTI != NULL) && (childRTTI->isDerivedFrom(Type::getClassRTTI())))
			{
				Type* serializableObject = NULL;
				read(child, serializableObject);
				inspectableObjects.push_back(serializableObject);
			}
		}

		_pointerId = 1000000;
		_firstPass = true;
	}


	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::pack(DataItem& configurationItem, const glm::PODArray< const Type* >& inspectableObjects)
	{
		//clear();
		for (typename glm::PODArray< const Type* >::const_iterator iteVector = inspectableObjects.begin(); iteVector != inspectableObjects.end(); ++iteVector)
		{
			write(configurationItem, *iteVector);
		}

		_firstPass = false;

		configurationItem.reserveChildrenSpace(inspectableObjects.size());

		for (typename glm::PODArray< const Type* >::const_iterator iteVector = inspectableObjects.begin(); iteVector != inspectableObjects.end(); ++iteVector)
		{
			DataItem& child = configurationItem.addChild();
			child.setName("golaemObject");
			write(child, *iteVector);
			if (child.getAttribute("typeName") == NULL)
			{
				child.addAttribute<glm::GlmString>("typeName", (*iteVector)->getRTTI().getTypeName());
			}
		}

		_firstPass = true;
	}

	//------------------------------------------------------------------------------------------
	inline void XMLStream::read(const DataItem& configurationItem, char& value, const char* attributeName)
	{
		doReadAttribute(configurationItem, value, attributeName);
	}

	//------------------------------------------------------------------------------------------
	inline void	XMLStream::write(DataItem& configurationItem, char value, const char* attributeName)
	{
		doWriteAttribute(configurationItem, value, attributeName);
	}

	//------------------------------------------------------------------------------------------
	inline void	XMLStream::read(const DataItem& configurationItem, const char*& value, const char* attributeName)
	{
		value = configurationItem.getAttribute(attributeName);
	}

	//------------------------------------------------------------------------------------------
	inline void	XMLStream::write(DataItem& configurationItem, const char* value, const char* attributeName)
	{
		doWriteAttribute(configurationItem, value, attributeName);
	}

	//------------------------------------------------------------------------------------------
	inline void	XMLStream::read(const DataItem& configurationItem, Uuid& uuid, const char* attributeName)
	{
		uuid = configurationItem.getAttribute(attributeName);
	}

	//------------------------------------------------------------------------------------------
	inline void	XMLStream::write(DataItem& configurationItem, const Uuid& uuid, const char* attributeName)
	{
		configurationItem.addAttribute(attributeName, uuid.getString());
	}

	//-------------------------------------------------------------------------
	template<class FirstType, class SecondType>
	void XMLStream::read(const DataItem& configurationItem, std::pair<FirstType, SecondType> & value)
	{
		const DataItem* firstChild(configurationItem.getChild(static_cast<unsigned int>(0)));
		GLM_ASSERT(firstChild != NULL);
		const DataItem* secondChild(configurationItem.getChild(1));
		GLM_ASSERT(secondChild != NULL);
		if (firstChild->getAttribute<unsigned int>("index") == 0)
		{
			read(*firstChild, value.first);
			read(*secondChild, value.second);
		}
		else
		{
			read(*firstChild, value.second);
			read(*secondChild, value.first);
		}
	}

	//-------------------------------------------------------------------------
	template<class FirstType, class SecondType>
	void XMLStream::write(DataItem& configurationItem, const std::pair<FirstType, SecondType> & value)
	{
		if (_firstPass)
		{
			DataItem firstValue("element");
			firstValue.addAttribute("index", 0);
			write(firstValue, value.first);

			DataItem secondValue("element");
			secondValue.addAttribute("index", 1);
			write(secondValue, value.second);
		}
		else
		{
			configurationItem.reserveChildrenSpace(2);

			DataItem& firstValue = configurationItem.addChild();
			firstValue.setName("element");
			firstValue.addAttribute("index", 0);
			write(firstValue, value.first);

			DataItem& secondValue = configurationItem.addChild();
			secondValue.setName("element");
			secondValue.addAttribute("index", 1);
			write(secondValue, value.second);
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::read(const DataItem& configurationItem, glm::Array<T>& value)
	{
		if (_firstPass)
		{
			value.clear();
			value.resize(configurationItem.getChildrenCount());
		}
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& dataItem(*configurationItem.getChild(i));
			read(dataItem, value[dataItem.getAttribute<unsigned int>("index", i)]);
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::write(DataItem& configurationItem, const glm::Array<T>& value)
	{
		if (_firstPass)
		{
			for (size_t i(0); i < value.size(); ++i)
			{
				DataItem element("element");
				element.addAttribute("index", i);
				write(element, value[i]);
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (size_t i(0); i < value.size(); ++i)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				element.addAttribute("index", i);
				write(element, value[i]);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::read(const DataItem& configurationItem, glm::PODArray<T>& value)
	{
		if (_firstPass)
		{
			value.clear();
			value.resize(configurationItem.getChildrenCount());
		}
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& dataItem(*configurationItem.getChild(i));
			read(dataItem, value[dataItem.getAttribute<unsigned int>("index", i)]);
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::write(DataItem& configurationItem, const glm::PODArray<T>& value)
	{
		if (_firstPass)
		{
			for (size_t i(0); i < value.size(); ++i)
			{
				DataItem element("element");
				element.addAttribute("index", i);
				write(element, value[static_cast<uint32_t>(i)]);
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (size_t i(0); i < value.size(); ++i)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				element.addAttribute("index", i);
				write(element, value[static_cast<uint32_t>(i)]);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::read(const DataItem& configurationItem, glm::PODArrayOfArray<T>& value)
	{
		const size_t arrayCount = configurationItem.getChildrenCount();
		const size_t itemCountAll = configurationItem.getAttribute<size_t>("size_items");
		if (_firstPass)
		{
			value.clear();

			if (arrayCount > 0 && itemCountAll > 0)
			{
				value.reserve(arrayCount, itemCountAll);
			}

			for (unsigned int childArrayIndex = 0; childArrayIndex < arrayCount; ++childArrayIndex)
			{
				const DataItem& arrayElement(*configurationItem.getChild(childArrayIndex));
				GLM_ASSERT_MSG(arrayElement.getAttribute<unsigned int>("index") == childArrayIndex, "Can't push child arrays out of order to glm::PODArrayOfArray<T>");

				const unsigned int itemCount = arrayElement.getChildrenCount();
				for (unsigned int childItemIndex = 0; childItemIndex < itemCount; ++childItemIndex)
				{
					const DataItem& itemElement(*arrayElement.getChild(childItemIndex));
					T item;
					read(itemElement, item);
				}
			}
		}
		else
		{
			for (unsigned int childArrayIndex = 0; childArrayIndex < arrayCount; ++childArrayIndex)
			{
				const DataItem& arrayElement(*configurationItem.getChild(childArrayIndex));
				const unsigned int arrayIndex = arrayElement.getAttribute<unsigned int>("index");
				GLM_DEBUG_ASSERT_MSG(arrayIndex == childArrayIndex, "Can't push child arrays out of order to glm::PODArrayOfArray<T>");

				const unsigned int arrayCapacity = arrayElement.getAttribute<unsigned int>("capacity");
				const unsigned int itemCount = arrayElement.getChildrenCount();
				value.push_back_array(itemCount, arrayCapacity);

				for (unsigned int childItemIndex = 0; childItemIndex < itemCount; ++childItemIndex)
				{
					const DataItem& itemElement(*arrayElement.getChild(childItemIndex));
					const unsigned int itemIndex = itemElement.getAttribute<unsigned int>("index");
					read(itemElement, value.at(arrayIndex, itemIndex));
				}
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::write(DataItem& configurationItem, const glm::PODArrayOfArray<T>& value)
	{
		if (_firstPass)
		{
			const size_t arrayCount = value.size_arrays();
			for (size_t arrayIndex = 0; arrayIndex < arrayCount; ++arrayIndex)
			{
				DataItem arrayElement("element");
				const size_t itemCount = value.size_items(arrayIndex);
				for (size_t itemIndex = 0; itemIndex < itemCount; ++itemIndex)
				{
					DataItem itemElement("element");
					write(itemElement, value.at(arrayIndex, itemIndex));
				}
			}
		}
		else
		{
			configurationItem.addAttribute("size_items", value.size_items());

			configurationItem.reserveChildrenSpace(value.size_arrays());

			const unsigned int arrayCount = static_cast<unsigned int>(value.size_arrays());
			for (unsigned int arrayIndex = 0; arrayIndex < arrayCount; ++arrayIndex)
			{
				DataItem& arrayElement = configurationItem.addChild();
				arrayElement.setName("element");
				arrayElement.addAttribute("index", arrayIndex);
				const unsigned int capacity = static_cast<unsigned int>(value.capacity_items(arrayIndex));
				arrayElement.addAttribute("capacity", capacity);

				const unsigned int itemCount = static_cast<unsigned int>(value.size_items(arrayIndex));
				for (unsigned int itemIndex = 0; itemIndex < itemCount; ++itemIndex)
				{
					DataItem& itemElement = arrayElement.addChild();
					itemElement.setName("element");
					itemElement.addAttribute("index", itemIndex);

					write(itemElement, value.at(arrayIndex, itemIndex));
				}
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::read(const DataItem& configurationItem, glm::GlmList<T>& value)
	{
		if (_firstPass)
		{
			value.clear();
			typename glm::GlmList<T>::iterator ite(value.begin());
			T element;
			for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
			{
				const DataItem& dataItem(*configurationItem.getChild(i));
				read(dataItem, element);
				ite = value.begin();
				for (unsigned int j(0); j < dataItem.getAttribute<unsigned int>("index"); ++j)
				{
					++ite;
				}
				value.insert(ite, element);
			}
		}
		else
		{
			typename glm::GlmList<T>::iterator ite(value.begin());
			//T element ;
			for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
			{
				const DataItem& dataItem(*configurationItem.getChild(i));
				ite = value.begin();
				for (unsigned int j(0); j < dataItem.getAttribute<unsigned int>("index"); ++j)
				{
					++ite;
				}
				// read directly to found element
				read(dataItem, *ite);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::write(DataItem& configurationItem, const glm::GlmList<T>& value)
	{
		typename glm::GlmList<T>::const_iterator ite(value.begin());
		unsigned int i(0);
		if (_firstPass)
		{
			for (; ite != value.end(); ++ite)
			{
				DataItem element("element");
				element.addAttribute("index", i);
				++i;
				write(element, *ite);
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (; ite != value.end(); ++ite)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				element.addAttribute("index", i);
				++i;
				write(element, *ite);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::read(const DataItem& configurationItem, glm::GlmDeque<T>& value)
	{
		if (_firstPass)
		{
			value.clear();
			T element;
			int maxIndex = -1;
			// assume that index are in range (to avoid expensive insert method)
			for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
			{
				const DataItem& dataItem(*configurationItem.getChild(i));
				read(dataItem, element);
				maxIndex = glm::max(maxIndex, static_cast<int>(dataItem.getAttribute<unsigned int>("index")));
				unsigned int currentIndex = dataItem.getAttribute<unsigned int>("index");
				//ite = value.begin() + currentIndex;
				GLM_DEBUG_ASSERT_MSG(currentIndex == i, "Please, check that Deque elements are declared in index order 0 to n for '%s'", configurationItem.getName());
				GLM_UNREFERENCED(currentIndex);
				value.push_back(element);
				//value.insert(ite, element); 
			}
			if (maxIndex != -1)
			{
				value.resize(maxIndex + 1);
			}
		}
		else
		{
			typename glm::GlmDeque<T>::iterator ite(value.begin());
			for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
			{
				const DataItem& dataItem(*configurationItem.getChild(i));
				ite = value.begin() + dataItem.getAttribute<unsigned int>("index");
				read(dataItem, *ite);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class T>
	void XMLStream::write(DataItem& configurationItem, const glm::GlmDeque<T>& value)
	{
		if (_firstPass)
		{
			for (size_t i(0); i < value.size(); ++i)
			{

				DataItem element("element");
				element.addAttribute("index", i);
				write(element, *(value.begin() + i));
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (size_t i(0); i < value.size(); ++i)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				element.addAttribute("index", i);
				write(element, *(value.begin() + i));
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class Type, class Comparator>
	void XMLStream::read(const DataItem& configurationItem, std::set<Type, Comparator> & value)
	{
		// std::set are not usable with IInspectable objects having dynamicArrayAttributes or pointers, as they overwrite the first pass object in second pass, messing potential arrays and pointers

		value.clear();
		Type element;
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& dataItem(*configurationItem.getChild(i));
			read(dataItem, element);
			value.insert(element);
		}
	}

	//-------------------------------------------------------------------------
	template <class Type, class Comparator>
	void XMLStream::write(DataItem& configurationItem, const std::set<Type, Comparator>& value)
	{
		typename std::set<Type, Comparator>::const_iterator ite(value.begin());
		if (_firstPass)
		{
			for (; ite != value.end(); ++ite)
			{

				DataItem element("element");
				write(element, *ite);
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (; ite != value.end(); ++ite)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				write(element, *ite);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class KeyType, class Type, class Comparator>
	void XMLStream::read(const DataItem& configurationItem, std::map<KeyType, Type, Comparator> & value)
	{
		if (_firstPass)
		{
			value.clear();
		}

		KeyType key;
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& dataItem(*configurationItem.getChild(i));

			const DataItem* firstChild(dataItem.getChild(static_cast<unsigned int>(0)));
			GLM_ASSERT(firstChild != NULL);
			const DataItem* secondChild(dataItem.getChild(1));
			GLM_ASSERT(secondChild != NULL);

			if (firstChild->getAttribute<unsigned int>("index") == 0)
			{
				read(*firstChild, key);
				read(*secondChild, value[key]);
			}
			else
			{
				read(*secondChild, key);
				read(*firstChild, value[key]);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class KeyType, class Type, class Comparator>
	void XMLStream::write(DataItem& configurationItem, const std::map<KeyType, Type, Comparator>& value)
	{
		typename std::map<KeyType, Type, Comparator>::const_iterator it(value.begin());
		if (_firstPass)
		{
			for (; it != value.end(); ++it)
			{
				DataItem element("element");
				write(element, *it);
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (; it != value.end(); ++it)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				write(element, *it);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::read(const DataItem& configurationItem, glm::GlmSet<Type> & value)
	{
		// glm::GlmSet are not usable with IInspectable objects having dynamicArrayAttributes or pointers, as they overwrite the first pass object in second pass, messing potential arrays and pointers

		value.clear();
		Type element;
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& dataItem(*configurationItem.getChild(i));
			read(dataItem, element);
			value.insert(element);
		}
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::write(DataItem& configurationItem, const glm::GlmSet<Type>& value)
	{
		typename glm::GlmSet<Type>::const_iterator ite(value.begin());
		if (_firstPass)
		{
			for (; ite != value.end(); ++ite)
			{

				DataItem element("element");
				write(element, *ite);
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (; ite != value.end(); ++ite)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				write(element, *ite);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class KeyType, class Type>
	void XMLStream::read(const DataItem& configurationItem, glm::GlmMap<KeyType, Type> & value)
	{
		if (_firstPass)
		{
			value.clear();
		}

		KeyType key;
		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const DataItem& dataItem(*configurationItem.getChild(i));

			const DataItem* firstChild(dataItem.getChild(static_cast<unsigned int>(0)));
			GLM_ASSERT(firstChild != NULL);
			const DataItem* secondChild(dataItem.getChild(1));
			GLM_ASSERT(secondChild != NULL);

			if (firstChild->getAttribute<unsigned int>("index") == 0)
			{
				read(*firstChild, key);
				read(*secondChild, value[key]);
			}
			else
			{
				read(*secondChild, key);
				read(*firstChild, value[key]);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class KeyType, class Type>
	void XMLStream::write(DataItem& configurationItem, const glm::GlmMap<KeyType, Type>& value)
	{
		typename glm::GlmMap<KeyType, Type>::const_iterator it(value.begin());
		if (_firstPass)
		{
			for (; it != value.end(); ++it)
			{
				DataItem element("element");
				//write(element, *it);

				// pair writing, for std::map compatibility
				DataItem firstValue("element");
				firstValue.addAttribute("index", 0);
				write(firstValue, it.getKey());

				DataItem secondValue("element");
				secondValue.addAttribute("index", 1);
				write(secondValue, it.getValue());
			}
		}
		else
		{
			configurationItem.reserveChildrenSpace(value.size());

			for (; it != value.end(); ++it)
			{
				DataItem& element = configurationItem.addChild();
				element.setName("element");
				//write(element, *it);

				// pair writing, for std::map compatibility
				element.reserveChildrenSpace(2);

				DataItem& firstValue = element.addChild();
				firstValue.setName("element");
				firstValue.addAttribute("index", 0);
				write(firstValue, it.getKey());

				DataItem& secondValue = element.addChild();
				secondValue.setName("element");
				secondValue.addAttribute("index", 1);
				write(secondValue, it.getValue());
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::read(const DataItem& configurationItem, SmartPointer<TPointeeType>& value)
	{
		TPointeeType* pointer(NULL);
		read(configurationItem, pointer);
		value = pointer;
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::write(DataItem& configurationItem, const SmartPointer<TPointeeType>& value)
	{
		write(configurationItem, value.getImpl());
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::read(const DataItem& configurationItem, TPointeeType*& value)
	{
		unsigned int pointerId(configurationItem.getAttribute<unsigned int>("pointerID", _pointerId++));
		if (_firstPass == true)
		{
			if ((XMLStream::_idToPointerMap.find(pointerId) == XMLStream::_idToPointerMap.end()) && (pointerId != 0))
			{
				glm::GlmString typeName(configurationItem.getAttribute<glm::GlmString>("typeName", getTypeName<TPointeeType>()));
				value = static_cast<TPointeeType*>(RTTI::createInstance(getTypeID(typeName.c_str())));
				if (value == NULL)
				{
					GLM_SDK_TRACE_WARNING(typeName.c_str() << " is not a registered inspectable type");
				}
				XMLStream::_idToPointerMap[pointerId] = value;
				if (value != NULL)
				{
					read(configurationItem, *value);
				}
			}
			else
			{
				value = NULL;
			}
		}
		else
		{
			value = static_cast<TPointeeType*>(XMLStream::_idToPointerMap[pointerId]);
			if (value != NULL)
			{
				read(configurationItem, *value);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::write(DataItem& configurationItem, TPointeeType* value)
	{
		if (_firstPass == true)
		{
			if (XMLStream::_pointerToIdMap.find(value) == XMLStream::_pointerToIdMap.end())
			{
				unsigned int pointerId(static_cast<unsigned int>(XMLStream::_pointerToIdMap.size()));
				XMLStream::_pointerToIdMap[value] = pointerId;
				XMLStream::_inspectablesPointersSet.insert(value);
				write(configurationItem, *value, false);
			}
		}
		else
		{
			unsigned int pointerId(XMLStream::_pointerToIdMap[value]);
			configurationItem.addAttribute<unsigned int>("pointerID", pointerId);
			if (XMLStream::_inspectablesPointersSet.find(value) != XMLStream::_inspectablesPointersSet.end())
			{
				if (value != NULL)
				{
					if (::strcmp(value->getRTTI().getTypeName(), getTypeName<TPointeeType>()) != 0)
					{
						configurationItem.addAttribute("typeName", value->getRTTI().getTypeName());
					}
					write(configurationItem, *value);
				}
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::read(const DataItem& configurationItem, const TPointeeType*& value)
	{
		unsigned int pointerId(configurationItem.getAttribute<unsigned int>("pointerID", _pointerId++));
		if (_firstPass)
		{
			if ((XMLStream::_idToPointerMap.find(pointerId) == XMLStream::_idToPointerMap.end()) && (pointerId != 0))
			{
				glm::GlmString typeName(configurationItem.getAttribute<glm::GlmString>("typeName", getTypeName<TPointeeType>()));
				value = static_cast<TPointeeType*>(RTTI::createInstance(getTypeID(typeName.c_str())));
				if (value == NULL)
				{
					GLM_SDK_TRACE_WARNING(typeName.c_str() << " is not a registered inspectable type");
				}
				XMLStream::_idToPointerMap[pointerId] = const_cast<TPointeeType*>(value);
				if (value != NULL)
				{
					read(configurationItem, *const_cast<TPointeeType*>(value));
				}
			}
			else
			{
				value = NULL;
			}
		}
		else
		{
			value = static_cast<TPointeeType*>(XMLStream::_idToPointerMap[pointerId]);
			if (value != NULL)
			{
				read(configurationItem, *const_cast<TPointeeType*>(value));
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::write(DataItem& configurationItem, const TPointeeType* value)
	{
		if (_firstPass == true)
		{
			if (XMLStream::_pointerToIdMap.find(const_cast<TPointeeType*>(value)) == XMLStream::_pointerToIdMap.end())
			{
				unsigned int pointerId(static_cast<unsigned int>(XMLStream::_pointerToIdMap.size()));
				XMLStream::_pointerToIdMap[const_cast<TPointeeType*>(value)] = pointerId;
				XMLStream::_inspectablesPointersSet.insert(value);
				write(configurationItem, *const_cast<TPointeeType*>(value), false);
			}
		}
		else
		{
			unsigned int pointerId(XMLStream::_pointerToIdMap[const_cast<TPointeeType*>(value)]);
			configurationItem.addAttribute("pointerID", pointerId);
			if (XMLStream::_inspectablesPointersSet.find(const_cast<TPointeeType*>(value)) != XMLStream::_inspectablesPointersSet.end())
			{
				if (value != NULL)
				{
					if (::strcmp(value->getRTTI().getTypeName(), getTypeName<TPointeeType>()) != 0)
					{
						configurationItem.addAttribute("typeName", value->getRTTI().getTypeName());
					}
					write(configurationItem, *const_cast<TPointeeType*>(value));
				}
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::read(const DataItem& configurationItem, Type& value)
	{
		if (_firstPass && (configurationItem.getAttribute("pointerID") != NULL))
		{
			unsigned int pointerId(configurationItem.getAttribute<unsigned int>("pointerID"));
			glm::GlmMap<unsigned int, void*>::iterator ite(_idToPointerMap.find(pointerId));
			if ((ite != _idToPointerMap.end()) && (ite.getValue() != &value))
			{
				delete static_cast<Type*>(ite.getValue());
			}
			_idToPointerMap[pointerId] = &value;
		}

		for (unsigned int i(0); i < configurationItem.getChildrenCount(); ++i)
		{
			const AbstractAttribute* attribute = value.getRTTI().getAttribute(configurationItem.getChild(i)->getName());
			if (attribute != NULL)
			{
				attribute->unpack(*this, *configurationItem.getChild(i), &value);
			}
			else
			{
				GLM_SDK_TRACE_WARNING("Attribute named " << configurationItem.getChild(i)->getName() << " unexpected on node " << configurationItem.getName());
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class Type>
	void XMLStream::write(DataItem& configurationItem, const Type& value, bool isValue)
	{
		if (_firstPass == true)
		{
			if (XMLStream::_pointerToIdMap.find(&value) == XMLStream::_pointerToIdMap.end())
			{
				unsigned int pointerId(static_cast<unsigned int>(XMLStream::_pointerToIdMap.size()));
				XMLStream::_pointerToIdMap[&value] = pointerId;
			}
			if (isValue)
			{
				XMLStream::_inspectablesPointersSet.erase(&value);
			}
			for (unsigned int i(0); i < value.getRTTI().getAttributesCount(); ++i)
			{
				value.getRTTI().getAttribute(i)->pack(*this, configurationItem, &value);
			}
		}
		else
		{
			XMLStream::_inspectablesPointersSet.erase(&value);

			// may have been written before if IInspectable is stored as pointer, const pointer, etc
			if (configurationItem.getAttribute("pointerID") == NULL)
			{
				unsigned int pointerId(_pointerToIdMap[&value]);
				configurationItem.addAttribute("pointerID", pointerId);
			}

			configurationItem.reserveChildrenSpace(value.getRTTI().getAttributesCount());

			for (unsigned int i(0); i < value.getRTTI().getAttributesCount(); ++i)
			{
				const AbstractAttribute& attribute(*value.getRTTI().getAttribute(i));

				DataItem& attributeItem = configurationItem.addChild();
				attributeItem.setName(attribute.getName());
				attribute.pack(*this, attributeItem, &value);
			}
		}
	}

	//-------------------------------------------------------------------------
	template <class TValue >
	void XMLStream::doReadAttribute(const DataItem& configurationItem, TValue& value, const char* attributeName)
	{
		value = configurationItem.getAttribute<TValue>(attributeName);
	}

	//-------------------------------------------------------------------------
	template <class TValue >
	void XMLStream::doWriteAttribute(DataItem& configurationItem, TValue value, const char* attributeName)
	{
		if (!_firstPass)
		{
			configurationItem.addAttribute(attributeName, value);
		}
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::doRead(const DataItem& configurationItem, TPointeeType*& value)
	{
		unsigned int pointerId(configurationItem.getAttribute<unsigned int>("pointerID", _pointerId++));
		if (_firstPass)
		{
			if (XMLStream::_idToPointerMap.find(pointerId) == XMLStream::_idToPointerMap.end())
			{
				GLM_DEBUG_ASSERT(value == NULL);
				value = new TPointeeType();
				XMLStream::_idToPointerMap[pointerId] = value;
				read(configurationItem, *value);
			}
		}
		value = static_cast<TPointeeType*>(XMLStream::_idToPointerMap[pointerId]);
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::doWrite(DataItem& configurationItem, TPointeeType* value)
	{
		if (!_firstPass)
		{
			unsigned int pointerId(0);
			if (XMLStream::_pointerToIdMap.find(value) == XMLStream::_pointerToIdMap.end())
			{
				pointerId = static_cast<unsigned int>(XMLStream::_pointerToIdMap.size());
				XMLStream::_pointerToIdMap[value] = pointerId;
				write(configurationItem, *value);
			}
			else
			{
				pointerId = XMLStream::_pointerToIdMap[value];
			}
			configurationItem.addAttribute("pointerID", pointerId);
		}
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::doRead(const DataItem& configurationItem, const TPointeeType*& value)
	{
		unsigned int pointerId(configurationItem.getAttribute<unsigned int>("pointerID", _pointerId++));
		if (_firstPass)
		{
			if (XMLStream::_idToPointerMap.find(pointerId) == XMLStream::_idToPointerMap.end())
			{
				GLM_DEBUG_ASSERT(value == NULL);
				value = new TPointeeType();
				XMLStream::_idToPointerMap[pointerId] = const_cast<TPointeeType*>(value);
				read(configurationItem, *const_cast<TPointeeType*>(value));
			}
		}
		value = static_cast<TPointeeType*>(XMLStream::_idToPointerMap[pointerId]);
	}

	//-------------------------------------------------------------------------
	template <class TPointeeType>
	void XMLStream::doWrite(DataItem& configurationItem, const TPointeeType* value)
	{
		if (!_firstPass)
		{
			unsigned int pointerId(0);
			if (XMLStream::_pointerToIdMap.find(const_cast<TPointeeType*>(value)) == XMLStream::_pointerToIdMap.end())
			{
				pointerId = static_cast<unsigned int>(XMLStream::_pointerToIdMap.size());
				XMLStream::_pointerToIdMap[const_cast<TPointeeType*>(value)] = pointerId;
				write(configurationItem, *value);
			}
			else
			{
				pointerId = XMLStream::_pointerToIdMap[const_cast<TPointeeType*>(value)];
			}
			configurationItem.addAttribute("pointerID", pointerId);
		}
	}

	//-------------------------------------------------------------------------
	inline bool XMLStream::isFirstPass()
	{
		return _firstPass;
	}

	//-------------------------------------------------------------------------
	inline void XMLStream::setFirstPass(bool firstPass)
	{
		_firstPass = firstPass;
	}

	//-------------------------------------------------------------------------
	inline void	XMLStream::pushValueInFifo(void* value)
	{
		_queueOfAccessorAttributeValues.push(value);
	}

	//-------------------------------------------------------------------------
	inline void* XMLStream::popValuefromFifo()
	{
		void* returnValue = _queueOfAccessorAttributeValues.front();
		_queueOfAccessorAttributeValues.pop();
		return returnValue;
	}
}
