#include <vector>
#include <algorithm>

#include "Processor.h"
#include "SynTree/Declaration.h"

namespace Designators {
    Matcher::Matcher( const std::list< DeclarationWithType * > &decls ) {
	int cnt = 0;
	for ( std::list< DeclarationWithType * >::const_iterator i = decls.begin();
	     i != decls.end(); i++, cnt++ ) {
	    std::string newName = (*i)->get_name();
	    if ( table.find( newName ) == table.end() ) {
		table.insert( std::pair<std::string, int>(newName, cnt) );
		order.push_back( newName );
		declarations.push_back( *i );
		alternatives.push_back( 0 );
	    }
	}
    }

    template< class InputIterator >
    bool Matcher::add(InputIterator begin, InputIterator end, ResolvExpr::Alternative &alt ) {
	while ( begin != end ) {
	    if ( table.find( *begin ) != table.end() )
		alternatives[ table[ *begin ] ] = new ResolvExpr::Alternative(alt);
	    else
		return false;
	    begin++;
	}
	return true;
    }

    template< class InputIterator, class OutputIterator >
    bool Matcher::slice(InputIterator begin, InputIterator end, OutputIterator out ) {
	while ( begin != end )
	    if ( table.find( *begin ) != table.end() )
		*out++ = declarations [ table[ *begin++ ] ];
	    else
		return false; // throw 0;
	return true;
    }

    template< class OutputIterator >
    bool Matcher::get_reorderedCall( OutputIterator out ) {
	// fill call with defaults, if need be
	for (Matcher::iterator o = begin(); o != end(); o++ )
	    if ( alternatives[ table[ *o ] ] == 0 )
		return false;
	    else
		out++ = *alternatives[table[ *o ]];
	return true;
    }

    bool fixDesignations( ResolvExpr::AlternativeFinder &finder, Expression *designation ) {
	// Distribute `designation' over alternatives contained in `finder'
	if ( ! designation) return false;
	else
	    for ( ResolvExpr::AlternativeFinder::iterator alt = finder.begin(); alt != finder.end(); alt++ )
		alt->expr->set_argName( designation );
	return true;
    }

    template < class OutputIterator >
    bool extractNames( Expression *expr, OutputIterator out, Matcher matcher ) {
	Expression *designator = expr->get_argName();
	if ( designator == 0 ) return false;

	if ( NameExpr *ndes = dynamic_cast<NameExpr *>(designator) )
	    out++ = ndes->get_name();
	else if ( TupleExpr *tdes = dynamic_cast<TupleExpr *>(designator) ) {
	    std::cerr << "Tuple designation" << std::endl;
//      ObjectDecl *decl = extractTupleV(matcher, tdes); // xxx
	    // transform?
	    for ( std::list< Expression * >::iterator n = tdes->get_exprs().begin();
		 n != tdes->get_exprs().end(); n++ ) {

		if ( NameExpr *name = dynamic_cast<NameExpr *>(*n) )
		    out++ = name->get_name();
		else
		    // flatten nested Tuples
		    throw SemanticError( "Invalid tuple designation." );
	    }
	}
	return true;
    }

    std::string extractName( Expression *expr ) /* throw NoNameExtraction */ {
	if ( NameExpr *name = dynamic_cast< NameExpr *>(expr) )
	    return name->get_name();
	else /* if () */
	    throw 0;
    }

    DeclarationWithType *gensym( DeclarationWithType *, std::string prefix ) {
	return 0;
    }

    ObjectDecl *extractTupleV( Matcher matcher, TupleExpr *nmTuple ) {
	// extract a subtuple of the function `fun' argument list, corresponding to the tuple of names requested by
	// `nmTuple'.
	std::list< Expression * > &exprs = nmTuple->get_exprs();
	std::cerr << "In extractTupleV, the tuple has " << exprs.size() << " components." << std::endl;
	std::list< std::string > names;
	std::transform( exprs.begin(), exprs.end(), back_inserter(names), extractName );
	std::list< DeclarationWithType * > decls;
	matcher.slice( names.begin(), names.end(), back_inserter(decls) );
	//std::for_each( decls.begin(), decls.end(), gensym );
	std::cerr << "Returning declaration with " << decls.size() << " components." << std::endl;

	return 0;//new ObjectDecl()
    }

    void check_alternative( FunctionType *fun, ResolvExpr::AltList &args ) {
	using namespace ResolvExpr;

	Matcher matcher( fun->get_parameters() );
	for ( AltList::iterator a = args.begin(); a != args.end(); a++ ) {
	    std::list< std::string > actNames;
	    if ( ! extractNames( a->expr, back_inserter(actNames), matcher ) ) {
		return; // not a designated call, leave alternative alone
	    } else {
		// see if there's a match
		matcher.add( actNames.begin(), actNames.end(), *a );
	    }
	}
	//AltList newArgs;
	args.clear();
	matcher.get_reorderedCall( back_inserter(args) );

	return;
    }
#if 0
    void pruneAlternatives( Expression *expr, ResolvExpr::AlternativeFinder &finder ) {
	if ( expr->get_argName() != 0 ) {
	    // Looking at designated expression
	    using namespace ResolvExpr;
	    AltList &alternatives = finder.get_alternatives();
	    std::cerr << "Now printing alternatives: " << std::endl;
	    for ( AltList::iterator a = alternatives.begin(); a != alternatives.end(); a++ )
		a->expr->print( std::cerr );
	    //std::cerr << "Looking for constructions of length no more than: " << tdes->get_exprs().size() << "." << std::endl;
	}
	return;
    }
#endif // 0
} // namespaces Designators
