/* $Id$ 
 *
 * SymbolTable: Raw storage facility for symbols.
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#ifndef __SYMBOL_TABLE_HPP_INCLUDED
#define __SYMBOL_TABLE_HPP_INCLUDED

#include <stack>
#include <map>
#include "frontend/ast/SymbolDeclaration.hpp"
#include "frontend/ast/Library.hpp"
#include "frontend/misc/Symbol.hpp"
#include "frontend/misc/DeclarativeRegion.hpp"

namespace ast {

//! manage symbols.
class SymbolTable {
public:
	/** c'tor. */
	SymbolTable();

	/** d'tor */
	virtual ~SymbolTable();

	/** register a symbol with name name and type type in the current
	 *  DeclarativeRegion. This method will indirectly report an error
	 *  via the associated DeclarativeRegion method.
	 *
	 *  @param type Type of the symbol.
	 *  @param decl reference to the actually declared symbol.
	 *  @return registered Symbol.
	 */
	Symbol* 
	registerSymbol(
		enum symType type,
		SymbolDeclaration &decl);

	/** register a symbol in the current region, starting a new
	 *  declarative Region with the Symbol. Use-case: Entity, Package, ...
	 *
	 *  @param type Type of the Symbol.
	 *  @param decl reference to the actually declared symbol.
	 */
	void registerSymbolWithRegion(
		enum symType type,
		SymbolDeclaration &decl);

	/** create a new region and push it onto the stack. should be
	 *  called when entering a nested declarative region.
	 *
	 */
	virtual void pushNewRegion(void);

	/** If the library doesn't appear yet in the library list, 
	 *  create a new declarative region for given library and push it
	 *  on the stack. Otherwise fetch the region of the library and
	 *  push this on the stack.
	 *  Register given library in the library list if not yet present.
	 *
	 *  @param node Library that should get entered.
	 */
	void pushLibrary(Library &node);

	/** push the DeclarativeRegion region on the stack.
	 *  @param region DeclarativeRegion that should get pushed on the
	 *         stack.
	 */
	void pushRegion(DeclarativeRegion &region);

	/** remove the current region from the stack and make the next
	 *  outer region the current one. Should be called when leaving
	 *  a declarative region.
	 */
	void popRegion(void);

	/** lookup symbol by name. Stack mustn't be empty for this.
	 *  @param name search for this name.
	 *  @return visible candidates for given symbol.
	 */
	std::list<Symbol*> lookup(const std::string &name) const;

	/** add given symbol to imported symbols of current region, 
	 *  in case it's not in there yet.
	 *
	 *  @param sym symbol to import.
	 */
	void importSymbol(Symbol &sym);

	/** import all symbols from region
	 *  @param region DeclarativeRegion from which all declarations
	 *         should get imported.
	 */
	void importSymbols(const DeclarativeRegion &region);

	/** add library with name name to the currently imported symbols,
	 *  register an error if library cannot be found.
	 *
	 *  @param name name of the library.
	 *  @return true on success, false if library was not found.
	 */
	bool addlibrary(const std::string &name);

	/** register the symbol decl as the symbol to which the current
	 *  opened declarative region is attached to. The current region
	 *  should be an anonymous one.
	 *  
	 *  @param type type of the symbol
	 *  @param decl symbol declaration.
	 */
	void lateRegisterAttach(enum symType type, SymbolDeclaration &decl);

	/** region stack needs to have at least two entries. Rebind 
	 *  top-1 to have top as parent. Flip top and top-1 on the stack.
	 */
	void flipStack(void);

	/** find out about the current declarative region.
	 *  @return current declarative region or NULL.
	 */
	DeclarativeRegion* getCurrentRegion(void) const;

	/** return a type of std.standard
	 *  @param name of the type declaration that should be looked up in 
	 *         std.standard.
	 *  @return fitting type declaration or NULL if there is no such 
	 *          declaration.
	 */
	TypeDeclaration *getStdStandardType(const char *name);

	/** determine the current path name, mangled so that delimiters are 
	 *  two underscores.
	 *  @return current path name.
	 *
	 *  TODO dummy implementation atm, which will just return a 
	 *       unique name.
	 */
	std::string getMangledPathName(void) const;

	/** push the library std on the stack, then the package standard. */
	void pushStdStandard(void);

	/** Obtain the hidden region in which builtin attributes reside.
	 *  @return hidden region in which builtin attributes reside.
	 */
	DeclarativeRegion *
	getAttributeRegion(void) const 
	{
		return this->attributeRegion;
	}

protected:
	/** stack of current open regions, top (last) is current region. */
	std::stack<DeclarativeRegion*> regionStack;

	/** region in which attributes reside */
	DeclarativeRegion *attributeRegion;

private:
	/** list of known libraries (Symbols referring to libraries) */
	std::list<Symbol*> libraries;

	/** typedef for type mappings */
	typedef std::map<const char*, TypeDeclaration*> typeMap;

	/** chache for type lookups in std.standard. */
	typeMap stdStandardTypes;

};

}; /* namespace ast */

#endif /* __SYMBOL_TABLE_HPP_INCLUDED */
