#ifndef _BASINIT_H_
#define _BASINIT_H_

#include <list>

#include "SynTree/Visitor.h"
#include "SymTab/Indexer.h"

#include "SynTree/Type.h"
#include "SynTree/Initializer.h"
#include "SynTree/Expression.h"
#include "NameInCollection.h"
#include "NameAssociation.h"

namespace InitTweak {

  bool isDeclStmtP(Statement *stmt);

  class BreakInitializer;
  class BreakDesignator;

  class BasicInit: public Mutator
    {
    public:
      BasicInit() : bindings( 0 ) {}
      BasicInit( SymTab::Indexer &_index ) : bindings( 0 ), index( _index ) {}
      BasicInit( const BasicInit &other ) {
	bindings = other.get_bindings();
	index = other.index;
      }

      ~BasicInit() { /* delete bindings; bindings = 0; */ }

      NameAssociation< Expression *, BreakInitializer > *get_bindings() const { return bindings; }
      void set_bindings( NameAssociation< Expression *, BreakInitializer > *newValue ) {
	bindings = newValue;
      }

      bool has_bindings() {
	return ( get_bindings() != 0 || ! stmts.empty() );
      }

      virtual ObjectDecl     *mutate( ObjectDecl *objectDecl )
      { index.visit( objectDecl ); return objectDecl; }
      virtual TypeDecl       *mutate( TypeDecl *typeDecl )
      { index.visit( typeDecl ); return typeDecl; }
      virtual TypedefDecl    *mutate( TypedefDecl *typeDecl )
      { index.visit( typeDecl ); return typeDecl; }
      virtual StructDecl     *mutate( StructDecl *aggregateDecl )
      { index.visit( aggregateDecl ); return aggregateDecl; }
      virtual UnionDecl      *mutate( UnionDecl *aggregateDecl )
      { index.visit( aggregateDecl ); return aggregateDecl; }
      virtual EnumDecl       *mutate( EnumDecl *aggregateDecl )
      { index.visit( aggregateDecl ); return aggregateDecl; }

      virtual Type           *mutate( StructInstType *aggrInst )
      { index.visit( aggrInst ); return aggrInst; }
      virtual Type           *mutate( UnionInstType *aggrInst )
      { index.visit( aggrInst ); return aggrInst; }

      virtual CompoundStmt   *mutate(CompoundStmt *compoundStmt);
      virtual Statement *mutate(DeclStmt *declStmt);

      std::list< Statement *> get_statements() const { return stmts;  }

      static void build_statements( NameAssociation< Expression *, BreakInitializer > *assoc,
				    std::string aggName, std::list< Statement *> &stmts );
    private:
      NameAssociation< Expression *, BreakInitializer > *bindings;
      Statement *assignFromDecl( DeclStmt *declStmt );
      SymTab::Indexer index;
      std::list< Statement *> stmts;

      class Classify {
      public:
	enum TypeKind { NULL_T, SINGLE_T, COMPOUND_T };
	enum InitKind { NULL_I, SINGLE_I, COMPOUND_I };

	static TypeKind type( Type * );
	static InitKind initializer( Initializer *);

	static NameInCollection *declaration( ObjectDecl *objdecl, SymTab::Indexer *index );
	static std::list< Statement * >
	   matchInit( NameInCollection *, ObjectDecl *, Initializer * );
	static Statement *constructAssgn( std::string membname, ObjectDecl *toInit, SingleInit *sinit );

	// static std::list< Statement * > constructListAssgn( NameAssociation<Expression *, BreakDesignator > assoc );
      };
  };

  class BreakInitializer {
    enum InitKind { EMPTY, SINGLE, COMPOUND };

    class BreakDesignator;
    typedef BreakDesignator NameSplitter;

  public:
    typedef std::list<Initializer *>::iterator element_iterator;
    typedef std::list< NameSplitter >::iterator name_iterator;

    BreakInitializer ( Initializer *_init ) : kind( EMPTY ), sinit(0), cinit(0) {
      std::list<Expression *> temp;

      if( ( sinit=dynamic_cast< SingleInit * >(_init) ) != 0 ) {
	kind = SINGLE;
	temp = sinit->get_designators();
      } else if ( ( cinit=dynamic_cast< ListInit * >(_init) ) != 0 ) {
	kind = COMPOUND;
	temp = cinit->get_designators();
      }

      std::transform( temp.begin(), temp.end(), std::back_inserter( designators ),
		      ctor_noptr<NameSplitter, Expression *> );

    }

    //BreakInitializer( const BreakInitializer &other ) { this.col = other.col; }
    ~BreakInitializer () {}

    BreakInitializer set_name( NameSplitter &name ) {
      designators.clear();
      designators.push_back( name );

      return *this;
    }

    element_iterator element_begin() {
      assert( cinit != 0 );
      return cinit->begin_initializers();
    }
    element_iterator element_end() {
      assert( cinit != 0 );
      return cinit->end_initializers();
    }

    name_iterator names_begin() { return designators.begin(); }
    name_iterator names_end() { return designators.end(); }

    int names_size() const { return designators.size(); }

    bool has_index() const { return ! designators.empty(); }
    bool is_single() const { return kind == SINGLE; }
    bool is_composite() const { return kind == COMPOUND;  }

    Expression *get_value() {
      switch ( kind ) {
      case EMPTY:
	return 0;
	break;
      case SINGLE:
	return sinit->get_value();
	break;
      case COMPOUND:
	assert(false);
	break;
      default:
	assert(false);
      }
      return 0;
    }

    // attributes
  private:
    InitKind kind;
    SingleInit *sinit;
    ListInit *cinit;
    std::list< BreakDesignator > designators;

    // helper classes
  public:
    class BreakDesignator {
    public:
      BreakDesignator( Expression *exp ) {
	Expression *prfx = exp;
	UntypedMemberExpr *me = 0;

	do {
	  if ( (me=dynamic_cast< UntypedMemberExpr * >( prfx )) == 0 ) break;
	  blown_struct.push_front( me->get_member() );
	  prfx = me->get_aggregate();
	} while( prfx != 0 );

	NameExpr *ne;
	if ( (ne=dynamic_cast< NameExpr * >( prfx )) != 0 ) 
	  blown_struct.push_front( ne->get_name() );
      }

      BreakDesignator( std::string name ) {
	blown_struct.push_front( name );
      }

      bool is_flat() const { return blown_struct.size() == 1; }
      bool is_nested() const { return blown_struct.size() > 1; }

      std::string get_name() { return blown_struct.front(); }

      BreakDesignator &name_remainder() {
	blown_struct.pop_front();
	return *this;
      }

    private:
      std::list< std::string > blown_struct;
    };

  };

} // namespace InitTweak

#endif // #ifndef _BASINIT_H_

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