//
// Copyright (c) 2009-2010 Ronald Kluth
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

/**
 * @file CppLog/output/XmlWriter.h
 * @author Ronald Kluth
 * @date 2010/03/25
 * @brief Declaration and implementation of abstract class XmlWriter
 * @since 0.1
 */

#ifndef LOG_OUTPUT_XMLWRITER_INCLUDED
#define LOG_OUTPUT_XMLWRITER_INCLUDED

#include "../detail/InfoTypeLoop.h"
#include "../Consumer.h"
#include "../StringLiteral.h"

#include <ostream>
#include <string>

namespace Log {

/**
 * @brief Abstract base class for consumers that write records to output streams in XML
 *
 * This base class provides XML output components with some basic
 * static methods for creating correct XML markup strings.
 *
 * @note Be aware that string names of info types should not contain white space
 * because these names are used  as XML tag names.
 */
class XmlWriter : public Consumer< Record >
{
public:
	/// Construction
	XmlWriter()
	{
	}
	
	/// Destruction
	virtual ~XmlWriter()
	{
	}

	/// Prints start tag to the stream: <element>
	static void startElement( std::ostream& os, unsigned int indent,
			std::string const& tag )
	{
		os << std::string( indent, ' ' ) << "<" << tag << ">";
	}

	/// Prints close tag to the stream: </element>
	static void closeElement( std::ostream& os, unsigned int indent,
			std::string const& tag )
	{
		os << std::string( indent, ' ' ) << "</" << tag << ">" << std::endl;
	}

	/// Prints an empty element to the stream: <element/>
	static void emptyElement( std::ostream& os, unsigned int indent,
			std::string const& tag )
	{
		os << std::string( indent, ' ' ) << "<" << tag << "/>" << std::endl;
	}

	/// Prints a complete element to the stream: <element>value</element>
	template< typename ValueT >
	static void writeElement( std::ostream& os, unsigned int indent,
			std::string const& tag, ValueT const& value )
	{
		startElement( os, indent, tag );
		os << value;
		closeElement( os, 0, tag ); 
	}

	/// Prints an XML header to the stream to start a document
	static void writeHeader( std::ostream& os )
	{
		os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
	}
private:
	/// Non-copyable
	XmlWriter( XmlWriter const& );
	/// Non-assignable
	XmlWriter& operator=( XmlWriter const& );

protected:
	/// Functor that transforms record info into XML elements and writes them to a stream
	struct StreamInfoElements
	{
		std::ostream& stream_;
		unsigned int indent_;

		StreamInfoElements( std::ostream& stream, unsigned int indent )
		:	stream_( stream )
		,	indent_( indent )
		{}
		
		template < typename InfoValueT >
		void operator()( Log::StringLiteral const& name, InfoValueT const& value, bool found )
		{
			if( found )
			{
				writeElement( stream_, indent_, name.c_str(), value );
			}
			else
			{
				emptyElement( stream_, indent_, name.c_str() );
			}
		}
	};
};

} // namespace Log

#endif /* LOG_OUTPUT_XMLWRITER_INCLUDED */
