#ifndef _XEXMLPARSER_HPP
#define _XEXMLPARSER_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Test Executor
 * ------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief XML Parser.
 *
 * \todo [2012-06-07 pyry] Not supported / handled properly:
 *  - xml namespaces (<ns:Element>)
 *  - backslash escapes in strings
 *  - &quot; -style escapes
 *  - utf-8
 *//*--------------------------------------------------------------------*/

#include "xeDefs.hpp"
#include "deRingBuffer.hpp"

#include <string>
#include <map>

namespace xe
{
namespace xml
{

enum Token
{
	TOKEN_INCOMPLETE = 0,					//!< Not enough data to determine token.
	TOKEN_END_OF_STRING,					//!< End of document string.
	TOKEN_DATA,								//!< Block of data (anything outside tags).
	TOKEN_COMMENT,							//!< <!-- comment -->
	TOKEN_IDENTIFIER,						//!< Identifier (in tags).
	TOKEN_STRING,							//!< String (in tags).
	TOKEN_TAG_START,						//!< <
	TOKEN_TAG_END,							//!< >
	TOKEN_END_TAG_START,					//!< </
	TOKEN_EMPTY_ELEMENT_END,				//!< />
	TOKEN_PROCESSING_INSTRUCTION_START,		//!< <?
	TOKEN_PROCESSING_INSTRUCTION_END,		//!< ?>
	TOKEN_EQUAL,							//!< =
	TOKEN_ENTITY,							//!< Entity reference, such as &amp;

	TOKEN_LAST
};

enum Element
{
	ELEMENT_INCOMPLETE = 0,	//!< Incomplete element.
	ELEMENT_START,			//!< Element start.
	ELEMENT_END,			//!< Element end.
	ELEMENT_DATA,			//!< Data element.
	ELEMENT_END_OF_STRING,	//!< End of document string.

	ELEMENT_LAST
};

const char* getTokenName (Token token);

// \todo [2012-10-17 pyry] Add line number etc.
class ParseError : public xe::ParseError
{
public:
	ParseError (const std::string& message) : xe::ParseError(message) {}
};

class Tokenizer
{
public:
						Tokenizer			(void);
						~Tokenizer			(void);

	void				clear				(void);		//!< Resets tokenizer to initial state.

	void				feed				(const deUint8* bytes, int numBytes);
	void				advance				(void);

	Token				getToken			(void) const		{ return m_curToken;	}
	int					getTokenLen			(void) const		{ return m_curTokenLen;	}
	deUint8				getTokenByte		(int offset) const	{ DE_ASSERT(m_curToken != TOKEN_INCOMPLETE && m_curToken != TOKEN_END_OF_STRING); return m_buf.peekBack(offset); }
	void				getTokenStr			(std::string& dst) const;
	void				appendTokenStr		(std::string& dst) const;

	void				getString			(std::string& dst) const;

private:
						Tokenizer			(const Tokenizer& other);
	Tokenizer&			operator=			(const Tokenizer& other);

	int					getChar				(int offset) const;

	void				error				(const std::string& what);

	enum State
	{
		STATE_DATA = 0,
		STATE_TAG,
		STATE_IDENTIFIER,
		STATE_VALUE,
		STATE_COMMENT,
		STATE_ENTITY,

		STATE_LAST
	};

	enum
	{
		END_OF_STRING	= 0,			//!< End of string (0).
		END_OF_BUFFER	= 0xffffffff	//!< End of current data buffer.
	};

	Token						m_curToken;			//!< Current token.
	int							m_curTokenLen;		//!< Length of current token.

	State						m_state;			//!< Tokenization state.

	de::RingBuffer<deUint8>		m_buf;
};

class Parser
{
public:
	typedef std::map<std::string, std::string>		AttributeMap;
	typedef AttributeMap::const_iterator			AttributeIter;

						Parser				(void);
						~Parser				(void);

	void				clear				(void);		//!< Resets parser to initial state.

