#pragma once

#include <ast/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;

	static void typePrint ( const ext::pair < std::string, abstraction::TypeQualifiers::TypeQualifierSet > & result, std::ostream & os ) {
		if ( abstraction::TypeQualifiers::isConst ( std::get < 1 > ( result ) ) )
			os << "const ";

		os << std::get < 0 > ( result );

		if ( abstraction::TypeQualifiers::isLvalueRef ( std::get < 1 > ( result ) ) )
			os << " &";

		if ( abstraction::TypeQualifiers::isRvalueRef ( std::get < 1 > ( result ) ) )
			os << " &&";
	}

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 ) ) {
	}

	CommandResult 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;

			typePrint ( std::get < 0 > ( overload ).getNormalizedResult ( ), common::Streams::out );

			common::Streams::out << " (";
			for ( size_t i = 0; i < std::get < 0 > ( overload ).getParams ( ).size ( ); ++ i ) {
				if ( i != 0 )
					common::Streams::out << ",";

				common::Streams::out << " ";

				typePrint ( std::get < 0 > ( overload ).getParams ( ) [ i ], common::Streams::out );

				common::Streams::out << " " << std::get < 0 > ( overload ).getParamNames ( ) [ i ];
			}
			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 CommandResult::OK;
	}
};

} /* namespace cli */

