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

#pragma once

#include "glmPODArray.h"
#include "glmString.h"
#include "glmAssert.h"
#include "glmRTTI.h"
#include <iomanip>

namespace glm
{
	/**
	* \brief Node of the configuration items tree.
	*
	* This class is used to fill the configuration tree. Each configuration item is defined by :
	* - a name
	* - a list of attributes, each defined by a (string) name and a (string) value
	* - a list of children configuration items
	*
	* \ingroup dataItem
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_CORE_API DataItem
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

		friend class AbstractDataFileParser ;

		class AttributeItem
		{
			public:

			GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)
			
			AttributeItem( const char* name = "", const char* value = "" ) :
				_name( name),
				_value( value )
			{}

			glm::GlmString _name;
			glm::GlmString _value;
		};

		/**
		* \brief constructor
		*/
		DataItem( const char* name = "root" ) ;

		/**
		* \brief destructor
		*/
		virtual ~DataItem() ;

		/**
		* \brief accessor to the configuration item name
		* \return the configuration item name
		*/
		const char* getName() const ;

		/**
		* \brief set accessor to the configuration item name
		*/
		void setName( const char* name ) ;
		void setName( const char* name, unsigned int size ) ;

		/**
		* \brief const get accessor to a child
		* \param childNumber number of the child to retrieve
		* \return a const pointer to the child childNumber, NULL if it does not exist
		*/
		const DataItem* getChild( unsigned int childNumber ) const ;

		/**
		* \brief get accessor to a child
		* \param childNumber number of the child to retrieve
		* \return a pointer to the child childNumber, NULL if it does not exist
		*/
		DataItem* touchChild( unsigned int childNumber ) ;

		/**
		* \brief const get accessor to a child
		* \param name name of the child to retrieve
		* \return a const pointer to the child named name, NULL if it does not exist
		*/			
		const DataItem* getChild( const char* name ) const ;

		/**
		* \brief get accessor to a child
		* \param name name of the child to retrieve
		* \return a pointer to the child named name, NULL if it does not exist
		*/			
		DataItem* touchChild( const char* name ) ;

		/**
		* \brief const get accessor to the configuration items childrem named name
		* \param name of the children to return
		* \param wholeHierarchy true to get children from the whole children hierarchy
		* \return a vector of children named name
		*/
		glm::Array<const DataItem*> getChildren( const char* name, bool wholeHierarchy = false ) const ;

		/**
		* \brief get accessor to the configuration items childrem named name
		* \param name of the children to return
		* \param wholeHierarchy true to get children from the whole children hierarchy
		* \return a vector of children named name
		*/
		glm::Array<DataItem*> touchChildren( const char* name, bool wholeHierarchy = false ) ;

		/**
		* \brief add a child to the configuration item
		* \param child const reference to the child to add
		*/
		void addChild( const DataItem& child ) ;
		
		//! returns a reference on newly created dataItem, modifiable
		DataItem& addChild() ;

		//! reserves sufficient space for expected chilren, avoids copies of old vector to new one when vector capacity change due to resize
		void reserveChildrenSpace( size_t childrenCount );

		/**
		* \brief accessor to the children count
		* \return the children count
		*/
		unsigned int getChildrenCount() const ;

		/**
		* \brief accessor to the value of an attribute
		* \param name of the attribute
		* \return the value of the attribute referenced by name, NULL if none
		*/
		const char* getAttribute( const char* name ) const ;

		/**
		* \brief set accessor to the value of an attribute
		* \param name of the attribute
		* \param value of the attribute to set
		*/
		void setAttribute( const char* name, const char* value ) ;

		/**
		* \brief get accessor to the value of an attribute
		* \param name of the attribute
		* \return the value of the attribute referenced by name, NULL if none
		* \exception launch glm::Exception if content is not compatible with T
		*/
		template < class T > T getAttribute( const char* name ) const ;

		/**
		* \brief set accessor to the value of an attribute
		* \param name of the attribute
		* \param value of the attribute to set
		* \exception launch glm::Exception if content is not compatible with T
		*/
		template < class T > void setAttribute( const char* name, const T& value ) ;

