/* $Id$
 *
 * Code generator, which transforms the abstract syntax tree into intermediate
 * code.
 *
 * 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 __GEN_CODE_HPP_INCLUDED
#define __GEN_CODE_HPP_INCLUDED

#include "frontend/visitor/TopDownVisitor.hpp"
#include "intermediate/container/CodeContainer.hpp"
#include "intermediate/operands/Register.hpp"
#include "intermediate/container/Data.hpp"
#include "intermediate/opcodes/OpCode.hpp"
#include "frontend/visitor/NullVisitor.hpp"
#include "frontend/visitor/GCArrays.hpp"
#include "frontend/visitor/GCLoops.hpp"
#include "frontend/visitor/GCRegisterSet.hpp"
#include "frontend/ast/Types.hpp"
#include "frontend/misc/Driver.hpp"

namespace ast {

//! generate intermediate code from the AST
/** This visitor will generate intermediate code from the abstract syntax
 *  tree. 
 *  Steps, that must have been done prior to running this visitor are:
 *  - all symbols must have been resolved (ParserDriver, Parser in 
 *    conjunction with ResolveSymbols)
 *  - types must have been resolved (ResolveTypes)
 *  - intermediate code names must have been set (SetPathNames)
 *  - process drivers and formal parts must have been collected
 *    (GatherImplicits).
 *  - concurrent statements must have been transformed into processes
 *    (not implemented yet)
 *  - CheckLoops
 *  - port map's must have been normalized
 *  - what else?
 *
 */
class GenCode : public TopDownVisitor {
public:
	//! c'tor
	GenCode();

	//! alternate c'tor for expression handling
	/** This c'tor can be used to handle (sub-)expressions.
	 *  @param cc CodeContainer instance to reuse.
	 */
	GenCode(intermediate::CodeContainer *cc);

private:
	/** Visit a Package node.
	 *  @param node Package node that gets visited.
	 */
	virtual void visit(Package &node);

	/** Visit an Entity declaration.
	 *  @param node Entity Declaration node that gets visited.
	 */
	virtual void visit(Entity &node);

	/** Visit an Architecture node.
	 *  @param node Architecture node that gets visited.
	 */
	virtual void visit(Architecture &node);

	/** Visit a CompInstStat node.
	 *  @param node CompInstStat node that gets visited.
	 */
	virtual void visit(CompInstStat &node);

	/** Visit a VarAssignStat
	 *  @param node VarAssignStat node that gets visited.
	 */
	virtual void visit(VarAssignStat &node);

	/** Visit a SigAssignStat
	 *  @param node SigAssignStat node that gets visited.
	 */
	virtual void visit(SigAssignStat &node);

	/** visit a ConstInteger
         *  @param node node that gets visited.
         */
	virtual void visit(ConstInteger &node);

	/** visit a ConstReal
         *  @param node node that gets visited.
         */
	virtual void visit(ConstReal &node);

	/** visit a SimpleName
         *  @param node node that gets visited.
         */
	virtual void visit(SimpleName &node);

	/** visit an AttributeName
         *  @param node node that gets visited.
         */
	virtual void visit(AttributeName &node);

	/** visit an IfStat
         *  @param node node that gets visited.
         */
	virtual void visit(IfStat &node);

	/** visit a Subscript
         *  @param node node that gets visited.
         */
	virtual void visit(Subscript &node);

	/** visit a Slice
         *  @param node node that gets visited.
         */
	virtual void visit(Slice &node);

	/** visit a DiscreteRange
         *  @param node node that gets visited.
         */
	virtual void visit(DiscreteRange &node);

	/** visit a SelectedName
         *  @param node node that gets visited.
         */
	virtual void visit(SelectedName &node);

	/** visit a FunctionCall
         *  @param node node that gets visited.
         */
	virtual void visit(FunctionCall &node);

	/** visit an Aggregate
         *  @param node node that gets visited.
         */
	virtual void visit(Aggregate &node);

	/** visit a ReturnStat
         *  @param node node that gets visited.
         */
	virtual void visit(ReturnStat &node);

	/** Visit an Signal declaration.
	 *  @param node SignalDeclaration node that gets visited.
	 */
	virtual void visit(SignalDeclaration &node);

	/** Visit a VarDeclaration
	 *  @param node VarDeclaration node that gets visited.
	 */
	virtual void visit(VarDeclaration &node);

	/** Visit an Constant declaration.
	 *  @param node ConstantDeclaration node that gets visited.
	 */
	virtual void visit(ConstantDeclaration &node);

	/** Visit a Process node.
	 *  @param node Process node that gets visited.
	 */
	virtual void visit(Process &node);

