#ifndef _TUPLE_ASSIGNMENT_H_
#define _TUPLE_ASSIGNMENT_H_

#include <string>
#include <vector>
#include "ResolvExpr/AlternativeFinder.h"

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

namespace Tuples {
  class TupleAssignSpotter {
  public:
    // dispatcher for Tuple (multiple and mass) assignment operations
    TupleAssignSpotter( ResolvExpr::AlternativeFinder * );
    ~TupleAssignSpotter() { delete matcher; matcher = 0; }

    bool pointsToTuple( Expression * );
    static bool isTupleVar( DeclarationWithType * );
    bool isTuple( Expression *, bool isRight = false );
    bool isMVR( Expression * );
    bool isTupleAssignment( UntypedExpr *, std::list<ResolvExpr::AltList> & );
    bool match();

  private:
    // records for assignment generation
    class Options {
    public:
      void add_option( ResolvExpr::AltList &opt );
      std::list< ResolvExpr::AltList > get_best();
      void print( std::ostream & );
      int size() const { return options.size(); }
      ResolvExpr::AltList get_option( std::list< ResolvExpr::AltList >::size_type index );

      // should really use the one in ResolvExpr/AlternativeFinder, but it's too coupled with the object
      template< typename InputIterator, typename OutputIterator >
      void findMinCost( InputIterator begin, InputIterator end, OutputIterator out );

      template< typename InputIterator, typename OutputIterator >
      void lift_intersection( InputIterator begin, InputIterator end, OutputIterator out );
    private:
      std::list< ResolvExpr::AltList > options;
      std::vector< std::vector< ResolvExpr::Cost > > costMatrix;
    };

    class Matcher {
    public:
      Matcher( /*TupleAssignSpotter &spot, */Expression *_lhs, Expression *_rhs );
      virtual ~Matcher() {}
      virtual bool match( std::list< Expression * > &out ) = 0;
      virtual bool solve( std::list< Expression * > &assigns ) = 0;
      static UntypedExpr *createAssgn( Expression *left, Expression *right );
    protected:
      Matcher() /*: own_spotter( TupleAssignSpotter(0) ) */{}
      void init(/* TupleAssignSpotter &, */Expression *_lhs, Expression *_rhs );
      std::list< Expression * > lhs, rhs;
      //TupleAssignSpotter &own_spotter;
    };

    class MassAssignMatcher : public Matcher {
    public:
      MassAssignMatcher( Expression *_lhs, Expression *_rhs ) : Matcher( _lhs, _rhs ) {
	rhs.push_back( _rhs );
      }
      virtual bool match( std::list< Expression * > &out );
      virtual bool solve( std::list< Expression * > &assigns );
    private:
      //std::vector< ResolvExpr::AltList > optMass;
    };

    class MultipleAssignMatcher : public Matcher {
    public:
      MultipleAssignMatcher( Expression *_lhs, Expression *_rhs );
      virtual bool match( std::list< Expression * > &out );
      virtual bool solve( std::list< Expression * > &assigns );
    private:
      //Options options;
    };

    friend class Matcher;

    ResolvExpr::AlternativeFinder *currentFinder;
    //std::list<Expression *> rhs, lhs;
    Expression *rhs, *lhs;
    Matcher *matcher;
    bool hasMatched;
    Options options;
    std::vector< ResolvExpr::AltList > optMass;
  };


  ResolvExpr::Cost extract_cost( ResolvExpr::Alternative & );

  template< typename InputIterator, typename OutputIterator >
  void
  findMinCostAlt( InputIterator begin, InputIterator end, OutputIterator out )
  {
    using namespace ResolvExpr;
    AltList alternatives;

    // select the alternatives that have the minimum parameter cost
    Cost minCost = Cost::infinity;
    for( AltList::iterator i = begin; i != end; ++i ) {
      if( i->cost < minCost ) {
        minCost = i->cost;
        i->cost = i->cvtCost;
        alternatives.clear();
        alternatives.push_back( *i );
      } else if( i->cost == minCost ) {
        i->cost = i->cvtCost;
        alternatives.push_back( *i );
      }
    }
    std::copy( alternatives.begin(), alternatives.end(), out );
  }

} // namespace Tuples


#endif // #ifndef _TUPLE_ASSIGNMENT_H_

/*
  Local Variables:
  mode: c++
  End:
*/
