#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;
  }

  /*
  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;
  }
  */
} // namespaces Designators
