//-----------------------------------------------------------------------------------------------
// SquirrelXML.hpp
//
#pragma once
#ifndef INCLUDED_SQUIRRELXML
#define INCLUDED_SQUIRRELXML


//-----------------------------------------------------------------------------------------------
const int MAX_XML_FILE_PATH_LENGTH = 1024;
const int MAX_XML_ELEMENT_NAME_LENGTH = 256;
const int MAX_XML_ATTRIBUTE_NAME_LENGTH = 256;
const int MAX_XML_ELEMENT_NESTING_DEPTH = 256;


//-----------------------------------------------------------------------------------------------
#define SQUIRRELXML_ALLOW_STL // Comment this out to remove use of the Standard Template Library
#ifdef SQUIRRELXML_ALLOW_STL
	#include <string>
#endif


//-----------------------------------------------------------------------------------------------
void SquirrelXML_SkipWhitespace( const char*& scan, int& lineNumber, const char*& startOfLine );
void SquirrelXML_SkipQuotedString( const char*& scan, int& lineNumber, const char*& startOfLine );


/////////////////////////////////////////////////////////////////////////////////////////////////
class SquirrelXMLSubstring
{
	friend class SquirrelXMLAttribute;
	friend class SquirrelXMLElement;
	friend class SquirrelXMLDocument;

public:
	SquirrelXMLSubstring();
	bool GetText( char* out_charBuffer, int bufferSize ) const;
	bool IsEmpty() const;
	void Clear();
	bool DoesSubstringEqual( const char* caseSensitiveCompareString ) const;

#ifdef SQUIRRELXML_ALLOW_STL
	bool GetString( std::string& out_string ) const;
#endif

private:
	const char*		m_substringStart;
	int				m_substringLength;
};


/////////////////////////////////////////////////////////////////////////////////////////////////
class SquirrelXMLAttribute
{
	friend class SquirrelXMLElement;
	friend class SquirrelXMLDocument;

public:
	SquirrelXMLAttribute* GetNextSiblingAttribute() const { return m_nextSiblingAttribute; }
	bool GetAttributeName( char* out_attributeNameCharBuffer, int bufferSize ) const;
	int GetLineIndex() const { return m_lineNumber; }
	int GetCharIndexOnLine() const { return m_charIndexOnLine; }
	bool DoesAttributeNameEqual( const char* caseSensitiveCompareString ) const;
	bool GetAttributeDataAsText( char* out_attributeDataCharBuffer, int bufferSize ) const;
	bool GetAttributeDataAsCharLiteral( char& out_attributeDataAsCharLiteral ) const;

#ifdef SQUIRRELXML_ALLOW_STL
	bool GetAttributeName( std::string& out_attributeName ) const;
	bool GetAttributeDataAsString( std::string& out_attributeDataAsString ) const;
#endif

private:
	SquirrelXMLAttribute();
	~SquirrelXMLAttribute();

private:
	SquirrelXMLSubstring	m_attributeNameSubstring;
	SquirrelXMLSubstring	m_attributeDataSubstring;
	SquirrelXMLElement*		m_ownerElement;
	SquirrelXMLAttribute*	m_nextSiblingAttribute;
	int						m_lineNumber;
	int						m_charIndexOnLine;
};


/////////////////////////////////////////////////////////////////////////////////////////////////
class SquirrelXMLElement
{
	friend class SquirrelXMLDocument;

public:
	SquirrelXMLElement();
	~SquirrelXMLElement();

#ifdef SQUIRRELXML_ALLOW_STL
	bool GetElementName( std::string& out_elementName ) const;
	bool GetPCDataAsString( std::string& out_pcDataAsString ) const;
#endif

	bool GetElementName( char* out_elementNameCharBuffer, int bufferSize ) const;
	bool DoesElementNameEqual( const char* caseSensitiveCompareString ) const;
	int GetLineIndex() const { return m_lineNumber; }
	int GetCharIndexOnLine() const { return m_charIndexOnLine; }
	bool GetCommentBeforeAsText( char* out_commentBuffer, int bufferSize ) const;
	bool GetPCDataAsText( char* out_pcDataCharBuffer, int bufferSize ) const;
	int GetNumChildElements() const;
	SquirrelXMLElement* GetFirstChildElement() const { return m_firstChildElement; }
	SquirrelXMLElement* GetNextSiblingElement() const { return m_nextSiblingElement; }

