#ifndef RAPIDXML_PRINT_HPP_INCLUDED
#define RAPIDXML_PRINT_HPP_INCLUDED

// The file below has been edited to clean-up the includes as necessiated by ScummVM.
// Notably, code which was dependent on streams has been removed.

// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation

#include "rapidxml.hpp"

namespace rapidxml
{
	///////////////////////////////////////////////////////////////////////
	// Printing flags

	const int print_no_indenting = 0x1;   //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.

	///////////////////////////////////////////////////////////////////////
	// Internal

	//! \cond internal
	namespace internal
	{
		///////////////////////////////////////////////////////////////////////////
		// Internal character operations

		// Copy characters from given range to given output iterator
		template<class OutIt, class Ch>
		inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
		{
			while (begin != end)
				*out++ = *begin++;
			return out;
		}

		// Copy characters from given range to given output iterator and expand
		// characters into references (&lt; &gt; &apos; &quot; &amp;)
		template<class OutIt, class Ch>
		inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
		{
			while (begin != end)
			{
				if (*begin == noexpand)
				{
					*out++ = *begin;    // No expansion, copy character
				}
				else
				{
					switch (*begin)
					{
					case Ch('<'):
						*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
						break;
					case Ch('>'):
						*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
						break;
					case Ch('\''):
						*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
						break;
					case Ch('"'):
						*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
						break;
					case Ch('&'):
						*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
						break;
					default:
						*out++ = *begin;    // No expansion, copy character
					}
				}
				++begin;    // Step to next character
			}
			return out;
		}

		// Fill given output iterator with repetitions of the same character
		template<class OutIt, class Ch>
		inline OutIt fill_chars(OutIt out, int n, Ch ch)
		{
			for (int i = 0; i < n; ++i)
				*out++ = ch;
			return out;
		}

		// Find character
		template<class Ch, Ch ch>
		inline bool find_char(const Ch *begin, const Ch *end)
		{
			while (begin != end)
				if (*begin++ == ch)
					return true;
			return false;
		}

		///////////////////////////////////////////////////////////////////////////
		// Internal printing operations

		// =====================================
		// fix for clang for this bug in gcc and others: http://sourceforge.net/p/rapidxml/bugs/16/

		template<class OutIt, class Ch> inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
		template<class OutIt, class Ch> inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);

		// =====================================

		// Print node
		template<class OutIt, class Ch>
		inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			// Print proper node type
			switch (node->type())
			{
				// Document
			case node_document:
				out = print_children(out, node, flags, indent);
				break;

				// Element
			case node_element:
				out = print_element_node(out, node, flags, indent);
				break;

				// Data
			case node_data:
				out = print_data_node(out, node, flags, indent);
				break;

				// CDATA
			case node_cdata:
				out = print_cdata_node(out, node, flags, indent);
				break;

				// Declaration
			case node_declaration:
				out = print_declaration_node(out, node, flags, indent);
				break;

				// Comment
			case node_comment:
				out = print_comment_node(out, node, flags, indent);
				break;

				// Doctype
			case node_doctype:
				out = print_doctype_node(out, node, flags, indent);
				break;

				// Pi
			case node_pi:
				out = print_pi_node(out, node, flags, indent);
				break;

				// Unknown
			default:
				assert(0);
				break;
			}

			// If indenting not disabled, add line break after node
			if (!(flags & print_no_indenting))
				*out = Ch('\n'), ++out;

