/* $Id$
 * LookupTypes: find out the type of an AST node.
 *
 * Copyright (C) 2008-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 __LOOKUP_TYPES_HPP_INCLUDED
#define __LOOKUP_TYPES_HPP_INCLUDED

#include "frontend/visitor/NullVisitor.hpp"
#include "frontend/ast/AttributeDeclaration.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include <cassert>

namespace ast {

/** class to determine the type of a symbol, inline implementation.
 */
class LookupTypes : public NullVisitor {
public:
	//! c'tor
	/** @param doBaseLookups stop lookup at normal type or traverse to
	 *         base of the type?
	 *  @param doElementLookups also cross the boundary from Arrays to 
	 *         array elements?
	 */
	LookupTypes(
		bool doBaseLookups,
		bool doElementLookups
		) : 	declaration(NULL), 
			baseLookup(doBaseLookups),
			elementLookup(doElementLookups) {}

	/** determined declaration, NULL if no type is known */
	const TypeDeclaration *declaration;
private:
	/** The following SymbolDeclarations are "not for us".
	 */
	virtual void visit(Library &node) {}
	virtual void process(SeqStat &node) {}
	virtual void process(ConcurrentStat &node) {}
	virtual void process(Callable &node) {}
	virtual void process(LibUnit &node) {}

	/** Visit an UnconstrainedArrayType node.
	 *  @param node UnconstrainedArrayType node that get's visited.
	 */
	virtual void visit(UnconstrainedArrayType &node) {
		if (this->elementLookup) {
			assert(node.elementType != NULL);
			node.elementType->accept(*this);
		} else {
			this->process(node);
		}
	}

	/** visit a TypeDeclaration
	 *  @param node the TypeDeclaration itself.
	 */
	virtual void process(TypeDeclaration &node) {
		this->declaration = &node;
	}

	//! visit a RecordTypeElement
	/** @param node RecordTypeElement */
	virtual void visit(RecordTypeElement& node) {
		assert(node.subtype);

		if (this->baseLookup) {
			node.subtype->accept(*this);
		} else {
			this->declaration = node.subtype;
		}
	}

	//! process a ValDeclaration
	/** @param node ValDeclaration */
	virtual void process(ValDeclaration &node) {
		assert(node.subtypeIndic);

		if (this->baseLookup) {
			node.subtypeIndic->accept(*this);
		} else {
			this->declaration = node.subtypeIndic;
		}
	}

	/** visit a PhysicalTypeUnit.
	 *  SimpleNames on the right hand side can mean a physical 
	 *  literal w.o. number.
	 *  @param node PhysicalTypeUnit.
	 */
	virtual void visit(PhysicalTypeUnit &node) {
		// already at base type
		this->declaration = node.parent;
		assert(this->declaration != NULL);
	}

	/** visit a SubtypeIndication
	 *  @param node SubtypeIndication which is the type itself.
	 */
	virtual void visit(SubtypeIndication &node) {
		if (this->baseLookup) {
			// ugly const cast, nevermind though, since
			// the final const will be preserved.
			TypeDeclaration *t = 
				const_cast<TypeDeclaration*>(
							node.declaration);
							
			t->accept(*this);
		} else {
			this->declaration = &node;
		}
	}

	/** visit a FunctionDeclaration
	 *  @param node FunctionDeclaration, take result type.
	 */
	virtual void visit(FunctionDeclaration &node) {
		assert(node.returnType);

		if (this->baseLookup) {
			node.returnType->accept(*this);
		} else {
			this->declaration = node.returnType;
		}
	}

	virtual void visit(AttributeDeclaration &node) {
		assert(node.type != NULL);

		if (this->baseLookup) {
			TypeDeclaration *t = 
				const_cast<TypeDeclaration*>(node.type);
			t->accept(*this);
		} else {
			this->declaration = node.type;
		}
	}


	//! process a SymbolDeclaration
	/** @param node SymbolDeclaration */
	virtual void process(SymbolDeclaration &node) {
		// must not happen, coming from a non-type SymbolDecl.
		assert(false);
	}

	/** lookup the base type, or is a SubtypeIndication enough? */
	bool baseLookup;
	/** lookup spans to element types of Array types as well? */
	bool elementLookup;
};


}; /* namespace ast */

#endif /* __LOOKUP_TYPES_HPP_INCLUDED */