	SquirrelXMLAttribute* GetFirstAttribute() const { return m_firstAttribute; }
	SquirrelXMLAttribute* GetAttribute( const char* attributeName ) const;
	bool GetTextAttribute( const char* attributeName, char* out_textValueBuffer, int bufferSize ) const;
	bool GetTextAttribute( const char* attributeName, std::string& out_stringValue ) const;
	bool GetCharLiteralAttribute( const char* attributeName, char& out_charLiteralData ) const;

private:
	SquirrelXMLSubstring	m_elementNameSubstring;
	SquirrelXMLSubstring	m_pcData;
	SquirrelXMLSubstring	m_commentBefore;
	SquirrelXMLElement*		m_parentElement;
	SquirrelXMLElement*		m_nextSiblingElement;
	SquirrelXMLElement*		m_firstChildElement;
	SquirrelXMLAttribute*	m_firstAttribute;
	int						m_lineNumber;
	int						m_charIndexOnLine;
};


/////////////////////////////////////////////////////////////////////////////////////////////////
class SquirrelXMLDocument
{
public:
	SquirrelXMLDocument( const char* xmlFilePath );
	~SquirrelXMLDocument();
	bool LoadXMLFileToBuffer( const char* xmlFilePath );
	void BuildXMLDocumentStructureFromBuffer();
	SquirrelXMLElement* GetRootElement();
	bool IsValid() const;

private:
	char*					m_rawText;
	int						m_rawTextLength;
	char					m_xmlFilePath[ MAX_XML_FILE_PATH_LENGTH ];
	SquirrelXMLElement*		m_rootElement;
};


/////////////////////////////////////////////////////////////////////////////////////////////////
// SquirrelXMLSubstring
//

//-----------------------------------------------------------------------------------------------
inline SquirrelXMLSubstring::SquirrelXMLSubstring()
	: m_substringStart( nullptr )
	, m_substringLength( 0 )
{
}


//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLSubstring::IsEmpty() const
{
	return( m_substringLength == 0 );
}


//-----------------------------------------------------------------------------------------------
inline void SquirrelXMLSubstring::Clear()
{
	m_substringStart = nullptr;
	m_substringLength = 0;
}


//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLSubstring::DoesSubstringEqual( const char* caseSensitiveCompareString ) const
{
	int substringCharIndex = 0;
	const char* substringScan = m_substringStart;
	const char* compareNameScan = caseSensitiveCompareString;
	for( ;; )
	{
		if( *compareNameScan == '\0' )
		{
			return( substringCharIndex == m_substringLength );
		}

		if( substringCharIndex >= m_substringLength )
			return false;

		if( *compareNameScan != *substringScan )
			return false;

		++ substringCharIndex;
		++ compareNameScan;
		++ substringScan;
	}
}


/////////////////////////////////////////////////////////////////////////////////////////////////
// SquirrelXMLAttribute
//

//-----------------------------------------------------------------------------------------------
inline SquirrelXMLAttribute::SquirrelXMLAttribute()
	: m_ownerElement( nullptr )
	, m_nextSiblingAttribute( nullptr )
{
}


//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLAttribute::DoesAttributeNameEqual( const char* caseSensitiveCompareString ) const
{
	return m_attributeNameSubstring.DoesSubstringEqual( caseSensitiveCompareString );
}


//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLAttribute::GetAttributeName( char* out_attributeNameCharBuffer, int bufferSize ) const
{
	return m_attributeNameSubstring.GetText( out_attributeNameCharBuffer, bufferSize );
}


//-----------------------------------------------------------------------------------------------
#ifdef SQUIRRELXML_ALLOW_STL
inline bool SquirrelXMLAttribute::GetAttributeName( std::string& out_attributeName ) const
{
	return m_attributeNameSubstring.GetString( out_attributeName );
}
#endif



/////////////////////////////////////////////////////////////////////////////////////////////////
// SquirrelXMLElement
//

//-----------------------------------------------------------------------------------------------
inline SquirrelXMLElement::SquirrelXMLElement()
	: m_parentElement( nullptr )
	, m_pcData( )
	, m_nextSiblingElement( nullptr )
	, m_firstChildElement( nullptr )
	, m_firstAttribute( nullptr )
{
}


//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLElement::DoesElementNameEqual( const char* caseSensitiveCompareString ) const
{
	return m_elementNameSubstring.DoesSubstringEqual( caseSensitiveCompareString );
}


//-----------------------------------------------------------------------------------------------
inline int SquirrelXMLElement::GetNumChildElements() const
{
	int numChildElements = 0;
	SquirrelXMLElement* child = GetFirstChildElement();
	while( child != nullptr )
	{
		++numChildElements;
		child = child->GetNextSiblingElement();
	}

	return numChildElements;
}



#ifdef SQUIRRELXML_ALLOW_STL
//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLElement::GetElementName( std::string& out_elementName ) const
{
	return m_elementNameSubstring.GetString( out_elementName );
}


//-----------------------------------------------------------------------------------------------
inline bool SquirrelXMLElement::GetPCDataAsString( std::string& out_pcDataAsString ) const
{
	return m_pcData.GetString( out_pcDataAsString );
}
#endif





#endif // INCLUDED_SQUIRRELXML
