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

#ifndef _RAW_ABSTRACTION_HPP_
#define _RAW_ABSTRACTION_HPP_

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

namespace abstraction {

class RawAbstraction : virtual public OperationAbstraction {
	ext::vector < ext::tuple < std::string, abstraction::TypeQualifiers::TypeQualifierSet > > m_paramSpecs;
	std::function < std::shared_ptr < abstraction::Value > ( std::vector < std::shared_ptr < abstraction::Value > > ) > m_callback;
	ext::vector < std::shared_ptr < abstraction::Value > > m_params;

public:
	RawAbstraction ( ext::vector < ext::tuple < std::string, abstraction::TypeQualifiers::TypeQualifierSet > > paramSpecs, std::function < std::shared_ptr < abstraction::Value > ( std::vector < std::shared_ptr < abstraction::Value > > ) > callback ) : m_paramSpecs ( paramSpecs ), m_callback ( std::move ( callback ) ), m_params ( m_paramSpecs.size ( ) ) {
	}

private:
	std::shared_ptr < abstraction::Value > run ( ) {
		std::vector < std::shared_ptr < abstraction::Value > > params;

		for ( const std::shared_ptr < abstraction::Value > & param : m_params )
			params.push_back ( param );

		return m_callback ( params );
	}

	void attachInput ( const std::shared_ptr < abstraction::Value > & input, size_t index ) override;

	void detachInput ( size_t index ) override;

public:
	bool inputsAttached ( ) const override;

	std::shared_ptr < abstraction::Value > eval ( ) override;

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

	bool isParamTypeUniversal ( size_t ) const override {
		return true;
	}

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

		if ( m_params [ index ] == nullptr )
			throw std::domain_error ( "Parameter index " + ext::to_string ( index ) + " not yet set.");

		return m_params [ index ]->getTypeIndex ( );
	}

	abstraction::TypeQualifiers::TypeQualifierSet getParamTypeQualifiers ( size_t index ) const override {
		if ( index >= m_params.size ( ) )
			throw std::invalid_argument ( "Parameter index " + ext::to_string ( index ) + " out of bounds.");

		if ( m_params [ index ] == nullptr )
			throw std::domain_error ( "Parameter index " + ext::to_string ( index ) + " not yet set.");

		return m_params [ index ]->getTypeQualifiers ( );
	}

	bool isReturnTypeUniversal ( ) const override {
		return true;
	}

	abstraction::TypeQualifiers::TypeQualifierSet getReturnTypeQualifiers ( ) const override {
		throw std::domain_error ( "Return type not available.");
	}

	ext::type_index getReturnTypeIndex ( ) const override {
		throw std::domain_error ( "Return type not available.");
	}

};

} /* namespace abstraction */

#endif /* _RAW_ABSTRACTION_HPP_ */
