#ifndef _CLI_OVERLOADS_INTROSPECTION_COMMAND_H_
#define _CLI_OVERLOADS_INTROSPECTION_COMMAND_H_

#include <command/Command.h>
#include <environment/Environment.h>

namespace cli {

class OverloadsIntrospectionCommand : public Command {
	std::unique_ptr < cli::Arg > m_param;
	ext::vector < std::unique_ptr < cli::Arg > > m_templateParams;

public:
	OverloadsIntrospectionCommand ( std::unique_ptr < cli::Arg > param, ext::vector < std::unique_ptr < cli::Arg > > templateParams ) : m_param ( std::move ( param ) ), m_templateParams ( std::move ( templateParams ) ) {
	}

	Command::Result run ( Environment & environment ) const override {
		std::string param = m_param->eval ( environment );
		ext::vector < std::string > templateParams;
		for ( const std::unique_ptr < cli::Arg > & templateParam : m_templateParams )
			templateParams.push_back ( templateParam->eval ( environment ) );

		ext::list < ext::tuple < abstraction::AlgorithmFullInfo, std::string > > overloads = abstraction::Registry::listOverloads ( param, templateParams );
		bool first = false;

		for ( const ext::tuple < abstraction::AlgorithmFullInfo, std::string > & overload : overloads ) {

			if ( first )
				common::Streams::out << std::endl << "-------------------------------------------------------------------------------------" << std::endl;

			{
				const ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & result = std::get < 0 > ( overload ).getNormalizedResult ( );

				if ( std::get < 1 > ( result ).contains ( abstraction::ParamQualifiers::ParamQualifier::CONST ) )
					common::Streams::out << "const ";

				common::Streams::out << std::get < 0 > ( result );

				if ( std::get < 1 > ( result ).contains ( abstraction::ParamQualifiers::ParamQualifier::LREF ) )
					common::Streams::out << " &";

				if ( std::get < 1 > ( result ).contains ( abstraction::ParamQualifiers::ParamQualifier::RREF ) )
					common::Streams::out << " &&";
			}
			common::Streams::out << " (";
			for ( size_t i = 0; i < std::get < 0 > ( overload ).getParams ( ).size ( ); ++ i ) {
				const ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & parameter = std::get < 0 > ( overload ).getParams ( ) [ i ];
				const std::string & name = std::get < 0 > ( overload ).getParamNames ( ) [ i ];
				if ( i != 0 )
					common::Streams::out << ",";

				if ( std::get < 1 > ( parameter ).contains ( abstraction::ParamQualifiers::ParamQualifier::CONST ) )
					common::Streams::out << " const ";
				else
					common::Streams::out << " ";

				common::Streams::out << std::get < 0 > ( parameter );

				if ( std::get < 1 > ( parameter ).contains ( abstraction::ParamQualifiers::ParamQualifier::LREF ) )
					common::Streams::out << " &";

				if ( std::get < 1 > ( parameter ).contains ( abstraction::ParamQualifiers::ParamQualifier::RREF ) )
					common::Streams::out << " &&";

				common::Streams::out << " " << name;
			}
			common::Streams::out << " )" << std::endl << std::endl;

			common::Streams::out << "Category: " << std::get < 0 > ( overload ).getCategory ( ) << std::endl << std::endl;

			common::Streams::out << std::get < 1 > ( overload ) << std::endl;
			first = true;
		}

		return Command::Result::OK;
	}
};

} /* namespace cli */

#endif /* _CLI_OVERLOADS_INTROSPECTION_COMMAND_H_ */
