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

#ifndef _ANYARY_OPERATION_ABSTRACTION_HPP_
#define _ANYARY_OPERATION_ABSTRACTION_HPP_

#include <alib/memory>

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

#include <registry/Registry.h>

namespace abstraction {

template < class ReturnType, class ParamType >
class AnyaryOperationAbstraction : public ValueOperationAbstraction < ReturnType > {
protected:
	ext::vector < std::pair < std::shared_ptr < OperationAbstraction >, bool > > m_params;

private:
	virtual bool attachInput ( const std::shared_ptr < OperationAbstraction > & input, unsigned index, bool move, bool checkInput ) override {
		if ( input == nullptr )
			return false;

		if ( checkInput && ! abstraction::CheckInput < ValueProvider < ParamType > >::checkInput ( input->getProxyAbstraction ( ), 0 /* Note: yes index zero */ ) )
			return false;

		if ( m_params.size ( ) < index + 1 )
			m_params.resize ( index + 1 );

		m_params [ index ] = std::make_pair ( input, move );
		return true;
	}

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

		m_params [ index ].first = nullptr;
		m_params [ index ].second = false;

		return true;
	}

public:
	AnyaryOperationAbstraction ( ) {
	}

	virtual bool inputsAttached ( ) const override {
		for ( const std::pair < std::shared_ptr < OperationAbstraction >, bool > & param : m_params )
			if ( ! ( bool ) param.first )
				return false;

		return true;
	}

	virtual bool eval ( ) override {
		if ( ! inputsAttached ( ) )
			return false;

		if ( this->cached ( ) )
			return true;

		for ( const std::pair < std::shared_ptr < OperationAbstraction >, bool > & param : m_params )
			if ( ! param.first->eval ( ) )
				return false;

		return this->run ( );
	}

	virtual unsigned numberOfParams ( ) const override {
		return m_params.size ( );
	}

	virtual ext::type_index getParamTypeIndex ( unsigned ) const override {
		return ext::type_index ( typeid ( ParamType ) );
	}

};

} /* namespace abstraction */

#endif /* _ANYARY_OPERATION_ABSTRACTION_HPP_ */