		/**
		* \brief accessor to the value of an attribute 
		* \param name of the attribute
		* \param defaultValue default value of the attribute if none
		* \return the value of the attribute referenced by name, a default value if none
		* \exception launch glm::Exception if content is not compatible with T
		*/
		template < class T > T getAttribute( const char* name, const T& defaultValue ) const ;

		/**
		* \brief accessor to the name and value of an attribute
		* \param attribute number of the attribute to retrieve
		* \return a pair of the name and value of the attribute
		*/
		std::pair< const char*, const char* >  getAttribute( unsigned int attribute ) const ;

		/**
		* \brief accessor to the name and value of an attribute
		* \param attribute number of the attribute to retrieve
		* \return a pair of the name and value of the attribute
		* \exception launch glm::Exception if content is not compatible with T
		*/
		template < class T > std::pair< const char*, T >  getAttribute( unsigned int attribute ) const ;

		/**
		* \brief add an attribute to the configuration item
		* \param name of the attribute
		* \param value of the attribute
		*/
		void addAttribute( const char* name, const char* value ) ;
		void addAttribute( const char* name, unsigned int nameSize, const char* value, unsigned int valueSize ) ;

		/**
		* \brief add an attribute to the configuration item
		* \param name of the attribute
		* \param value of the attribute
		*/
		template < class T > void addAttribute( const char* name, const T& value ) ;

		/**
		* \brief accessor to the attributes count
		* \return the attributes count
		*/
		unsigned int getAttributesCount() const ;

		/**
		* \brief fill a string with the configuration items name, attributes and children
		* \return a string representing the configuration items tree
		*/
		glm::GlmString traceDataItems(glm::GlmString& tabString) const ;

		/**
		* \brief accessor to the configuration item content
		* \return the configuration item content (NULL if none)
		*/
		const char* getContent() const ;

		/**
		* \brief accessor to the configuration item content
		* \param defaultValue default value of the attribute if none
		* \return the configuration item content (NULL if none)
		*/
		const char* getContent( const char* defaultValue ) const ;

		/**
		* \brief accessor to the configuration item content
		* \return the configuration item content (NULL if none)
		* \exception launch glm::Exception if content is not compatible with T
		*/
		template <class T> T getContent() const ;

		/**
		* \brief accessor to the configuration item content
		* \param defaultValue default value of the attribute if none
		* \return the configuration item content, a default value if none
		* \exception launch glm::Exception if content is not compatible with T
		*/
		template <class T> T getContent( const T& defaultValue ) const ;

		/**
		* \brief set the configuration item content
		* \param content of the configuration item
		*/
		void setContent( const char* content ) ;
		void setContent( const char* content, unsigned int size ) ;

		/**
		* \brief set the configuration item content
		* \param content of the configuration item
		*/
		template <class T> void setContent( const T& content ) ;
		
		/**
		* \brief clear the content, arguments, and children
		*/
		void clear() ;

	private:

		/// configuration item name 
		glm::GlmString _name ;

		/// content
		glm::GlmString _content;

		/// attributes
		glm::Array<AttributeItem> _attributes ;