	/** Visit a ForLoopStat
	 *  @param node ForLoopStat node that gets visited.
	 */
	virtual void visit(ForLoopStat &node);

	/** Visit a WhileLoopStat
	 *  @param node WhileLoopStat node that gets visited.
	 */
	virtual void visit(WhileLoopStat &node);

	/** Visit a NextStat
	 *  @param node NextStat node that gets visited.
	 */
	virtual void visit(NextStat &node);

	/** Visit an ExitStat
	 *  @param node ExitStat node that gets visited.
	 */
	virtual void visit(ExitStat &node);

	/** Visit a WaitStat
	 *  @param node WaitStat node that gets visited.
	 */
	virtual void visit(WaitStat &node);

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

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

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

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

	/** Visit an AttributeSpecification node.
	 *  @param node AttributeSpecification node that gets visited.
	 */
	virtual void visit(AttributeSpecification &node);

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

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

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

	/** process (w. direct dispatch) to CaseAlternative 
	 *  @param node CaseAlternative node.
	 *  @param cmpVal compare value of CaseStat node.
	 *  @param caseNext label to jump to for the next alternative.
	 *         This might be the same as caseOut.
	 *  @param caseOut label to jump to after the case statement.
	 */
	void 
	processAlternative(
		CaseAlternative &node,
		intermediate::Operand *cmpVal,
		intermediate::Label *caseNext,
		intermediate::Label *caseOut
	);

public:
	//! the code container containing the generated code
	/** This container will get allocated by the c'tor, but it
	 *  won't get free'd again, since the caller will need it.
	 */
	intermediate::CodeContainer *container;

private:
	/** process a RangeConstraintType.
	 *  @param node RangeConstraintType to generate icode for */
	template <typename T> 
	void processRCT(RangeConstraintType &node);

	//! Process a generic Callable.
        /** This function will get called for each Callable (or class
         *  derived from Callable) that gets visited.
         *
         *  @param node Callable instance.
	 *  @return CodeContainer of the create subprogram.
         */
	intermediate::CodeContainer *processCallable(Callable &node);

	//! Process a generic TypeDeclaration.
        /** This function will get called for each TypeDeclaration (or class
         *  derived from TypeDeclaration) that gets visited.
         *
         *  @param node TypeDeclaration instance.
         */
	virtual void process(TypeDeclaration &node);

	/** process an attribute name "range".
	 *  @param node attribute name that is a range attribute.
	 */
	void processRangeAttr(AttributeName &node);

	/** eventually perform an assignment, if the current mode is load.
	 *  This is useful, if the storage area must have been determined
	 *  before arrays can get assigned. The assignment will hence get 
	 *  done on the right hand side, in the top expression.
	 *
	 *  @param node expression of the assignment.
	 */
	void processExpression(Expression &node);

	/** process a discrete range by from and to members.
	 *  @param node DiscreteRange that has from and to set.
	 */
	void processDRByBounds(DiscreteRange &node);

	/** perform an assignment from sourceRegs to destRegs based
	 *  on a specified type.
	 *  @param type type that the source/destination RegisterSet
	 *         refers to.
	 */
	void doAssignment(TypeDeclaration &type);

	/** process an array aggregate association.
	 *  @param node ElementAssociation to process.
	 *  @param aType type of the aggregate.
	 */
	void 
	processArrayAssoc(
		ElementAssociation &node, 
		TypeDeclaration *aType
	);

	/** process an array aggregate.
	 *  @param node Aggregate to process.
	 */
	void processArrayAggregate(Aggregate &node);

	/** process an aggregate, which is of base type record.
	 *  @param node Aggregate to process.
	 */
	void processRecordAggregate(Aggregate &node);

	/** process a signal/constant/vardeclaration
	 *  @param node declaration to process
	 *  @param st desired storage type for intermediate code.
	 *  @return resulting Data declaration that is already added
	 *          to the current code container.
	 */
	intermediate::Data *
	processValDecl(
		ValDeclaration &node, 
		enum intermediate::StorageType st
	);

	/** Call a subprogram node.
	 *  This function will push the arguments, and call the subprogram.
	 *  @param node subprogram to call.
	 *  @param foreign foreign annotation to pass on, NULL for non-foreign
	 *  @return return register (pointer to value) or NULL for procedures
	 */
	template <typename T>
	intermediate::Register *callSubprog(T &node, const char *foreign);

