/*
 * XmlApi.hpp
 *
 * Created on: Apr 1, 2013
 * Author: Jan Travnicek
 */

#ifndef XML_API_HPP_
#define XML_API_HPP_

#include <alib/deque>
#include <alib/map>
#include <alib/string>
#include <alib/memory>
#include <alib/typeinfo>
#include "sax/Token.h"
#include "sax/FromXMLParserHelper.h"

#include "object/Object.h"
#include "object/AnyObject.h"

#include <global/GlobalData.h>

#include "exception/CommonException.h"

namespace core {

template < typename T >
struct xmlApi;

class xmlApiInputContext : public ext::deque < sax::Token >::iterator {
	ext::map < int, object::Object > idToInstanceContexts;

public:
	explicit xmlApiInputContext ( const ext::deque < sax::Token >::iterator & iter ) : ext::deque < sax::Token >::iterator ( iter ) {
	}

	ext::map < int, object::Object > & idToInstance ( ) {
		return idToInstanceContexts;
	}

};

class xmlApiOutputContext : public ext::deque < sax::Token > {
	ext::map < object::Object, int > instanceToIdContexts;
	int idMaxContext = 0;

public:
	xmlApiOutputContext ( ) = default;

	ext::map < object::Object, int > & instanceToId ( ) {
		return instanceToIdContexts;
	}

	int & idMax ( ) {
		return idMaxContext;
	}

};

template < >
struct xmlApi < object::Object > {
public:
	class GroupParser {
	public:
		virtual object::Object parse ( ext::deque < sax::Token >::iterator & input ) = 0;

		virtual ~GroupParser ( ) noexcept = default;
	};

private:
	static ext::map < std::string, std::unique_ptr < GroupParser > > & parseFunctions ( );

	template < class Type >
	class ParserRegister : public GroupParser {
	public:
		object::Object parse ( ext::deque < sax::Token >::iterator & input ) override {
			return object::Object ( xmlApi < Type >::parse ( input ) );
		}

	};

public:
	class GroupComposer {
	public:
		virtual void compose ( ext::deque < sax::Token > & input, const object::Object & group ) = 0;

		virtual ~GroupComposer( ) noexcept = default;
	};

private:
	static ext::map < std::string, std::unique_ptr < GroupComposer > > & composeFunctions ( );

	template < class Type >
	class ComposerRegister : public GroupComposer {
	public:
		void compose ( ext::deque < sax::Token > & input, const object::Object & group ) override {
			xmlApi < Type >::compose ( input, static_cast < const object::AnyObject < Type > & > ( group.getData ( ) ).getData ( ) );
		}

	};

public:
	static void unregisterXmlReader ( const std::string & tagName, const std::string & typeName );

	template < class Type >
	static void unregisterXmlReader ( ) {
		unregisterXmlReader ( xmlApi < Type >::xmlTagName ( ), ext::to_string < Type > ( ) );
	}

	static void registerXmlReader ( std::string tagName, const std::string & typeName, std::unique_ptr < GroupParser > entry );

	template < class Type >
	static void registerXmlReader ( ) {
		registerXmlReader ( xmlApi < Type >::xmlTagName ( ), ext::to_string < Type > ( ), std::unique_ptr < GroupParser > ( new ParserRegister < Type > ( ) ) );
	}

	static void unregisterXmlWriter ( const std::string & type, const std::string & typeName );

	template < class Type >
	static void unregisterXmlWriter ( ) {
		unregisterXmlWriter ( ext::to_string < object::AnyObject < Type > > ( ), ext::to_string < Type > ( ) );
	}

	static void registerXmlWriter ( std::string type, const std::string & typeName, std::unique_ptr < GroupComposer > entry );

	template < class Type >
	static void registerXmlWriter ( ) {
		registerXmlWriter ( ext::to_string < object::AnyObject < Type > > ( ), ext::to_string < Type > ( ), std::unique_ptr < GroupComposer > ( new ComposerRegister < Type > ( ) ) );
	}

	static object::Object parseRef ( xmlApiInputContext & input );

	static object::Object parseUnique ( xmlApiInputContext & input );

	static object::Object parseObject ( xmlApiInputContext & input, const std::string & tagName );

	static object::Object parse ( xmlApiInputContext & input, const std::string & tagName );

	static object::Object parse ( ext::deque < sax::Token >::iterator & data );

	static bool first ( const ext::deque < sax::Token >::const_iterator & input );

	static std::string xmlTagName ( );

	static void composeRef ( xmlApiOutputContext & output, typename ext::map < object::Object, int >::iterator & elem );

	static void composeUnique ( xmlApiOutputContext & output, const object::Object & data );

	static void composeObject ( xmlApiOutputContext & output, const object::Object & data );

	static void compose ( ext::deque < sax::Token > & data, const object::Object & object );

};

} /* namespace core */

#endif /* XML_API_HPP_ */
