/*
 * XmlDataFactory.hpp
 *
 *  Created on: Jan 1, 2014
 *      Author: Jan Travnicek
 */

#ifndef XML_DATA_FACTORY_HPP_
#define XML_DATA_FACTORY_HPP_

#include <alib/string>
#include <alib/deque>
#include <alib/measure>

#include <sax/Token.h>
#include <sax/SaxParseInterface.h>
#include <sax/SaxComposeInterface.h>
#include <core/xmlApi.hpp>
#include <measure/xml/MeasurementResults.hpp>

namespace factory {

/**
 * Data builder.
 */
class XmlDataFactory {
public:

	class fromFile {
		const std::string & filename;

	public:
		fromFile ( const std::string & file ) : filename ( file ) {
		}

		/**
		 * Parses the XML in file and returns the String.
		 * @param filename path to the file
		 * @return String
		 */
		template < class T >
		operator T ( ) {
			return fromTokens ( sax::SaxParseInterface::parseFile ( filename ) );
		}
	};

	class fromString {
		const std::string & string;

	public:
		fromString ( const std::string & str ) : string ( str ) {
		}

		/**
		 * Parses the XML and returns the String.
		 * @param str string containing the XML
		 * @return String
		 */
		template < class T >
		operator T ( ) {
			return fromTokens ( sax::SaxParseInterface::parseMemory ( string ) );
		}
	};

	class fromStdin {
	public:
		/**
		 * Parses the XML from stdin and returns the String.
		 * @return String
		 */
		template < class T >
		operator T ( ) {
			return fromTokens ( sax::SaxParseInterface::parseStdin ( ) );
		}
	};

	class fromStream {
		std::istream & in;

	public:
		fromStream ( std::istream & i ) : in ( i ) {
		}

		/**
		 * Parses the XML from stream and returns the String.
		 * @return String
		 */
		template < class T >
		operator T ( ) {
			return fromTokens ( sax::SaxParseInterface::parseStream ( in ) );

		}
	};

	class fromTokens {
		ext::deque < sax::Token > tokens;

	public:
		fromTokens ( ext::deque < sax::Token > && toks ) : tokens ( std::move ( toks ) ) {
		}

		/**
		 * Parses the String from list of tokens.
		 * @param tokens XML represented as list of tokens
		 * @return parsed String
		 */
		template < class T >
		operator T ( ) {
			core::xmlApiInputContext context ( tokens.begin ( ) );

			if ( context == tokens.end ( ) ) throw exception::CommonException ( "Empty tokens list" );

			measurements::start ( "XML Parser", measurements::Type::INIT );
			T res = core::xmlApi < T >::parse ( context );
			measurements::end ( );

			if ( context != tokens.end ( ) ) throw exception::CommonException ( "Unexpeted tokens at the end of the xml" );

			return res;
		}
	};

	/**
	 * Determines whether the input token stream contains given type
	 * @param tokens
	 * @return bool
	 */
	template < class T >
	static bool first ( const ext::deque < sax::Token > & tokens ) {
		if( tokens.empty ( ) ) throw exception::CommonException ( "Empty tokens list" );

		return core::xmlApi < T >::first ( tokens.begin ( ) );
	}

	/**
	 * Parses the XML in file and returns the String.
	 * @param filename path to the file
	 * @return String
	 */
	template < class T >
	static void toFile ( const T & data, const std::string & filename ) {
		ext::deque < sax::Token > tokens = toTokens < T > ( data );
		sax::SaxComposeInterface::composeFile ( filename, tokens );
	}

	/**
	 * Parses the XML and returns the String.
	 * @param str string containing the XML
	 * @return String
	 */
	template < class T >
	static std::string toString ( const T & data ) {
		ext::deque < sax::Token > tokens = toTokens < T > ( data );
		return sax::SaxComposeInterface::composeMemory ( tokens );
	}

	/**
	 * Parses the XML from stdin and returns the String.
	 * @return String
	 */
	template < class T >
	static void toStdout ( const T & data ) {
		ext::deque < sax::Token > tokens = toTokens < T > ( data );
		sax::SaxComposeInterface::composeStdout ( tokens );
	}

	/**
	 * Parses the XML from stream and returns the String.
	 * @return String
	 */
	template < class T >
	static void toStream ( const T & data, std::ostream & out ) {
		ext::deque < sax::Token > tokens = toTokens < T > ( data );
		sax::SaxComposeInterface::composeStream ( out, tokens );
	}

	/**
	 * Parses the String from list of tokens.
	 * @param tokens XML represented as list of tokens
	 * @return parsed String
	 */
	template < class T >
	static ext::deque < sax::Token > toTokens ( const T & data ) {
		core::xmlApiOutputContext context;

		measurements::start ( "XML Composer", measurements::Type::FINALIZE );
		core::xmlApi < T >::compose ( context, data );
		measurements::end ( );

		return std::move ( context );
	}

};

template < >
inline XmlDataFactory::fromTokens::operator measurements::MeasurementResults ( ) {
	core::xmlApiInputContext context ( tokens.begin ( ) );

	if ( context == tokens.end ( ) ) throw exception::CommonException ( "Empty tokens list" );

	measurements::MeasurementResults res = core::xmlApi < measurements::MeasurementResults >::parse ( context );

	if ( context != tokens.end ( ) ) throw exception::CommonException ( "Unexpeted tokens at the end of the xml" );

	return res;
}

template < >
inline ext::deque < sax::Token > XmlDataFactory::toTokens ( const measurements::MeasurementResults & data ) {
	core::xmlApiOutputContext context;

	core::xmlApi < measurements::MeasurementResults >::compose ( context, data );

	return std::move ( context );
}

} /* namespace factory */

#endif /* XML_DATA_FACTORY_HPP_ */
