#ifndef XMLWRITER_H
#define XMLWRITER_H

#include <fstream>
#include <string>
#include <stack>

class XmlWriter {
	public:
		XmlWriter() : _file(0), _stream(0)
		{
		}

		~XmlWriter()
		{
			Close();
		}

		void Close()
		{
			if(_file != 0)
			{
				while(_openedElements.size() != 0)
					Close();
				delete _file;
				_file = 0;
			}
		}

		void StartDocument(const std::string &filename)
		{
			Close();
			_file = new std::ofstream(filename.c_str());
			_stream = _file;
			startDocument();
		}

		void StartDocument(std::ostream &stream)
		{
			Close();
			_stream = &stream;
			startDocument();
		}

		void Comment(const char *comment)
		{
			startNewTag();

			(*_stream) << "<!-- " << comment << "-->";
		}

		void Start(const char *element)
		{
			startNewTag();

			(*_stream) << '<' << element;
			_inTag = true;
			_tagHasData = false;
			_openedElements.push(element);
		}

		void End()
		{
			if(_openedElements.size() == 0)
				throw std::runtime_error("End() called without open element");
			if(_inTag)
				(*_stream) << " />";
			else {
				(*_stream) << '\n';
				indent(_openedElements.size()-1);
				(*_stream) << "</" << _openedElements.top() << '>';
			}
			_openedElements.pop();
			_inTag = false;
		}

		void Attribute(const char *attributeName, const char *value)
		{ 
			if(!_inTag)
				throw std::runtime_error("Attribute() called incorrectly");
			(*_stream) << ' ' << attributeName << "=\"" << value << "\"";
		}

		template<typename ValueType>
		void Attribute(const char *attributeName, ValueType value)
		{ 
			std::stringstream s;
			s << value;
			Attribute(attributeName, s.str().c_str());
		}

		void Write(const char *element, const char *value)
		{
			startNewTag();

			(*_stream) << '<' << element << '>' << value << "</" << element << '>';
		}

		template<typename ValueType>
		void Write(const char *element, ValueType value)
		{
			std::stringstream s;
			s << value;
			Write(element, s.str().c_str());
		}

	private:
		void startDocument()
		{
			(*_stream) << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
			_inTag = false;
		}
		void closeTag()
		{
			if(_inTag)
			{
				(*_stream) << '>';
				_inTag = false;
			}
		}
		void startNewTag()
		{
			closeTag();
			(*_stream) << '\n';
			indent(_openedElements.size());
		}

		void indent(unsigned depth)
		{
			for(unsigned i=0;i<depth;++i)
				(*_stream) << ' ' << ' ';
		}

		std::stack<std::string> _openedElements;

		std::ofstream *_file;
		std::ostream *_stream;
		bool _inTag;
		bool _tagHasData;
};

#endif
