#include <ast/statements/ContainerStatement.h>
#include <ast/Option.h>
#include <ast/Arg.h>
#include <common/CastHelper.h>
#include <exception>
#include <alib/iostream>
#include <registry/Registry.h>

namespace cli {

ContainerStatement::ContainerStatement ( std::string container, ext::vector < std::shared_ptr < Statement > > params, std::unique_ptr < TypeOption > type ) : m_container ( std::move ( container ) ), m_params ( std::move ( params ) ), m_type ( std::move ( type ) ) {
}

std::shared_ptr < abstraction::Value > ContainerStatement::translateAndEval ( const std::shared_ptr < abstraction::Value > & prev, Environment & environment ) const {
	ext::vector < std::shared_ptr < abstraction::Value > > params;
	for ( const std::shared_ptr < Statement > & param : m_params ) {
		params.push_back ( param->translateAndEval ( prev, environment ) );
	}

	std::shared_ptr < abstraction::OperationAbstraction > algo = abstraction::Registry::getContainerAbstraction ( m_container, m_type->getType ( ) );

	int i = 0;
	ext::vector < std::shared_ptr < abstraction::Value > > casted_params;
	for ( const std::shared_ptr < abstraction::Value > & param : params ) {
		if ( abstraction::Registry::isCastNoOp ( algo->getParamType ( i ), param->getType ( ) ) ) {
			casted_params.push_back ( param );
		} else {
			casted_params.push_back ( abstraction::CastHelper::eval ( environment, param, algo->getParamType ( i ) ) );
		}
		++ i;
	}

	i = 0;
	for ( const std::shared_ptr < abstraction::Value > & param : casted_params ) {
		algo->attachInput ( param, i );
		++ i;
	}

	std::shared_ptr < abstraction::Value > res = algo->eval ( );
	if ( ! res )
		throw std::invalid_argument ( "Eval of algorithm " + m_container + " failed." );

	return res;
}

} /* namespace cli */
