//
// 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.
//
// FunctionFixer.cc --
//
// Author           : Rodolfo G. Esteves
// Created On       : Sat May 16 08:12:38 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Thu May 21 21:11:45 2015
// Update Count     : 7
//

#include <list>
#include <vector>
#include <cassert>
#include <algorithm>

#include "FunctionFixer.h"
#include "SynTree/Expression.h"
#include "SynTree/Declaration.h"
#include "SynTree/Type.h"

namespace ArgTweak {
	FunctionFixer::FunctionFixer( SymTab::Indexer *ind ) : index( ind ) {
		if ( index == 0 ) index = new SymTab::Indexer();
	}

	FunctionFixer::~FunctionFixer() {
		delete index;
	}

	DeclarationWithType *FunctionFixer::mutate( FunctionDecl *functionDecl ) {
		index->visit( functionDecl );
		/* check for duplicate named parameters here?  It might not be an error if they're never used, on the other hand, it
		   might be to costly to check for duplicates every time we try a match */
		return Parent::mutate( functionDecl );
	}

	Expression *FunctionFixer::mutate( UntypedExpr *untypedExpr ) throw ( SemanticError ) {
		assert( untypedExpr != 0 );

		if ( NameExpr * function = dynamic_cast< NameExpr *>(untypedExpr->get_function() ) ) {
			std::list < DeclarationWithType * > options;
			index->lookupId ( function->get_name(), options );
			for ( std::list < DeclarationWithType * >::iterator i = options.begin(); i != options.end(); i++ ) {
				if ( FunctionType * f = dynamic_cast< FunctionType * > ( (*i)->get_type() ) )	{
					std::list < DeclarationWithType * > &pars = f->get_parameters();
					bool candidateExists ;
					for ( std::list < DeclarationWithType * >::iterator p = pars.begin(); p != pars.end(); p++ ) {
						if ( ( candidateExists = align( f->get_parameters(), untypedExpr->get_args(), Matcher() ) ) ) break;
					}
					if ( ! candidateExists ) throw SemanticError("Error in function call");
				} // if
			} // for
		} // if
		return untypedExpr;
	}

	template < class L1, class L2, class Helper >
	bool align( L1 &pattern, L2 &possible_permutation, Helper help ) {
		std::map < typename Helper::key, int > positions;
		int p = 0;

		for ( typename L1::iterator i = pattern.begin(); i != pattern.end(); i++, p++ )
			if ( help.extract_key( *i ) != Helper::null_key )
				positions[ help.extract_key( *i ) ] = p;

		L2 copy_pp( possible_permutation );

		std::vector< typename L2::value_type > temp(copy_pp.size(), Helper::null_value );
		for ( typename L2::iterator i = copy_pp.begin(); i != copy_pp.end(); i++ )
			if ( positions.find( help.extract_key( *i ) ) != positions.end() ) {
				temp [ positions [ help.extract_key( *i ) ] ] = *i;
				*i = Helper::null_value;
			} // if

		// rest of the arguments
		int a = 0;
		bool goAhead = true;							// go ahead and change the list
		for ( typename L2::iterator i = copy_pp.begin(); i != copy_pp.end(); i++, a++ ) {
			if (  *i != Helper::null_value )
				if ( temp[a] == Helper::null_value )
					temp[a] = *i;
				else
					{ goAhead = false; /* there's something there already */; break; }
			else
				if ( temp[a] == Helper::null_value )
					{ goAhead = false; /* can't leave empty spaces */ break; }
				else
					;									// all good, this was filled during the first pass
			assert ( temp[a] != Helper::null_value );
		} // for

		// Change the original list
		if ( goAhead ) std::copy( temp.begin(), temp.end(), possible_permutation.begin() );

		return goAhead;
	}

	std::string FunctionFixer::Matcher::null_key("");
	Expression *FunctionFixer::Matcher::null_value = 0;

	std::string FunctionFixer::Matcher::extract_key ( DeclarationWithType *decl ) {
		return decl->get_name();
	}

	std::string FunctionFixer::Matcher::extract_key ( Expression *expression ) {
		if ( expression->get_argName() == 0 )
			return std::string("");
		else
			return *(expression->get_argName());
	}
} // namespace ArgTweak

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