/*
 * NaryOperationAbstraction.hpp
 *
 *  Created on: 20. 8. 2017
 *	  Author: Jan Travnicek
 */

#pragma once

#include <alib/memory>
#include <alib/array>

#include <abstraction/OperationAbstraction.hpp>
#include <common/AbstractionHelpers.hpp>

namespace abstraction {

template < size_t NumberOfParams >
class NaryOperationAbstractionImpl : virtual public OperationAbstraction {
	ext::array < std::shared_ptr < abstraction::Value >, NumberOfParams > m_params;

protected:
	ext::array < std::shared_ptr < abstraction::Value >, NumberOfParams > & getParams ( ) {
		return m_params;
	}

	virtual std::shared_ptr < abstraction::Value > run ( ) = 0;

private:
	void attachInput ( const std::shared_ptr < abstraction::Value > & input, size_t index ) override {
		if ( index >= m_params.size ( ) )
			throw std::invalid_argument ( "Parameter index " + ext::to_string ( index ) + " out of bounds.");

		m_params [ index ] = input;
	}

	void detachInput ( size_t index ) override {
		if ( index >= m_params.size ( ) )
			throw std::invalid_argument ( "Parameter index " + ext::to_string ( index ) + " out of bounds.");

		m_params [ index ] = nullptr;
	}

public:
	explicit NaryOperationAbstractionImpl ( ) {
		if constexpr ( NumberOfParams != 0 )
			for ( size_t i = 0; i < NumberOfParams; ++ i ) {
				m_params [ i ] = nullptr;
			}
	}

	bool inputsAttached ( ) const override {
		return std::all_of ( m_params.begin ( ), m_params.end ( ), [ ] ( const std::shared_ptr < abstraction::Value > & param ) { return static_cast < bool > ( param ); } );
	}

	std::shared_ptr < abstraction::Value > eval ( ) override {
		if ( ! inputsAttached ( ) )
			return nullptr;

		return this->run ( );
	}

	size_t numberOfParams ( ) const override {
		return NumberOfParams;
	}

};

} /* namespace abstraction */

extern template class abstraction::NaryOperationAbstractionImpl < 0 >;
extern template class abstraction::NaryOperationAbstractionImpl < 1 >;
extern template class abstraction::NaryOperationAbstractionImpl < 2 >;
extern template class abstraction::NaryOperationAbstractionImpl < 3 >;

namespace abstraction {

template < class ... ParamTypes >
class NaryOperationAbstraction : virtual public NaryOperationAbstractionImpl < sizeof ... ( ParamTypes ) > {
public:
	ext::type_index getParamTypeIndex ( size_t index ) const override {
		return abstraction::paramType < ParamTypes ... > ( index );
	}

	abstraction::TypeQualifiers::TypeQualifierSet getParamTypeQualifiers ( size_t index ) const override {
		return abstraction::paramTypeQualifiers < ParamTypes ... > ( index );
	}

};

} /* namespace abstraction */

