#include <ZUtil/ZXMLReader.hpp>

//We don't want rapidxml throwing exceptions at us, let the parse handler do it
#ifndef RAPIDXML_NO_EXCEPTIONS
#define RAPIDXML_NO_EXCEPTIONS
#endif

#include <rapidxml/rapidxml.hpp>

namespace rapidxml
{
	void parse_error_handler(const char *what, void *where, void *arg)
	{
		URFP(where); //For now

		ZString& errorMsg = *((ZString*)arg);

		errorMsg = "ZXMLReader (rapidxml): ";
		ZStringAlgo::Append(errorMsg, what);
	}
}

/*************************************************************************/

bool ZXMLReader::Read(const char* data, size_t length) 
{
	Tree.Clear();

	ZString name;	// current node name
	ZString value;	// current node value

	// make a copy of xml data so we can use rapidxml in destructive mode
	ZString xmlData(data, length);
	rapidxml::xml_document<> doc(&ErrorMessage);	// character type is 'char'

	// 0 is default flags (error message will be set by handler)
	if (!doc.parse<rapidxml::parse_no_data_nodes>(xmlData.Data())) {
		return false;
	}

	// iterate the nodes and set data in the tree	
	rapidxml::xml_node<> *node = doc.first_node();
	ZKVTree::Iterator itr = Tree.Begin();

	while (node != NULL) {
		name = node->name();
		value = node->value();

		// xml often contains a lot of control characters within the 'body'
		ZStringAlgo::Trim(value);

		// add this node under the root node
		ZKVTree::Iterator node_itr = Tree.Add(itr, name, value);
		
		// iterate the attributes and set data in the kvtree
		for (rapidxml::xml_attribute<> *attr = node->first_attribute(); attr != NULL; attr = attr->next_attribute()) {
			ZString attr_name = attr->name();
			ZString attr_value = attr->value();

			// add this node under the newly created node
			Tree.Add(node_itr, attr_name, attr_value);
		}
		
		// iterate to next node (check child first, then sibling, then go up to parent)
		if (node->first_node() != NULL) {	
			node = node->first_node();	
			itr = node_itr;
		} else if (node->next_sibling() != NULL) {
			node = node->next_sibling();
		} else {

			// go to next element (up and out)
			while (node->parent() != NULL) {
				node = node->parent();

				if (node->type() != rapidxml::node_document) {
					itr = itr.Parent();
				} else break;

				if (node->next_sibling() != NULL) {
					node = node->next_sibling();
					break;
				} 
				
			}

			// if we end up NULL or back at the 'document' node by going to parent, then we are done.
			if (node == NULL || node->parent() == NULL || node->type() == rapidxml::node_document) {
				return true;
			}
		}
	}

	return true;
}

/*************************************************************************/

const ZString& ZXMLReader::GetErrorString()
{
	return ErrorMessage;
}

/*************************************************************************/

void ZXMLReader::GetKVTree(ZKVTree& _kvtree)
{
	_kvtree = Tree;
}
