/* $Id$
 *
 * Generate intermediate code, related to type handling.
 *
 * 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 __GC_TYPES_HPP_INCLUDED
#define __GC_TYPES_HPP_INCLUDED

#include <list>
#include <string>
#include "frontend/visitor/NullVisitor.hpp"
#include "intermediate/container/TypeElement.hpp"

namespace ast {

//! intermediate code type handling. TODO
/** type handling in the intermediate code:
 *  
 * cases:
 * 1) int/real: universal_integer | universal_real
 * 2) array of int/real: universal_*[card - 1]
 * 3) record: universal_*, array[card - 1], record
 * 4) array of record: record[card - 1]
 *
 * The intermediate code will only allow the definition of record 
 * types and constraint array types.
 */

class GCTypes {
public:
	//! calculate the upper bound of an array (if any)
	/** @param indices list of index constraints.
	 *  @return upper bound of the array (0-based), or 1 in case 
	 * 	    no indexConstraint is present
	 */
	static universal_integer 
	calcArrayBound(const std::list<DiscreteRange*> &indices);


	//! generate type elements of composite types.
	/** This class can be used, to generate type elements for composite
	 *  types.
	 */
	class GenTypeElements : public NullVisitor {
	public:
		//! c'tor
		/** @param addInitializer add an initial value.
		 *  @param uErrMsg error message, in case an unconstraint
		 *         array is found and no indexConstraint was 
		 *         specified.
		 *         if uErrMsg is NULL, no error will be reported.
		 *  @param errNode node to report the error against (if any).
		 *  @param init optional constant initializer expression.
		 */
		GenTypeElements(
			bool addInitializer,
			const char *uErrMsg,
			const AstNode &errNode,
			const AstNode *constInitExp
		);

		virtual void visit(RangeConstraintType &node);
		virtual void visit(EnumerationType &node);
		virtual void visit(SubtypeIndication &node);
		virtual void visit(RecordType &node);
		virtual void visit(UnconstrainedArrayType &node);
		virtual void visit(PhysicalType &node);

		void reset(void);

		/** contains all intermediate type elements of a composite 
		 *  (i.e. record type). For arrays, must contain exactly one
		 *  TypeElement. */
		std::list<intermediate::TypeElement*> composite;

		/** return the list of indices applying down to the base type.
		 *  @return list with all index constraints */
		std::list<DiscreteRange*> getIndices(void) const;

		/** list with all AST types that are referred to as final
		 *  elements of a record type. Should contain exactly one
		 *  element for an array type. */
		std::list<TypeDeclaration *> referredTypes;
	private:
		/** process a DiscreteRange
		 *  @param node DiscreteRange to process.
		 *  @param actualType actual referred to type.
		 */
		template <typename T> 
		void
		processDR(
			DiscreteRange &node, 
			TypeDeclaration *actualType);

		template <typename T>
		void
		processSI(SubtypeIndication &node);

		//! find the name of a referenced array or record type.
		/** @param node type declaration to find the name for
		 *  @return name of the referenced type.
		 */
		static std::string findName(const TypeDeclaration &node);

		void
		processSIArray(SubtypeIndication &node);

		//! calculate the upper bound of an array (if any)
		/** @return upper bound of the array (0-based), or 1 in case 
		 * 	    no indexConstraint is present
		 */
		universal_integer calcArrayBound(void) const;

		//! calculate initial value elements.
		/** @return calculated initial value elements from 
		  *         constInit member.
		  */
		std::list<intermediate::ImmediateOperand*> 
		getInitValue(void) const;

		//! current set index constraints.
		std::list<DiscreteRange*> indexConstraint;
		//! initializer constraint
		DiscreteRange *constraint;

		//! add initializer to type elements?
		bool addInit;

		/** munge index constraints together? */
		bool mungeIndices;

		/** error message, in case an unonstraint array w.o. 
		 *  prior indexConstraint was found.
		 */
		const char *uAErrMsg;

		/** node to report the error against (if any). */
		const AstNode &errorNode;

		/** is foreign inherited from a SubtypeIndication? */
		bool isForeign;

		/** optional constant initializer expression */
		const AstNode *constInit;
	};

	class GenTypes : public NullVisitor {
	public:
		//! dummy c'tor to initialize members
		GenTypes() : 	type(NULL),
				indexConstraint(NULL) {}

		/** Visit an EnumerationType node.
		 *  @param node EnumerationType node that get's visited.
		 */
		virtual void visit(EnumerationType &node);

		/** Visit a RangeConstraintType node.
		 *  @param node RangeConstraintType node that get's visited.
		 */
		virtual void visit(RangeConstraintType &node);

		/** Visit a PhysicalType node.
		 *  @param node PhysicalType node that get's visited.
		 */
		virtual void visit(PhysicalType &node);

		/** Visit a RecordType node.
		 *  @param node RecordType node that get's visited.
		 */
		virtual void visit(RecordType &node);

		/** Visit an UnconstrainedArrayType node.
		 *  @param node UnconstrainedArrayType node that get's visited.
		 */
		virtual void visit(UnconstrainedArrayType &node);

		/** Visit a SubtypeIndication node.
		 *  @param node SubtypeIndication node that get's visited.
		 */
		virtual void visit(SubtypeIndication &node);

		/** collected type definition */
		intermediate::Type *type;

		/** index constraint in case of subtype indications of arrays 
		 */
		std::list<DiscreteRange*> *indexConstraint;
	};
};

}; /* namespace ast */

#endif /* __GC_TYPES_HPP_INCLUDED */
