/*
 * MemberAbstraction.hpp
 *
 *  Created on: 11. 7. 2017
 *	  Author: Jan Travnicek
 */

#ifndef _MEMBER_ABSTRACTION_HPP_
#define _MEMBER_ABSTRACTION_HPP_

#include <alib/memory>

#include <abstraction/NaryOperationAbstraction.hpp>
#include <abstraction/ReferenceAbstraction.hpp>
#include <abstraction/ValueOperationAbstraction.hpp>

namespace abstraction {

template < class ObjectType, class ReturnType, class ... ParamTypes >
class MemberAbstraction : virtual public NaryOperationAbstraction < ObjectType &, ParamTypes ... >, virtual public ValueOperationAbstraction < ReturnType > {
	std::function < ReturnType ( typename std::remove_reference < ObjectType >::type *, ParamTypes ... ) > m_callback;

public:
	MemberAbstraction ( std::function < ReturnType ( typename std::remove_reference < ObjectType >::type *, ParamTypes ... ) > callback ) : m_callback ( std::move ( callback ) ) {
	}

	std::shared_ptr < abstraction::Value > run ( ) override {
		ext::array < std::pair < std::shared_ptr < abstraction::Value >, bool >, sizeof ... ( ParamTypes ) + 1 > params = this->getParams ( );

		std::shared_ptr < OperationAbstraction > reference = std::make_shared < ReferenceAbstraction < typename std::remove_reference < ObjectType >::type > > ( );
		reference->attachInput ( std::get < 0 > ( this->getParams ( ) ).first, 0, std::get < 0 > ( this->getParams ( ) ).second );

		std::shared_ptr < abstraction::Value > ref = reference->eval ( );
		if ( ! ref )
			throw std::invalid_argument ( "Eval of object of call to member falsed." );

		std::get < 0 > ( params ) = std::make_pair ( ref, false );

		return this->template run_helper < typename std::remove_reference < ObjectType >::type *, ParamTypes ... > ( m_callback, params );
	}

};

} /* namespace abstraction */

#endif /* _MEMBER_ABSTRACTION_HPP_ */