	/** add code to move the return value into a register.
	 *  Must only be called at a place where a corresponding transfer
	 *  frame is accessible (here only from callSubprog)
	 *
	 *  @param node Subprogram in question.
	 *  @param callee Reference of the Subprogram.
	 *  @return Register with the return value.
	 */
	template <typename T>
	intermediate::Register *
	getReturnValue(T &node, intermediate::Reference *callee);

	typedef 
	std::pair<const ValDeclaration*, intermediate::Operand *> copyBackT;

	typedef std::list<copyBackT> copyBackListT;

	//! push the argument list of FunctionCall/ProcedureCall node.
	/** @param node subprogram call, which's argument list should get
	 *  		transferred.
	 *  @param foreign foreign annotation to pass to arguments
	 *         NULL for non-foreign.
	 *  @return list of intermediate code commands to copy back 
	 *          actuals to formals after the call.
	 */
	template <typename T>
	std::list<intermediate::OpCode *>
	setArgList(T &node, const char *foreign);

	//! push an argument of a function call on the stack.
	/** @param c Callable in question.
	 *  @param vd corresponding declaration of the formal.
	 *  @param arg argument
	 *  @param foreign desired foreign annotation or NULL if not foreign
	 *  @return intermediate opcode to copy back the actual to the
	 *          formal (or NULL if not applicable).
	 */
	intermediate::OpCode *
	setArg(
		Callable &c, 
		const ValDeclaration &vd, 
		AssociationElement &element,
		const char *foreign);

	/** set an argument by value.
	 *  used for 
	 *  - constant (non-composite)
	 *  - variable (non-composite) 
	 *
	 *  @param callee Reference name of the Callee
	 *  @param vd formal declaration (may be NULL, if the element 
	 *         contains a formal)
	 *  @param element AssociationElement with the actual.
	 *  @param foreign char value to annotate as foreign value, NULL
	 *         if not foreign.
	 *  @param copyBack prepare target operand for copy back values?
	 *  @return copy back command to copy back the actual to the formal
	 *          if copyBack is true, NULL otherwise.
	 */
	intermediate::OpCode *
	setArgByValue(
		intermediate::Reference *callee,
		const ValDeclaration *vd,
		AssociationElement &element,
		const char *foreign,
		bool copyBack);

	/* set an argument by passing a pointer
	 * used for
	 * - signal (in, inout) (composite) (here it's a pointer to the signal
	 *   pointer)
	 * - variable (composite)
	 *
	 *  @param callee Reference name of the Callee
	 *  @param vd formal declaration (may be NULL, if the element 
	 *         contains a formal)
	 *  @param element AssociationElement with the actual.
	 *  @param foreign char value to annotate as foreign value, NULL
	 *         if not foreign.
	 */
	void 
	setArgByBasePointer(
		intermediate::Reference *callee,
		const ValDeclaration *vd,
		AssociationElement &element,
		const char *foreign);

	/** set an argumenty by passing a pointer to the signal
	 *  used fo
	 *  - signal (non-composite)
	 *  @param callee Reference name of the Callee
	 *  @param vd formal declaration (may be NULL, if the element 
	 *         contains a formal)
	 *  @param element AssociationElement with the actual.
	 *  @param foreign char value to annotate as foreign value, NULL
	 *         if not foreign.
	 */
	void 
	setArgByPointer(
		intermediate::Reference *callee,
		const ValDeclaration *vd,
		AssociationElement &element,
		const char *foreign);

	/* set an argument by passing a pointer to a temporary.
	 * used for
	 * - composite constant 
	 */
	void 
	setArgByBasePointerToTemporary(
		intermediate::Reference *callee,
		const ValDeclaration &vd,
		AssociationElement &element,
		const char *foreign);

	/** set an argument by passing the base pointer to the driver.
	 *  (pointer to driver pointer)
	 *  used for 
	 *  - signal (inout, out) (composite [usePointer=true])
	 *  - signal (inout, out) (non-composite [usePointer=false])
	 *
	 *  For a signal parameter of mode inout, the actual part will
	 *  be visited twice!
	 */
	void 
	setArgByDriver(
		intermediate::Reference *callee,
		const std::list<Driver*> &drivers,
		const ValDeclaration &vd,
		AssociationElement &element,
		const char *foreign,
		bool usePointer);

	/** set the constraint arguments for an unconstraint array by 
	 *  the constraints from the given declaration.
	 *  @param cRef Reference to the function call
	 *  @param icPrefix intermediate code name prefix.
	 *  @param t type containing the constraint.
	 *  @param foreign optional foreign annotation.
	 */
	void 
	setConstraintsByType(
		intermediate::Reference *cRef,
		const std::string &icPrefix, 
		const TypeDeclaration *t,
		const char *foreign);

