#ifndef TYPEDEFTABLE_H
#define TYPEDEFTABLE_H

#include <map>
#include <list>
#include <string>
#include <stack>

class TypedefTable {
  public:
    enum kind_t { ID, TD, TG };
  private:
    struct Entry {
	int scope;
	kind_t kind;
    };
    
    struct DeferredEntry {
	std::string identifier;
	kind_t kind;
    };

    typedef std::map<std::string, std::list<Entry> > tableType;
    tableType table;

    int currentScope;
    std::string currentContext;
    int contextScope;
    
    typedef std::list< DeferredEntry > deferListType;
    std::stack< deferListType > deferListStack;
    std::map< std::string, deferListType > contexts;
    
    std::stack< std::string > nextIdentifiers;

    bool isKind( std::string identifier, kind_t kind ) const;
    void addToScope( const std::string &identifier, kind_t kind, int scope );
  public:
    TypedefTable();

    bool isIdentifier( std::string identifier ) const;
    bool isTypedef( std::string identifier ) const;
    bool isTypegen( std::string identifier ) const;
    
    // "addToCurrentScope" adds the identifier/type pair to the current scope This does less than you think it does,
    // since each declaration is within its own scope.  Mostly useful for type parameters.
    void addToCurrentScope( const std::string &identifier, kind_t kind );
    void addToCurrentScope( kind_t kind );		// use nextIdentifiers.top()

    // "addToEnclosingScope" adds the identifier/type pair to the scope that encloses the current one.  This is the
    // right way to handle type and typedef names
    void addToEnclosingScope( const std::string &identifier, kind_t kind );
    void addToEnclosingScope( kind_t kind );		// use nextIdentifiers.top()
    
    // "addToEnclosingScope2" adds the identifier/type pair to the scope that encloses the scope enclosing the the
    // current one.  This is the right way to handle assertion names
    void addToEnclosingScope2( const std::string &identifier, kind_t kind );
    void addToEnclosingScope2( kind_t kind );		// use nextIdentifiers.top()
    
    // set the next identifier to be used by an "add" operation without an identifier parameter within the current scope
    void setNextIdentifier( const std::string &identifier );
    
    // dump the definitions from a pre-defined context into the current scope
    void openContext( std::string contextName );
    
    void enterScope( void );
    void leaveScope( void );
    void enterContext( std::string contextName );
    void leaveContext( void );

    void print( void ) const;
};

#endif // TYPEDEFTABLE_H
