/* $Id$ 
 *
 * Generate intermediate code, array specific parts.
 *
 * 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_ARRAYS_HPP_INCLUDED
#define __GC_ARRAYS_HPP_INCLUDED

#include <utility>
#include "intermediate/container/CodeContainer.hpp"
#include "intermediate/container/LabelFactory.hpp"
#include "intermediate/operands/Register.hpp"
#include "frontend/ast/TypeDeclaration.hpp"
#include "frontend/visitor/GCTypes.hpp"

namespace ast {

//! ArrayHandling can subscribe to arrays.
class ArrayHandling {
public:
	/** @param at array type declaration (should be an unconstraint array)
	 *  @param b base pointer of the array.
	 *  @param container container to add generated code to.
	 *  @param lbounds operands containing the left bounds for each
	 *         dimension of the unconstraint array.
	 *  @param rbounds operands containing the right bounds for
	 *         each dimension of the unconstraint array.
	 *  @param directs operands containing the direction (1/-1) for each
	 *         dimension of the unconstraint array.
	 */
	ArrayHandling(
		TypeDeclaration *at,
		intermediate::Operand *b,
		intermediate::CodeContainer &container,
		std::list<intermediate::Operand *> lbounds,
		std::list<intermediate::Operand *> rbounds,
		std::list<intermediate::Operand *> directs);


	//! d'tor
	~ArrayHandling();

	intermediate::Register *
	subscribe(std::list<intermediate::Operand *> relativeIndices);

private:
	/** calculate the list of factors */
	void factorize(void);

	/** factors with which each dimennsion must be multiplied. */
	std::list<intermediate::Operand *> dimensionFactors;

protected:
	/** array type */
	TypeDeclaration *arrayType;
private:
	/** pointer to start of array */
	intermediate::Operand *base;
protected:
	/** container, to which code will be added */
	intermediate::CodeContainer &cc;
	/** list of left bounds for each dimension */
	std::list<intermediate::Operand *> leftBounds;
	/** list of right bounds for each dimension */
	std::list<intermediate::Operand *> rightBounds;
	/** list of directions (1=to, -1=downto) for each dimenstion */
	std::list<intermediate::Operand *> directions;
private:
	/** intermediate code type of array */
	intermediate::Type *itype;
protected:
	/** type of final elements. (doesn't necessary match resulting type
	 *  of a subscription, e.g. in cases of an array of an array, where 
	 *  only the first array is subscribed. This always refers to a type
	 *  which is not an array. */
	TypeDeclaration *elementType;
	/** list of indices */
	std::list<DiscreteRange*> indices;

public:
	/** transform the index to a zero-based index (useful for ConstArray)
	 *  based on the IndexConstraint of arrayType.
	 *  @param arrayType type of the constraint array.
	 *  @param idx index relative to constraint type.
	 *  @return zero-based index, suitable for ConstArray.
	 */
	static universal_integer 
	transform_idx(
		const TypeDeclaration *arrayType, 
		universal_integer idx
	);
};


//! generic array handling to iterate over all elements of an array.
/** Generic array handling which can iterate over all elements of an array,
 *  in case the bounds are statically known.
 *  Abstract class, implementors need to implement iterateBody.
 */
class StaticArrayIterate : public ArrayHandling {
public:
	/** c'tor
	 *  @param at Array type declaration (must not be an unonstraint array
	 *  @param b register with the base pointer of the array
	 *  @param container container to add generated code to.
	 */
	StaticArrayIterate(
		TypeDeclaration *at,
		intermediate::Operand *b,
		intermediate::CodeContainer &container
		) : 	ArrayHandling(
				at, 
				b, 
				container, 
				std::list<intermediate::Operand *>(),
				std::list<intermediate::Operand *>(),
				std::list<intermediate::Operand *>()) {}
	
	/** dummy virtual d'tor */
	virtual ~StaticArrayIterate() {}
	
	/** iterate over all elements of the array, calling iterateBody for 
	 *  each
	 */
	void iterate(void);

protected:
	/** called for each element during for an iteration. 
	 *  @param element pointer to the element in question.
	 *  @param indices list of indices denoting the element.
	 */
	virtual void 
	iterateBody(
		intermediate::Register *element, 
		std::list<universal_integer> indices
		) = 0;

private:
	/** check if all counters have reached ubounds.
	 *  @param counters loop counters.
	 *  @param rbounds right bounds of the array.
	 *  @param directions directions of the array (-1, 1).
	 *  @return true, if all counters are the same as the respective
	 *          right bounds, otherwise false.
	 */
	static bool 
	checkLoop(
		const std::list<universal_integer> &counters,
		const std::list<universal_integer> &rbounds,
		const std::list<universal_integer> &directions);

	/** increase the counters to advance to the next element.
	 *  @param counters loop counters to be increased.
	 *  @param lbounds corresponding left bounds.
	 *  @param rbounds corresponding right bounds.
	 *  @param directions directions (-1, 1) of the array, which are
	 *         used as a step factor here.
	 */
	static void
	incCounters(
		std::list<universal_integer> &counters,
		const std::list<universal_integer> &lbounds,
		const std::list<universal_integer> &rbounds,
		const std::list<universal_integer> &directions);
};

//! iterate over arrays
/** This class can generate loop code to iterate over an array.
 *  It works for both arrays, where the bounds are known statically, as 
 *  well for ones where the bounds are known dynamically.
 */
class ArrayIterate : public ArrayHandling {
public:
	/** @param at array type declaration (should be an unconstraint array)
	 *  @param b base pointer of the array.
	 *  @param container container to add generated code to.
	 *  @param lbounds operands containing the left bounds for each
	 *         dimension of the unconstraint array.
	 *  @param rbounds operands containing the right bounds for
	 *         each dimension of the unconstraint array.
	 *  @param directs directions (-1, 1) of the dimensions.
	 */
	ArrayIterate(
		TypeDeclaration *at,
		intermediate::Operand *b,
		intermediate::CodeContainer &container,
		std::list<intermediate::Operand *> lbounds,
		std::list<intermediate::Operand *> rbounds,
		std::list<intermediate::Operand *> directs
		) : 	ArrayHandling(at, 
					b, 
					container, 
					lbounds, 
					rbounds,
					directs),
			loop(intermediate::LabelFactory::getLabel("loop")) {}

	//! dummy d'tor
	virtual ~ArrayIterate() {}

	/** iterate over all elements of the array */
	void iterate(void);

protected:
	/** called for one element of the iteration in the place of the body
	 *  of the iteration.
	 *  @param element pointer to the element in question.
	 *  @param indices list of indices denoting the element (rel. offsets)
	 */
	virtual void 
	iterateBody(
		intermediate::Register *element, 
		std::list<intermediate::Register *> indices
		) = 0;

private:
	//! initialize the counters with the left bounds.
	void initCounters(void);

	//! increase the counters
	void incCounters(void);

	/** list of counters for dimensions. */
	std::list<intermediate::Register *> counters;

	/** loop label. */
	intermediate::Label *loop;

};

}; /* namespace ast */

#endif /* __GC_ARRAYS_HPP_INCLUDED */