	/** set the constraint arguments for an unconstraint array by 
	 *  the constraints from the source register set.
	 *  @param cRef Reference to the function call
	 *  @param icPrefix intermediate code name prefix
	 *  @param foreign optional foreign annotation
	 */
	void
	setConstraintsByRS(
		intermediate::Reference *cRef, 
		const std::string &icPrefix,
		const char *foreign);

	/** add data elements for unconstraint array bounds to
	 *  the container.
	 *  @param node unconstraint ValDeclaration 
	 */
	void addUnconstraintParams(const ValDeclaration &node);

	/** pickup the constraints for an unconstraint SimpleName
	 */
	void getConstraints(const ValDeclaration &vd);

	/** copy the array node to the destination register set.
	 *  @param node array to copy.
	 */
	void processArrayCopy(ConstArray &node);

	/** udpate (sig-assign) the const array to the destination register
	 *  set.
	 *  @param node source array.
	 */
	void processArrayUpdate(ConstArray &node);

	//! process a ConstReal or ConstInteger node.
	/** @param node node to process.
	 */
	template <typename T>
	void
	processConst(T &node);

	//! process a Subscript node with one index.
	/** @param node subscript to process.
	 *  @param indices list with index expressions.
	 */
	void
	processSubscription(Subscript &node, std::list<Expression*> &indices);

	//! process a next/exit statement.
	/** @param optCond optional condition when the next/exit statement
	 *         should be evaluated.
	 *  @param target branch target, if the condition is met.
	 */
	void
	processCFLoopStat(
		Expression *optCond, 
		intermediate::Label *target);

	//! assign the expression to the destination register set if true.
	bool assignExpression;

	//! is the currently evaluated expression a target?
	/** For a target, instead of the signal pointer, the driver pointer
	 *  must get used. Also direct values in valueReg are not allowed.
	 */
	bool isTarget;

public:
	//! source register set
	RegisterSet sourceRegs;
private:

	//! destination register set
	RegisterSet destRegs;

	//! data operand passed to AssignVisitor. 
	/** Contains various additional data depending on the assignMode.
	 *  (e.g. delay for signal assignments, severity level for log 
	 *  opcodes etc. See assignMode for details).
	 */
	intermediate::Operand *dataOp;

	/** Correctly copy/assign composite return values of a function 
	 *  call to the appropriate place and mangle the source
	 *  RegisterSet accordingly.
	 */
	void handleCompositeReturn(FunctionCall &node);

	/** create an init function for the architecture and add it to
	 *  the current code container.
	 *  @param node Architecture for which to create an init function.
	 */
	void createInitFunction(const Architecture &node);

	/** generate code to initialize an Architecture declared as 
	 *  foreign.
	 *
	 *  @param node foreign Architecture 
	 *  @param foreign corresponding foreign attribute.
	 */
	void foreignInit(
		const Architecture &node, 
		const AttributeSpecification &foreign
	);

	/** instantiate subcomponents in the architectures init function.
	 *  @param node Architecture which holds the subcomponents.
	 */
	void instantiateComponents(const Architecture &node);

	/** instantiate one subcomponent given by the CompInstStat
	 *  @param node CompInstStat of the sub component instantiation.
	 */
	void instantiateComponent(CompInstStat &node);

	/** generate an AssociationList where formal 
	 *  and actual both refer to the declaration.
	 *  @param dest append generated Associations in dest
	 *  @param src list of ValDeclarations (or derived from VDs).
	 *  @param lookupRegion declarative Region where the declarations
	 *         of src reside.
	 *
	 *  mainly useful for foreignInit(Architecture)
	 */
	template <typename T>
	void
	makeSelfAssociations(
		std::list<AssociationElement *> &dest,
		const T &src,
		const DeclarativeRegion *lookupRegion
	);


	/** Set all ports as parameters to reference ref.
	 *  @param cont Reference of component container
	 *  @param portList port list that should get set as parameters.
	 *  @param isForeign is this for a foreign entity?
	 */
	void 
	setPortList(
		intermediate::Reference *cont, 
		const std::list<AssociationElement *> &portList,
		bool isForeign
	);

	/** Set all generics as parameters to reference ref.
	 *  @param cont Reference of component container
	 *  @param genericMap generic map that should get set as parameters.
	 *  @param isForeign is this for a foreign entity?
	 */
	void
	setGenericMap(
		intermediate::Reference *cont,
		std::list<AssociationElement *> &genericMap,
		bool isForeign);

	/** create an init function for the process and add it to
	 *  the current code container.
	 *  @param node Process for which an init function should get created
	 */
	void createInitFunction(const Process &node);