		/// children
		glm::Array<DataItem> _children ;

	} ;

	template <>	GLM_CORE_API bool DataItem::getAttribute<bool>( const char* name ) const ; 
	template <>	GLM_CORE_API std::pair< const char*, bool > DataItem::getAttribute<bool>( unsigned int attribute ) const ; 
	template <> GLM_CORE_API bool DataItem::getAttribute<bool>( const char* name, const bool& defaultValue ) const ;
	template <> GLM_CORE_API bool DataItem::getContent() const ;
	template <> GLM_CORE_API bool DataItem::getContent( const bool& defaultValue ) const ;

	template <>	GLM_CORE_API glm::GlmString DataItem::getAttribute<glm::GlmString>( const char* name ) const ; 
	template <>	GLM_CORE_API std::pair< const char*, glm::GlmString > DataItem::getAttribute<glm::GlmString>( unsigned int attribute ) const ; 
	template <> GLM_CORE_API glm::GlmString DataItem::getAttribute<glm::GlmString>( const char* name, const glm::GlmString& defaultValue ) const ;
	template <> GLM_CORE_API glm::GlmString DataItem::getContent() const ;
	template <> GLM_CORE_API glm::GlmString DataItem::getContent( const glm::GlmString& defaultValue ) const ;

	//-------------------------------------------------------------------------
	// getName()
	//-------------------------------------------------------------------------
	inline const char* 
		DataItem::getName() const
	{
		return _name.c_str() ;
	}

	//-------------------------------------------------------------------------
	// setName()
	//-------------------------------------------------------------------------
	inline void
		DataItem::setName( const char* name )
	{
		_name = name ;
	}
	
	//-------------------------------------------------------------------------
	// setName() version used by in situ parsers
	//-------------------------------------------------------------------------
	inline void
		DataItem::setName( const char* name, unsigned int size )
	{
		_name.clear();
		_name.append( name, size );
		_name.append( "\0" );
	}

	//-------------------------------------------------------------------------
	// getChild()
	//-------------------------------------------------------------------------
	inline const DataItem*
		DataItem::getChild( unsigned int childNumber ) const
	{
		GLM_DEBUG_ASSERT( childNumber < _children.size() );
		return &_children[childNumber] ;
	}

	//-------------------------------------------------------------------------
	// touchChild()
	//-------------------------------------------------------------------------
	inline DataItem*
		DataItem::touchChild( unsigned int childNumber )
	{
		GLM_DEBUG_ASSERT( childNumber < _children.size() );
		return &_children[childNumber] ;
	}

	//-------------------------------------------------------------------------
	// addChild()
	//-------------------------------------------------------------------------
	inline void 
		DataItem::addChild( const DataItem& child )
	{
		_children.push_back( child ) ;
	}
	
	//-------------------------------------------------------------------------
	// addChild()
	//-------------------------------------------------------------------------
	inline DataItem&
		DataItem::addChild( )
	{
		size_t oldChildrenSize = _children.size();
		_children.resize(oldChildrenSize + 1 );
		return _children[oldChildrenSize] ; //childrenSize is old one, not out of bound
	}

	//-------------------------------------------------------------------------
	// addChild()
	//-------------------------------------------------------------------------
	inline void 
		DataItem::reserveChildrenSpace( size_t childrenCount )
	{
		_children.reserve( childrenCount );
	}

	//-------------------------------------------------------------------------
	// getChildrenCount()
	//-------------------------------------------------------------------------
	inline unsigned int 
		DataItem::getChildrenCount() const
	{
		return static_cast<unsigned int>( _children.size() ) ;
	}

	//-------------------------------------------------------------------------
	// getAttribute()
	//-------------------------------------------------------------------------
	inline const char* 
		DataItem::getAttribute( const char* name ) const
	{
		glm::Array<AttributeItem>::const_iterator ite = _attributes.begin();
		while ( ite != _attributes.end() )
		{
			if ( ite->_name == name )
			{
				return ite->_value.c_str() ;
			}			
			++ite;
		}
		return NULL ;
	}

	//-------------------------------------------------------------------------
	// setAttribute()
	//-------------------------------------------------------------------------
	inline void
		DataItem::setAttribute( const char* name, const char* value )
	{
		GLM_DEBUG_ASSERT_MSG( name != NULL, "name can not be NULL" ) ;
		GLM_DEBUG_ASSERT_MSG( strlen( name ) > 0, "name is empty" ) ;
		GLM_DEBUG_ASSERT_MSG( value != NULL, "the value can not be NULL" ) ;

		glm::Array<AttributeItem>::iterator ite = _attributes.begin();
		while ( ite != _attributes.end() )
		{
			if ( ite->_name == name )
			{
				ite->_value = value ;
				return;
			}
			++ite;
		}
	}

	//-------------------------------------------------------------------------
	// getAttribute()
	//-------------------------------------------------------------------------
	template <class T> 
	T 
		DataItem::getAttribute( const char* name ) const
	{
		const char* attribute( getAttribute( name ) ) ;
		GLM_DEBUG_ASSERT_MSG( attribute != NULL, "no attribute of name : %s", name ) ;
		GLM_DEBUG_ASSERT_MSG( strlen( attribute ) > 0, "attribute %s is empty", name ) ;
		T instance ;
		std::stringstream stream ;
		stream << attribute ;
		stream >> std::dec >> std::setprecision( 32 ) >> instance ;
		GLM_DEBUG_ASSERT_MSG( !stream.fail(), "incorrect value for template T, DataItem name = %s, attribute name = %s, value = %s, type = %s", getName(), name, attribute, getTypeName<T>() ) ;
		return instance ;
	}

	//-------------------------------------------------------------------------
	// setAttribute()
	//-------------------------------------------------------------------------
	template < class T > 
	void
		DataItem::setAttribute( const char* name, const T& value )
	{
		GLM_DEBUG_ASSERT_MSG( name != NULL, "name can not be NULL" ) ;
		GLM_DEBUG_ASSERT_MSG( strlen( name ) > 0, "name is empty" ) ;
		glm::Array<AttributeItem>::iterator ite = _attributes.begin();
		while ( ite != _attributes.end() )
		{
			if ( ite->_name == name )
			{
				std::stringstream stream ;
				stream << std::dec << std::setprecision( 32 ) << value ;
				GLM_DEBUG_ASSERT_MSG( !stream.fail(), "incorrect value for template T, DataItem name = %s, attribute name = %s, value = %s, type = %s", getName(), name, value, getTypeName<T>() ) ;
				ite->_value = stream.str().c_str() ;
				return;
			}
			++ite;
		}
	}

	//-------------------------------------------------------------------------
	// getAttribute()
	//-------------------------------------------------------------------------
	template <class T> 
	T
		DataItem::getAttribute( const char* name, const T& defaultValue ) const
	{
		const char* attribute( getAttribute( name ) ) ;
		if ( ( attribute != NULL ) && ( strlen( attribute ) > 0 ) )
		{
			T instance ;
			std::stringstream stream ;
			stream << attribute ;
			stream >> std::dec >> std::setprecision( 32 ) >> instance ;
			if ( !stream.fail() )
			{
				return instance ;
			}
		}
		return defaultValue ;
	}

	//-------------------------------------------------------------------------
	// getAttributesCount
	//-------------------------------------------------------------------------
	inline
		unsigned int DataItem::getAttributesCount() const 
	{
		return static_cast< unsigned int >( _attributes.size() ) ;
	}

	//-------------------------------------------------------------------------
	// getAttribute
	//-------------------------------------------------------------------------
	inline
		std::pair< const char*, const char* > DataItem::getAttribute( unsigned int attribute ) const 
	{
		glm::Array<AttributeItem>::const_iterator ite( _attributes.begin() ) ;
		for ( ; attribute != 0; --attribute )
		{
			++ite ;
		}
		return std::pair< const char*, const char* >( ite->_name.c_str(), ite->_value.c_str() ) ;
	}

	//-------------------------------------------------------------------------
	// getAttribute
	//-------------------------------------------------------------------------
	template <class T>
	inline
		std::pair< const char*, T > DataItem::getAttribute( unsigned int attribute ) const 
	{
		glm::Array<AttributeItem>::const_iterator ite( _attributes.begin() ) ;
		for ( unsigned int attributeCount( 0 ) ; attributeCount < attribute ; ++attributeCount )
		{
			++ite ;
		}
		T instance ;
		std::stringstream stream ;
		stream << ite->_value.c_str() ;
		stream >> std::dec >> std::setprecision( 32 ) >> instance ;
		GLM_DEBUG_ASSERT_MSG( !stream.fail(), "incorrect value for template T, DataItem name = %s, attribute number = %s, value = %s", getName(), attribute, ite->_value.c_str() ) ;
		return std::pair< const char*, T >( ite->_name.c_str(), instance ) ;
	}

	//-------------------------------------------------------------------------
	// addAttribute()
	//-------------------------------------------------------------------------
	inline void 
		DataItem::addAttribute( const char* name, const char* value )
	{
		size_t size = _attributes.size();
		_attributes.resize( size + 1 );
		_attributes[size]._name = name;
		_attributes[size]._value = value;
	}

	//-------------------------------------------------------------------------
	// addAttribute() version used by in situ parsers
	//-------------------------------------------------------------------------
	inline void 
		DataItem::addAttribute( const char* name, unsigned int nameSize, const char* value, unsigned int valueSize ) 
	{
		size_t size = _attributes.size();
		_attributes.resize( size + 1 );
		AttributeItem& currentItem = _attributes[size];

		currentItem._name.clear();
		currentItem._name.append( name, nameSize );
		currentItem._name.append( "\0" );

		currentItem._value.clear();
		currentItem._value.append( value, valueSize );
		currentItem._value.append( "\0" );
	}	

	//-------------------------------------------------------------------------
	// addAttribute()
	//-------------------------------------------------------------------------
	template < class T >
	inline void 
		DataItem::addAttribute( const char* name, const T& value )
	{
		std::stringstream stream ;
		stream << std::dec << std::setprecision( 32 ) << value ;
		addAttribute( name, stream.str().c_str() ) ;
	}

	//-------------------------------------------------------------------------
	// getContent()
	//-------------------------------------------------------------------------
	inline const char*
		DataItem::getContent() const
	{
		if ( _content.empty() )
		{
			return NULL ;
		}
		else
		{
			return _content.c_str() ;
		}
	}

	//-------------------------------------------------------------------------
	// getContent( const char* defaultValue )
	//-------------------------------------------------------------------------
	inline const char*
		DataItem::getContent( const char* defaultValue ) const
	{
		if ( _content.empty() )
		{
			return defaultValue ;
		}
		else
		{
			return _content.c_str() ;
		}
	}

	//-------------------------------------------------------------------------
	// getContent()
	//-------------------------------------------------------------------------
	template <class T> 
	T
		DataItem::getContent() const
	{
		T instance ;
		std::stringstream stream ;
		stream << _content ;
		stream >> std::dec >> std::setprecision( 32 ) >> instance ;
		GLM_DEBUG_ASSERT_MSG( !stream.fail(), "incorrect value for template T, DataItem name = %s, content = %s, T = %s", getName(), _content.c_str(), getTypeName<T>() ) ;
		return instance ;
	}

	//-------------------------------------------------------------------------
	// getContent()
	//-------------------------------------------------------------------------
	template <class T> 
	T
		DataItem::getContent( const T& defaultValue ) const
	{
		T instance ;
		std::stringstream stream ;
		stream << _content ;
		stream >> std::dec >> std::setprecision( 32 ) >> instance ;
		if ( !stream.fail() )
		{
			return instance ;
		}
		else
		{
			return defaultValue ;
		}
	}

	//-------------------------------------------------------------------------
	// setContent()
	//-------------------------------------------------------------------------
	inline void
		DataItem::setContent(const char* content)
	{
		_content = content ;		
	}

	//-------------------------------------------------------------------------
	// setContent() version used by in situ parsers
	//-------------------------------------------------------------------------
	inline void
		DataItem::setContent(const char* content, unsigned int size)
	{
		_content.clear();
		_content.append( content, size);
		_content.append( "\0" );
	}

	//-------------------------------------------------------------------------
	// setContent()
	//-------------------------------------------------------------------------
	template <class T> 
	void
		DataItem::setContent( const T& content )
	{
		std::stringstream stream ;
		stream << content ;
		_content = stream.str().c_str() ;
	}

	//-------------------------------------------------------------------------
	// clear()
	//-------------------------------------------------------------------------
	inline void
		DataItem::clear()
	{
		_content.clear() ;
		_attributes.clear() ;
		_children.clear() ;
	}
}
