#pragma once

#include <ostream>
#include <string>


namespace xmlcc
{
	struct element_start_tag
	{
		std::ostream& out_;

		element_start_tag(std::ostream& out, std::string const& name)
			: out_(out)
		{
			out_ << "<" << name;
		};

		element_start_tag(element_start_tag& tag)
			: out_(tag.out_)
		{
		};

		element_start_tag& attribute(std::string const& name, std::string const& value)
		{
			out_ << " " << name << "=" << "\"" << value << "\"";
			return *this;
		}

		template<class Func>
		element_start_tag& attribute_with_generator(std::string const& name, Func const& f)
		{
			out_ << " " << name << "=" << "\"";
			f(out_);
			out << "\"";
			return *this;
		}

		element_start_tag& operator()(std::string const& name, std::string const& value)
		{
			return attribute(name, value);
		}

		void close_now()
		{
			out_ << "/";
		}

		~element_start_tag()
		{
			out_ << ">";
		};
	};


	element_start_tag element_start(std::ostream& out, std::string const& name)
	{
		return element_start_tag(out, name);
	}

	void element_end(std::ostream& out, std::string const& name)
	{
		out << "</" << name << ">";
	}

	struct pcdata_escape
	{
		bool operator()(char const& c)
		{
			if( c == '&' || c == '<' || c == '>' || c == '"' || c == '\'')
			{
				return true;
			}
			return false;
		}
	};

	template<class IteratorT>
	void text(std::ostream& out, IteratorT first, IteratorT last)
	{
		typedef typename std::iterator_traits<IteratorT>::value_type ctype;

		IteratorT itr = std::find_if(first, last, pcdata_escape());

		std::copy(first, itr, std::ostreambuf_iterator<ctype>(out));

		if(itr == last)
		{
			return;
		}

		

		if(*itr == '&')
		{
			out << "&amp;";
		}
		else if(*itr == '<')
		{
			out << "&lt;";
		}
		else if(*itr == '>')
		{
			out << "&gt;";
		}
		else if(*itr == '\"')
		{
			out << "&quot;";
		}
		else if(*itr == '\'')
		{
			out << "&apos;";
		}

		++itr;
		return text(out, itr, last);
	}

	void text(std::ostream& out, std::string const& data)
	{
		text(out, data.begin(), data.end());
	}


	template<class IteratorT>
	void cdata(std::ostream& out, IteratorT first, IteratorT last)
	{
		typedef typename std::iterator_traits<IteratorT>::value_type ctype;

		static const char CDATA_END_TOKEN[] = "]]>";

		out << "<![CDATA[";

		IteratorT itr = std::search(first, last, CDATA_END_TOKEN, CDATA_END_TOKEN+3);

		std::copy(first, itr, std::ostreambuf_iterator<ctype>(out));

		if(itr == last)
		{
			out << "]]>";
			return;
		}

		++itr;
		++itr;

		out << "]]]]>";

		return cdata(out, itr, last);
	}

	void cdata(std::ostream& out, std::string const& data)
	{
		cdata(out, data.begin(), data.end());
	}

	void header(std::ostream& out, std::string const& version, std::string const& encoding)
	{
		out << "<?xml version=\"" << version << "\" encoding=\"" << encoding << "\"?>";
	}
}

