/* $Id$
 *
 * RegisterSet defines a means to address signals, drivers, values, and to
 * apply operations on these.
 *
 * 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_REGISTER_SET_HPP_INCLUDED
#define __GC_REGISTER_SET_HPP_INCLUDED

#include "intermediate/operands/Register.hpp"
#include "frontend/ast/Types.hpp"
#include "frontend/ast/TypeDeclaration.hpp"
#include "intermediate/container/CodeContainer.hpp"
#include <iostream>

namespace ast {

//! register set used to propagate values from/to expressions
class RegisterSet {
public:
	//! c'tor to initialize members with NULL
	/** This c'tor will do nothing but initializing the members
	 *  with NULL.
	 */
	RegisterSet(
		intermediate::CodeContainer &container
		) :	value(NULL),
			composite(NULL),
			sliceBegin(NULL),
			sliceEnd(NULL),
			cc(&container),
			isSignal(false) {
	}

	//! copy c'tor, adjust reference counts.
	RegisterSet(
		const RegisterSet &other
		) : 	value(other.value),
			composite(other.composite),
			sliceBegin(other.sliceBegin),
			sliceEnd(other.sliceEnd),
			cc(other.cc),
			leftBounds(other.leftBounds),
			rightBounds(other.rightBounds),
			directions(other.directions),
			isSignal(other.isSignal) {}

	//! d'tor, free references.
	~RegisterSet() {
		util::MiscUtil::terminate(this->value);
		util::MiscUtil::terminate(this->composite);
		util::MiscUtil::terminate(this->sliceBegin);
		util::MiscUtil::terminate(this->sliceEnd);
	}

	//! assignment operator
	/** @param other RegisterSet to assign to this RegisterSet.
	 *  @return nothing, chained assignment is ugly and should be
	 *  avoided.
	 */
	RegisterSet *
	operator =(const RegisterSet &other) {
		util::MiscUtil::terminate(this->value);
		util::MiscUtil::terminate(this->composite);
		util::MiscUtil::terminate(this->sliceBegin);
		util::MiscUtil::terminate(this->sliceEnd);

		if (other.value != NULL) {
			this->value = other.value;
		}
		if (other.composite != NULL) {
			this->composite = other.composite;
		}
		if (other.sliceBegin != NULL) {
			this->sliceBegin = other.sliceBegin;
		}
		if (other.sliceEnd != NULL) {
			this->sliceEnd = other.sliceEnd;
		}

		this->isSignal = other.isSignal;
		this->cc = other.cc;
		this->leftBounds = other.leftBounds;
		this->rightBounds = other.rightBounds;
		this->directions = other.directions;

		return this;
	}

	//! retrieve an Operand containing the real/integer value.
	/** @param bt base type.
	 *  @return operand with an integral or real value. Reference count
	 *          is already increased.
	 */
	intermediate::Operand *getValue(enum BaseType bt);

	/** retrieve an Operand with which a variable or Signal can be 
	 *  modified. For a variable, this will be an indirect operand.
	 *  For a signal or driver, it will be a pointer to the signal
	 *  or driver instance, which can be passed as is to update, connect,
	 *  or getsig.
	 *
	 *  value (immediate) -> error
	 *  variable: -> IndirectOperand(pointer to data instance)
	 *  signal: -> pointer to signal instance.
	 *  driver: -> error
	 *
	 *  @param bt base type.
	 *  @return operand which can be used in an Update or Mov Opcode to
	 *          update the signal or respectively the variable.
	 *          Reference count is already increased.
	 */
	intermediate::Operand *getDestination(enum BaseType bt);

	/** retrieve a pointer to the storage location.
	 *  This method is useful to perform offset calculations, which need
	 *  to be based on pointers.
	 *  Since the intermediate only has handles to pointers of signals and
	 *  drivers, which will be created during run-time - out of scope of 
	 *  the intermediate code - arrays or records are stored as arrays
	 *  of pointers to actual signals. getPointer will contain the pointer
	 *  to the elements in a composite type, so that aoffset/roffset can
	 *  add offsets to that pointers which in turn must be dereferenced to
	 *  obtain the pointer to the signal or driver.
	 *  
	 *  value (immediate) -> error
	 *  variable -> pointer to variable.
	 *  signal -> pointer to pointer to signal instance.
	 *  driver -> pointer to pointer to driver instance.
	 *
	 *  @return pointer to storage location.
	 */
	intermediate::Operand *getPointer(void);

	/** set the pointer to the storage location.
	 *  value (immediate) -> error
	 *  variable -> pointer to variable
	 *  signal -> pointer to pointer to signal
	 *  driver -> pointer to pointer to driver
	 *
	 *  @param ptr Operand with above semantics.
	 */
	void setPointer(intermediate::Operand *ptr);

	/** set the value.
	 *  value (immediate) -> value
	 *  variable (indirect) -> value (or error?)
	 *  signal -> error
	 *  driver -> error
	 */
	void setValue(intermediate::Operand *val);

	/** treat RegisterSet contents as an array, and subscribe to it
	 *  with the indices.
	 *  @param arrayType array type that the array should refer to.
	 *  @param indices list of relative indices of the array.
	 */
	void 
	subscribe(
		TypeDeclaration *arrayType,
		std::list<intermediate::Operand *> indices);

	/** denote, that the current content refers to an unconstraint 
	 *  array, and has the lower/upper bounds and directions.
	 *  @param lb left bounds of the unconstraint array.
	 *  @param rb right bounds of the unconstraint array.
	 *  @param ds directions of the unconstraint array.
	 */
	void
	setUnconstraintBounds(
		std::list<intermediate::Operand *> lb,
		std::list<intermediate::Operand *> rb,
		std::list<intermediate::Operand *> ds);


	/** does the RegisterSet refer to an unconstraint array? */
	bool isUnconstraint(void) const;

	/** store the current applicable lower/upper bounds and the
	 *  directions of an unconstraint array in lb/ub/ds.
	 *  @param lb: list to store lower bounds in.
	 *  @param rb: list to store upper bounds in.
	 *  @param ds: list to store directions in.
	 */
	void 
	getConstraints(
		std::list<intermediate::Operand *> &lb,
		std::list<intermediate::Operand *> &rb,
		std::list<intermediate::Operand *> &ds) const;


private:
	/** operand that contains an integer/real value. Don't read this
	 *  operand directly, use getValue() instead. */
	intermediate::Operand *value;

	/** Operand referring to the (base) address of a composite type,
	 *  or to the address of a variable, signal or driver.
	 *  To obtain the value, use getValue().
	 */
	intermediate::Operand *composite;
public: /* FIXME should be private as well */
	//! register containing the low bound of a slice (integer)
	/** currently ignored in almost all cases FIXME */
	intermediate::Operand *sliceBegin;
	//! register containing the high bound of a slice (integer)
	/** currently ignored in almost all cases FIXME */
	intermediate::Operand *sliceEnd;

private:
	//! current code container.
	intermediate::CodeContainer *cc;
	//! left bounds of an unconstraint array.
	std::list<intermediate::Operand *> leftBounds;
	//! right bounds of an unconstraint array.
	std::list<intermediate::Operand *> rightBounds;
	//! directions of an unconstraint array.
	std::list<intermediate::Operand *> directions;
public:
	//! does the composite value refer to a signal/driver?
	bool isSignal;
};

}; /* namespace ast */

#endif /* __GC_REGISTER_SET_HPP_INCLUDED */