	void				feed				(const deUint8* bytes, int numBytes);
	void				advance				(void);

	Element				getElement			(void) const						{ return m_element;										}

	// For ELEMENT_START / ELEMENT_END.
	const char*			getElementName		(void) const						{ return m_elementName.c_str();							}

	// For ELEMENT_START.
	bool				hasAttribute		(const char* name) const			{ return m_attributes.find(name) != m_attributes.end();	}
	const char*			getAttribute		(const char* name) const			{ return m_attributes.find(name)->second.c_str();		}
	const AttributeMap&	attributes			(void) const						{ return m_attributes;									}

	// For ELEMENT_DATA.
	int					getDataSize			(void) const;
	deUint8				getDataByte			(int offset) const;
	void				getDataStr			(std::string& dst) const;
	void				appendDataStr		(std::string& dst) const;

private:
						Parser				(const Parser& other);
	Parser&				operator=			(const Parser& other);

	void				parseEntityValue	(void);

	void				error				(const std::string& what);

	enum State
	{
		STATE_DATA = 0,						//!< Initial state - assuming data or tag open.
		STATE_ENTITY,						//!< Parsed entity is stored - overrides data.
		STATE_IN_PROCESSING_INSTRUCTION,	//!< In processing instruction.
		STATE_START_TAG_OPEN,				//!< Start tag open.
		STATE_END_TAG_OPEN,					//!< End tag open.
		STATE_EXPECTING_END_TAG_CLOSE,		//!< Expecting end tag close.
		STATE_ATTRIBUTE_LIST,				//!< Expecting attribute list.
		STATE_EXPECTING_ATTRIBUTE_EQ,		//!< Got attribute name, expecting =.
		STATE_EXPECTING_ATTRIBUTE_VALUE,	//!< Expecting attribute value.
		STATE_YIELD_EMPTY_ELEMENT_END,		//!< Empty element: start has been reported but not end.

		STATE_LAST
	};

	Tokenizer			m_tokenizer;

	Element				m_element;
	std::string			m_elementName;
	AttributeMap		m_attributes;

	State				m_state;
	std::string			m_attribName;
	std::string			m_entityValue;		//!< Data override, such as entity value.
};

// Inline implementations

inline void Tokenizer::getTokenStr (std::string& dst) const
{
	DE_ASSERT(m_curToken != TOKEN_INCOMPLETE && m_curToken != TOKEN_END_OF_STRING);
	dst.resize(m_curTokenLen);
	for (int ndx = 0; ndx < m_curTokenLen; ndx++)
		dst[ndx] = m_buf.peekBack(ndx);
}

inline void Tokenizer::appendTokenStr (std::string& dst) const
{
	DE_ASSERT(m_curToken != TOKEN_INCOMPLETE && m_curToken != TOKEN_END_OF_STRING);

	size_t oldLen = dst.size();
	dst.resize(oldLen+m_curTokenLen);

	for (int ndx = 0; ndx < m_curTokenLen; ndx++)
		dst[oldLen+ndx] = m_buf.peekBack(ndx);
}

inline int Parser::getDataSize (void) const
{
	if (m_state != STATE_ENTITY)
		return m_tokenizer.getTokenLen();
	else
		return (int)m_entityValue.size();
}

inline deUint8 Parser::getDataByte (int offset) const
{
	if (m_state != STATE_ENTITY)
		return m_tokenizer.getTokenByte(offset);
	else
		return (deUint8)m_entityValue[offset];
}

inline void Parser::getDataStr (std::string& dst) const
{
	if (m_state != STATE_ENTITY)
		return m_tokenizer.getTokenStr(dst);
	else
		dst = m_entityValue;
}

inline void Parser::appendDataStr (std::string& dst) const
{
	if (m_state != STATE_ENTITY)
		return m_tokenizer.appendTokenStr(dst);
	else
		dst += m_entityValue;
}

} // xml
} // xe

#endif // _XEXMLPARSER_HPP