	/** register the drivers in the stack segment for the process.
	 *  @param node process with drivers.
	 */
	void registerDrivers(const Process &node);

	/** register one driver in the stack segment for the process.
	 *  @param drv driver to register.
	 */
	void registerDriver(Driver &drv);


	/** annotate the type size for a type (in case it is known).
	 *  @param data data node that should get annotated.
	 *  @param type type of the declaration.
	 */
	static void annotateDataSize(
		intermediate::Data &data, 
		const SubtypeIndication *type
	);

	/** generate code for an index expression of a subscription.
	 *  @param index Expression to traverse to.
	 *  @return operand with the value of the index.
	 */
	intermediate::Operand *
	getSubscriptIndex(Expression *index);

	/** add annotations about type for foreign calls/generic/port map
	 *  aspects.
	 *  @param sp SetParam to be annotated.
	 *  @param vd ValDeclaration of the formal.
	 */
	static void
	annotateSetParamType(
		intermediate::SetParam &sp, 
		const ValDeclaration &vd);

	/** current enclosing process (or NULL). */
	Process *currentProcess;
	/** current enclosing subprogram declaration (or NULL) */
	Callable *currentSubprog;
	/** mapping for loop statements. */
	LoopRegistry loopRegistry;
	/** list of all instantiaded components by an architecture. */
	std::list<CompInstStat*> archComponents;

	/** operation to be performed for assignments */
	enum assignOperationE {
		/** standard operation: copy the parameter */
		ASSIGN_OPERATION_COPY,
		/** log each element in order with LOG. */
		ASSIGN_OPERATION_LOG,
		/** connect the driver in src to the signal in dst */
		ASSIGN_OPERATION_CONNECT,
		/** issue a WakeOn for each signal in src */
		ASSIGN_OPERATION_WAKEON

	};

	/** current operation to be used for assignements.
	 *  The default mode copy will perform a copy from source to
	 *  dest, either via MOV opcodes for non-signals and UPDATE
	 *  opcodes for signals, thereby using dataOp as delay operand.
	 *
	 *  Log will add a log opcode for each source, using dataOp
	 *  as severity level.
	 */
	enum assignOperationE assignOperation;

	//! generate code to assign src to dst
	class AssignVisitor : public NullVisitor {
	public:
		/** @param source operand referring to the source.
		 *  @param dest operand referring to the destination.
		 *  @param c CodeContainer to which code snippets should
		 *         get added.
		 *  @param dataOperand additional data passed to the visitor.
		 *         See assignMode for details.
		 */
		AssignVisitor(
			RegisterSet *source,
			RegisterSet *dest,
			intermediate::CodeContainer &c,
			intermediate::Operand *dataOperand,
			enum assignOperationE mode
			) :	src(source),
				dst(dest),
				container(c),
				dataOp(dataOperand),
				operation(mode) {}

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

		//! generate code to perform the selected operation.
		/** @param t baseType of sourceRegs.
		 */
		void performOperation(enum BaseType t);

		//! generate code for an assign statement.
		/** This method will call the fitting operand methods.
		 *   @param t base type of the assigned value.
		 */
		void makeAssignment(enum BaseType t);

		//! log a value present in src
		void logValue(void);

		//! issue a wakeon on a signal element
		void wakeon(enum BaseType t);

		//! connect the driver in src to the signal in dst
		void connect(void);

		//! process a SubtypeIndication with an array base type.
		/** @param node SubtypeIndication of type array.
		 */
		void processArraySubtype(SubtypeIndication &node);

		/** source operand */
		RegisterSet *src;
		/** target operand */
		RegisterSet *dst;
		/** instance of the CodeContainer */
		intermediate::CodeContainer &container;
		//! delay operand
		intermediate::Operand *dataOp;
		//! desired operation
		enum assignOperationE operation;
	};

	//! process a generic expression, and perform assignments.
	/** For each subclass of an expression, this class should get 
	 *  instanciated on the stack when processing the AST node.
	 *  The c'tor will take care to stack the necessary variables
	 *  and the d'tor will perform the assignment.
	 */
	class ProcessStackedExpression {
	public:
		//! c'tor
		/** @param gc GenCode instance.
                 *  @param node currently processed Expression.
		 */
		ProcessStackedExpression(GenCode &gc, Expression &n);

		//! d'tor
		~ProcessStackedExpression();
	private:
		//! GenCode instance
		GenCode &genCode;
		
		//! currently processed expression node
		Expression &node;

		//! backup of assignExpression
		bool stackedAssignExpression;
	};
};

}; /* namespace ast */

#endif /* __GEN_CODE_HPP_INCLUDED */
