//
// 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.
//
// Association.h -- 
//
// Author           : Rodolfo G. Esteves
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue May 19 16:27:09 2015
// Update Count     : 2
//

#ifndef _ASSOCIATION_H_
#define _ASSOCIATION_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 );
		} // if
		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 );
		} // if

		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) );
		} // if
		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;
		} // for
	}

	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 // _ASSOCIATION_H_

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