#ifndef _ASSOCIATE_H_
#define _ASSOCIATE_H_

#include <map>
#include <list>
#include <string>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <algorithm>

#include "SynTree/Expression.h"
#include "diet_map.h"

class Association;
class SingleName;
class PointAssociation;
class RangeAssociation;

// ** exceptions
class AssocException : public std::exception {
public:
  AssocException() {}
  AssocException( std::string _what ) : what( _what ) {}
  ~AssocException() throw () {}

  std::string get_what() const { return what; }
  void set_what( std::string newValue ) { what = newValue; }
private:
  std::string what;
};

// ** visitors
class AssociationVisitor {
public:
  AssociationVisitor() {}
  virtual ~AssociationVisitor() {}

  virtual void visit( SingleName * ) = 0;
  virtual void visit( PointAssociation * ) = 0;
  virtual void visit( RangeAssociation * ) = 0;
};

// ** containers
class Association {
 public:
  virtual ~Association();

  virtual Association *clone() = 0;
  virtual long int add_single( std::string, Expression *) = 0;
  virtual long int add_single( long int, Expression *expr) = 0;

  virtual Association *operator[]( int idx ) = 0;
  virtual Association *operator[]( std::string ) = 0;

  //  virtual AssociationIterator *get_iterator() = 0;

  virtual void accept( AssociationVisitor & ) = 0;
  virtual void display( std::ostream & ) = 0;
};

class SingleName : public Association {
public:
  SingleName( Expression *initExpr = 0 ) : expr( initExpr ) {}
  virtual ~SingleName();

  virtual SingleName *clone() {
    return 0; // XXX!
  }

  virtual long int add_single( long int idx, Expression *newExpr) {
    if( expr == 0 ) //|| *expr == *newExpr )
      expr = newExpr;
    return 0;
  }

  virtual long int add_single( std::string str, Expression *newExpr) {
    if( expr == 0 ) //|| *expr == *newExpr )
      expr = newExpr;
    return 0;
  }

  virtual Association *operator[]( int idx ) { assert(false); }
  virtual Association *operator[]( std::string idx ) { assert(false); }

  virtual void accept( AssociationVisitor &v ) { v.visit( this ); }
  virtual void display( std::ostream &os ) {
    os << "Single association" << std::endl;
  }

  Expression *get_expr() const { return expr; }

private:
  Expression *expr;
  Expression *deflt;
};

class PointAssociation : public Association {
public:
  typedef std::map< std::string, std::pair< long int, Association *> > map_type;

  PointAssociation() {}
  PointAssociation( const PointAssociation &other ) {
    copy( other.anonym.begin(), other.anonym.end(), back_inserter( anonym ));
  }

  virtual ~PointAssociation();

  virtual PointAssociation *clone() {
    return ( new PointAssociation( *this ) );
  }

  virtual long int add_single( long int idx, Expression *expr) {
    long int ret;

    if( idx >= (long int)ordering.size() ) throw AssocException("extra (spurious) members");

    if ( ordering[ idx ] == "")
      std::cerr << "Checkpoint 2" << std::endl;
    else {
      assert( table[ordering[idx]].second != 0 );
      ret = idx;
      table[ ordering[idx] ].second->add_single("", expr );
    }
    return ret;
  }

  virtual long int add_single( std::string idx, Expression *expr) {
    if( idx == "" )
      std::cerr << "Checkpoint 1" << std::endl;
    else {
      map_type::iterator j;
      if (  (j = table.find( idx )) == table.end() )  // this doesn't amount for reachable members deeper down the structure, fix
	throw AssocException("No such member");
      else
	return add_single( j->second.first, expr );
    }

    return -1;
  }

  void add_member( std::string str ) {
    if ( table.find( str ) != table.end() ) return;
    ordering.push_back( str );
    if( str != "" ) {
      std::pair<long int, Association *> p( ordering.size() - 1, 0 );
      table.insert( std::pair< std::string, std::pair<long int, Association *> >(str, p) );
    }
    return;
  }

  virtual void set_member( std::string str, Association *assoc ) {
    if( str == "" )
      anonym.push_back( assoc );
    else  if ( table.find( str ) == table.end() )
      throw AssocException( "no such member" );
    else
      table[ str ] = std::pair<long int, Association *>(ordering.size() - 1, assoc);

    return;
  }

  virtual Association *operator[]( int idx ) {
    if( ordering[idx] == "" ) {
      std::cerr << "Error, anonymous members not implemented yet" << std::endl;
      throw 0;
    } else
      return table[ ordering[idx] ].second;
  }

  virtual Association *operator[]( std::string idx ) {
    if ( table.find( idx ) == table.end() )
      throw AssocException("Member not found");
    else
      return table[ idx ].second;
  }

  /*
  virtual AssociationIterator *get_iterator() {
    PointAssocIterator *it;
    return it;
  }
  */

  void accept( AssociationVisitor &v ) { v.visit( this ); }

  virtual void display( std::ostream &os ) {
    os << "Point association: " << std::endl;
    for( map_type::iterator i = table.begin(); i != table.end(); i++ ) {
      os << "Member [" << i->first << ", index = " << i->second.first << "]";
      if ( i->second.second != 0 )
	i->second.second->display( os );
      else
	std::cerr << "No recursive association" << std::endl;

      os << std::endl;
    }
  }

  const int size() const { return ordering.size(); }

private:
  PointAssociation &operator=(const PointAssociation &);
  std::vector<std::string> ordering;
  std::list< Association * > anonym;
  std::map< std::string, std::pair<long int, Association *> > table;
};

class RangeAssociation : public Association {
public:
  static const int UNDEF;
  RangeAssociation( int _hi= UNDEF ) : hi( _hi ) {
    std::cerr << "Constructed RangeAssociation with: [" << hi << "]" << std::endl;
  }

  virtual ~RangeAssociation();

  virtual RangeAssociation *clone() {
    return 0; // XXX !!!!
  }

  virtual Association *operator[]( int idx ) {
    return 0; // XXX !!!
  }

  virtual Association *operator[]( std::string idx ) { assert(false); return 0; }

  /*
  virtual AssociationIterator *get_iterator() {
    RangeAssocIterator *it;
    return it;
  }
  */

  virtual long int add_single( long int idx, Expression *newExpr) { return 0; }
  virtual long int add_single( std::string, Expression *) { return 0; }
  void accept( AssociationVisitor &v ) { v.visit( this ); }
  virtual void display( std::ostream &os ) {
    os << "Range association, with limit: " << std::endl;
  }

private:
  int hi;
  diet::diet_tree<int> tree;
  /*
  for( diet_tree<int>::iterator i = tree.begin(); i != tree.end(); i++ )
    std::cout << "--(" << (*i).first << ", " << (*i).second << ")--" << std::endl;
  diet_tree<int> tree;
  tree.insert(100,200);
  */
};

// ** builders
class AssociationBuilder {
public:
  /* AssociationBuilder( Declaration * ) */
  virtual ~AssociationBuilder() {}
  virtual Association *get_assoc() = 0;
  virtual Association *grab_assoc() = 0;
  virtual void set_assoc(   Association * ) = 0;
};

class AssociationFiller {
public:
  // AssociationFiller( Declaration * ) {}
  virtual ~AssociationFiller() {}
  virtual Association *get_assoc() = 0;
  virtual void set_assoc( Association * ) = 0;
};

#endif //#define _ASSOCIATE_H_

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