			// Return modified iterator
			return out;
		}

		// Print children of the node
		template<class OutIt, class Ch>
		inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
				out = print_node(out, child, flags, indent);
			return out;
		}

		// Print attributes of the node
		template<class OutIt, class Ch>
		inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
		{
			for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
			{
				if (attribute->name() && attribute->value())
				{
					// Print attribute name
					*out = Ch(' '), ++out;
					out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
					*out = Ch('='), ++out;
					// Print attribute value using appropriate quote type
					if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
					{
						*out = Ch('\''), ++out;
						out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
						*out = Ch('\''), ++out;
					}
					else
					{
						*out = Ch('"'), ++out;
						out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
						*out = Ch('"'), ++out;
					}
				}
			}
			return out;
		}

		// Print data node
		template<class OutIt, class Ch>
		inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			assert(node->type() == node_data);
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
			return out;
		}

		// Print data node
		template<class OutIt, class Ch>
		inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			assert(node->type() == node_cdata);
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			*out = Ch('<'); ++out;
			*out = Ch('!'); ++out;
			*out = Ch('['); ++out;
			*out = Ch('C'); ++out;
			*out = Ch('D'); ++out;
			*out = Ch('A'); ++out;
			*out = Ch('T'); ++out;
			*out = Ch('A'); ++out;
			*out = Ch('['); ++out;
			out = copy_chars(node->value(), node->value() + node->value_size(), out);
			*out = Ch(']'); ++out;
			*out = Ch(']'); ++out;
			*out = Ch('>'); ++out;
			return out;
		}

		// Print element node
		template<class OutIt, class Ch>
		inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			assert(node->type() == node_element);

			// Print element name and attributes, if any
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			*out = Ch('<'), ++out;
			out = copy_chars(node->name(), node->name() + node->name_size(), out);
			out = print_attributes(out, node, flags);

			// If node is childless
			if (node->value_size() == 0 && !node->first_node())
			{
				// Print childless node tag ending
				*out = Ch('/'), ++out;
				*out = Ch('>'), ++out;
			}
			else
			{
				// Print normal node tag ending
				*out = Ch('>'), ++out;

				// Test if node contains a single data node only (and no other nodes)
				xml_node<Ch> *child = node->first_node();
				if (!child)
				{
					// If node has no children, only print its value without indenting
					out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
				}
				else if (child->next_sibling() == 0 && child->type() == node_data)
				{
					// If node has a sole data child, only print its value without indenting
					out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
				}
				else
				{
					// Print all children with full indenting
					if (!(flags & print_no_indenting))
						*out = Ch('\n'), ++out;
					out = print_children(out, node, flags, indent + 1);
					if (!(flags & print_no_indenting))
						out = fill_chars(out, indent, Ch('\t'));
				}

				// Print node end
				*out = Ch('<'), ++out;
				*out = Ch('/'), ++out;
				out = copy_chars(node->name(), node->name() + node->name_size(), out);
				*out = Ch('>'), ++out;
			}
			return out;
		}

		// Print declaration node
		template<class OutIt, class Ch>
		inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			// Print declaration start
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			*out = Ch('<'), ++out;
			*out = Ch('?'), ++out;
			*out = Ch('x'), ++out;
			*out = Ch('m'), ++out;
			*out = Ch('l'), ++out;

			// Print attributes
			out = print_attributes(out, node, flags);

			// Print declaration end
			*out = Ch('?'), ++out;
			*out = Ch('>'), ++out;

			return out;
		}

		// Print comment node
		template<class OutIt, class Ch>
		inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			assert(node->type() == node_comment);
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			*out = Ch('<'), ++out;
			*out = Ch('!'), ++out;
			*out = Ch('-'), ++out;
			*out = Ch('-'), ++out;
			out = copy_chars(node->value(), node->value() + node->value_size(), out);
			*out = Ch('-'), ++out;
			*out = Ch('-'), ++out;
			*out = Ch('>'), ++out;
			return out;
		}

		// Print doctype node
		template<class OutIt, class Ch>
		inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			assert(node->type() == node_doctype);
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			*out = Ch('<'), ++out;
			*out = Ch('!'), ++out;
			*out = Ch('D'), ++out;
			*out = Ch('O'), ++out;
			*out = Ch('C'), ++out;
			*out = Ch('T'), ++out;
			*out = Ch('Y'), ++out;
			*out = Ch('P'), ++out;
			*out = Ch('E'), ++out;
			*out = Ch(' '), ++out;
			out = copy_chars(node->value(), node->value() + node->value_size(), out);
			*out = Ch('>'), ++out;
			return out;
		}

		// Print pi node
		template<class OutIt, class Ch>
		inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
		{
			assert(node->type() == node_pi);
			if (!(flags & print_no_indenting))
				out = fill_chars(out, indent, Ch('\t'));
			*out = Ch('<'), ++out;
			*out = Ch('?'), ++out;
			out = copy_chars(node->name(), node->name() + node->name_size(), out);
			*out = Ch(' '), ++out;
			out = copy_chars(node->value(), node->value() + node->value_size(), out);
			*out = Ch('?'), ++out;
			*out = Ch('>'), ++out;
			return out;
		}
	}
	//! \endcond

	///////////////////////////////////////////////////////////////////////////
	// Printing

	//! Prints XML to given output iterator.
	//! \param out Output iterator to print to.
	//! \param node Node to be printed. Pass xml_document to print entire document.
	//! \param flags Flags controlling how XML is printed.
	//! \return Output iterator pointing to position immediately after last character of printed text.
	template<class OutIt, class Ch>
	inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
	{
		return internal::print_node(out, &node, flags, 0);
	}
}

#endif
