//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// CandidateFinder.cpp --
//
// Author           : Aaron B. Moss
// Created On       : Wed Jun 5 14:30:00 2019
// Last Modified By : Aaron B. Moss
// Last Modified On : Wed Jun 5 14:30:00 2019
// Update Count     : 1
//

#include "CandidateFinder.hpp"

#include <sstream>

#include "Candidate.hpp"
#include "CompilationState.h"
#include "SatisfyAssertions.hpp"
#include "AST/Expr.hpp"
#include "AST/Node.hpp"
#include "AST/Pass.hpp"

#define PRINT( text ) if ( resolvep ) { text }

namespace ResolvExpr {

namespace {

	/// Actually visits expressions to find their candidate interpretations
	struct Finder {
		CandidateFinder & candFinder;
		const ast::SymbolTable & symtab;
		CandidateList & candidates;
		const ast::TypeEnvironment & tenv;
		ast::ptr< ast::Type > & targetType;

		Finder( CandidateFinder & f )
		: candFinder( f ), symtab( f.symtab ), candidates( f.candidates ), tenv( f.env ), 
		  targetType( f.targetType ) {}
		
		#warning unimplemented
	};

	/// Prunes a list of candidates down to those that have the minimum conversion cost for a given 
	/// return type. Skips ambiguous candidates.
	CandidateList pruneCandidates( CandidateList & candidates ) {
		#warning unimplemented
		(void)candidates;
		assert(false);
		return {};
	}

} // anonymous namespace

void CandidateFinder::find( const ast::Expr * expr, ResolvMode mode ) {
	// Find alternatives for expression
	ast::Pass<Finder> finder{ *this };
	expr->accept( finder );

	if ( mode.failFast && candidates.empty() ) {
		SemanticError( expr, "No reasonable alternatives for expression " );
	}

	if ( mode.satisfyAssns || mode.prune ) {
		// trim candidates to just those where the assertions are satisfiable
		// - necessary pre-requisite to pruning
		CandidateList satisfied;
		std::vector< std::string > errors;
		for ( auto & candidate : candidates ) {
			satisfyAssertions( *candidate, symtab, satisfied, errors );
		}

		// fail early if none such
		if ( mode.failFast && satisfied.empty() ) {
			std::ostringstream stream;
			stream << "No alternatives with satisfiable assertions for " << expr << "\n";
			for ( const auto& err : errors ) {
				stream << err;
			}
			SemanticError( expr->location, stream.str() );
		}

		// reset candidates
		candidates = std::move( satisfied );
	}

	if ( mode.prune ) {
		// trim candidates to single best one
		auto oldsize = candidates.size();
		PRINT(
			std::cerr << "alternatives before prune:" << std::endl;
			print( std::cerr, candidates );
		)

		CandidateList pruned = pruneCandidates( candidates );
		if ( mode.failFast && pruned.empty() ) {
			std::ostringstream stream;
			CandidateList winners;
			
			#warning unimplemented
			assert(false);
		}
	}

	#warning unimplemented
	assert(false);
}

std::vector< CandidateFinder > CandidateFinder::findSubExprs( 
	const std::vector< ast::ptr< ast::Expr > > & xs 
) {
	std::vector< CandidateFinder > out;

	for ( const auto & x : xs ) {
		out.emplace_back( symtab, env );
		out.back().find( x, ResolvMode::withAdjustment() );
		
		PRINT(
			std::cerr << "findSubExprs" << std::endl;
			print( std::cerr, out.back().candidates );
		)
	}

	return out;
}

} // namespace ResolvExpr

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
