/*
 * UnboundedRegExpElement.h
 *
 *  Created on: Nov 23, 2013
 *      Author: Jan Travnicek
 */

#ifndef VISITOR_HPP_
#define VISITOR_HPP_

#include <alib/utility>
#include <alib/tuple>

namespace core {

/**
 * \brief
 * Class implementing an actual visitor interface to the visitor.
 *
 * The interface is represented by the return type, the visitor class implementing the callbacks, and additional params of the call.
 *
 */
template < class ReturnType, class Visitor, class ... Params >
class VisitorContextAux {
	/**
	 * \brief
	 * A place for the result.
	 */
	typename std::aligned_storage < sizeof ( ReturnType ), alignof ( ReturnType ) >::type result;

	/**
	 * \brief
	 * A place for the params.
	 */
	ext::tuple < Params ... > m_params;

public:
	/**
	 * \brief
	 * Constructor for initialisation of the visitor context, i.e. additional call parameters.
	 */
	VisitorContextAux ( Params && ... params ) : m_params ( std::forward < Params > ( params ) ... ) {
	}

	/**
	 * \brief
	 * Call method to the visitors visit method. The actuall class of visited object is already evaluated here.
	 */
	template < class Inherit, size_t ... Indexes >
	void call ( const Inherit & inherit, std::index_sequence < Indexes ... > ) {
		new ( & result ) ReturnType ( Visitor::visit ( inherit, std::forward < Params > ( std::get < Indexes > ( m_params ) ) ... ) );
	}

	/**
	 * \brief
	 * Call method to the visitors visit method. The actuall class of visited object is already evaluated here.
	 */
	template < class Inherit, size_t ... Indexes >
	void call ( Inherit & inherit, std::index_sequence < Indexes ... > ) {
		new ( & result ) ReturnType ( Visitor::visit ( inherit, std::forward < Params > ( std::get < Indexes > ( m_params ) ) ... ) );
	}

	/**
	 * \brief
	 * Call method to the visitors visit method. The actuall class of visited object is already evaluated here.
	 */
	template < class Inherit, size_t ... Indexes >
	void call ( Inherit && inherit, std::index_sequence < Indexes ... > ) {
		new ( & result ) ReturnType ( Visitor::visit ( std::forward < Inherit > ( inherit ), std::forward < Params > ( std::get < Indexes > ( m_params ) ) ... ) );
	}

	/**
	 * \brief
	 * Visit result getter.
	 */
	ReturnType getResult ( ) {
		ReturnType res = reinterpret_cast < ReturnType && > ( result );

		 // reinterpret_cast < ReturnType * > ( & result )->~ReturnType ( ); // not needed to delete; data gets moved from on previous line
		return res;
	}

};

/**
 * \brief
 * Class implementing an actual visitor interface to the visitor.
 *
 * The interface is represented by the return type, the visitor class implementing the callbacks, and additional params of the call.
 *
 */
template < class Visitor, class ... Params >
class VisitorContextAux < void, Visitor, Params ... > {

	/**
	 * \brief
	 * A place for the params.
	 */
	ext::tuple < Params ... > m_params;

public:
	/**
	 * \brief
	 * Constructor for initialisation of the visitor context, i.e. additional call parameters.
	 */
	VisitorContextAux ( Params && ... params ) : m_params ( std::forward < Params > ( params ) ... ) {
	}

	/**
	 * \brief
	 * Call method to the visitors visit method. The actuall class of visited object is already evaluated here.
	 */
	template < class Inherit, size_t ... Indexes >
	void call ( const Inherit & inherit, std::index_sequence < Indexes ... > ) {
		Visitor::visit ( inherit, std::forward < Params > ( std::get < Indexes > ( m_params ) ) ... );
	}

	/**
	 * \brief
	 * Call method to the visitors visit method. The actuall class of visited object is already evaluated here.
	 */
	template < class Inherit, size_t ... Indexes >
	void call ( Inherit & inherit, std::index_sequence < Indexes ... > ) {
		Visitor::visit ( inherit, std::forward < Params > ( std::get < Indexes > ( m_params ) ) ... );
	}

	/**
	 * \brief
	 * Call method to the visitors visit method. The actuall class of visited object is already evaluated here.
	 */
	template < class Inherit, size_t ... Indexes >
	void call ( Inherit && inherit, std::index_sequence < Indexes ... > ) {
		Visitor::visit ( std::forward < Inherit > ( inherit ), std::forward < Params > ( std::get < Indexes > ( m_params ) ) ... );
	}

	/**
	 * \brief
	 * Visit result getter.
	 */
	void getResult ( ) {
	}

};

} /* namespace core */

#endif /* VISITOR_HPP_ */
