#ifndef _INITTWEAK_MODEL_H_
#define _INITTWEAK_MODEL_H_

#include "Association.h"
#include "SemanticError.h"
#include "SynTree/Visitor.h"

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

namespace InitTweak {

  class InitModelBuilder : public AssociationBuilder, public Visitor {
  public:
    InitModelBuilder( Declaration * );
    ~InitModelBuilder();

    virtual Association *grab_assoc() { taken = true; return building; }
    virtual Association *get_assoc() { return building; }
    void set_assoc( Association *newAssoc ) { building = newAssoc; }

    void init();
    static int interpretDimension( Expression *exp ) {
      ConstantFolder folder( exp );
      try {
	return folder.get_constant();
      } catch (...) {
	throw SemanticError("Invalid array dimension");
      }
    }

    // types
    virtual void visit( ArrayType * );
    virtual void visit( StructInstType * );
    virtual void visit( UnionInstType * );
    virtual void visit( EnumInstType * );
    virtual void visit( ContextInstType * ) { throw 0; }
    virtual void visit( TypeInstType * )    { throw 0; }
    // virtual void visit( TupleType *tupleType );
    // declarations
    virtual void visit( StructDecl *);
    virtual void visit( UnionDecl *);
    virtual void visit( EnumDecl *);

  private:
    class ConstantFolder : public Visitor {
    public:
      ConstantFolder( Expression *_expr = 0 ): expr(_expr) {}
      int get_constant() throw() { expr->accept( *this ); return value; }
      void set_constant( Expression *newExp ) { expr = newExp; }
      // Visitor interface
      void visit( Expression * ) { throw 0; }
      void visit( NameExpr * ) { throw 0; }
      void visit( CastExpr * ) { throw 0; }
      void visit( UntypedMemberExpr * ) { throw 0; }
      void visit( VariableExpr * ) { throw 0; }
      void visit( ConstantExpr * );
      void visit( SizeofExpr * ) { throw 0; }
      void visit( AttrExpr * ) { throw 0; }
      void visit( LogicalExpr * ) { throw 0; }
      void visit( ConditionalExpr * ) { throw 0; }
      void visit( CommaExpr * ) { throw 0; }
    private:
      Expression *expr;
      int value;
    };

    bool taken;
    Declaration *decl;  // ?
    Association *building;
  };

  class InitModelFiller : public AssociationFiller, public Visitor {
  public:
    InitModelFiller( Association *, Initializer *, bool _topLevel = false );
    ~InitModelFiller() { /* pointers in here are not owned by object (never created by object either) */ }
    virtual Association *get_assoc() { return model; }
    virtual void set_assoc( Association *newAssoc ) { model = newAssoc; }

    void init();
    // Visitor interface
    virtual void visit( MemberInit *memberInit ) { throw 0; }
    virtual void visit( ElementInit *elementInit ) { throw 0; }
    virtual void visit( SingleInit *singleInit );
    virtual void visit( ListInit *listInit );

  private:
    Association *model;
    Initializer *orgInit;
    bool topLevel;
    long int next;
  };

  class InitUnspooler : public AssociationVisitor {
  public:
    InitUnspooler() : init(0), taken( false ) {}
    virtual ~InitUnspooler() { if (!taken && (init != 0)) { delete init; init = 0; } }
    Initializer *get_initializer() { return init; }
    Initializer *grab_initializer() { taken = true; return init; }

    virtual void visit( SingleName * );
    virtual void visit( PointAssociation * );
    virtual void visit( RangeAssociation * ) { std::cerr << "InitUnspooler - In a range assoc" << std::endl; return; }

  private:
    Initializer *init;
    bool taken;
  };

} // namespace InitTweak

#endif // #define _INITTWEAK_MODEL_H_

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