/* $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.
 */

#include "frontend/visitor/GCRegisterSet.hpp"
#include <cassert>
#include "intermediate/opcodes/GetSig.hpp"
#include "intermediate/opcodes/Mov.hpp"
#include "intermediate/operands/IndirectOperand.hpp"
#include "intermediate/operands/RegisterFactory.hpp"
#include "frontend/visitor/GCArrays.hpp"

namespace ast {

using namespace intermediate;

Operand *
RegisterSet::getValue(enum BaseType bt)
{
	enum OpType ot = toOpType(bt);

	if (this->isSignal) {
		if (this->value != NULL) {
			return this->value;
		}
		// generated code to access the signal first, and cache the
		// result in value
		assert(this->composite != NULL);

		Register *compositeReg = 
			dynamic_cast<Register*>(this->composite);
		if (compositeReg == NULL) {
			compositeReg = 
				this->cc->createRegister(OP_TYPE_POINTER);
			Mov *toReg = new Mov(this->composite, compositeReg);
			this->cc->addCode(toReg);
		}

		IndirectOperand *pToSig = 
			new IndirectOperand(compositeReg, OP_TYPE_POINTER);

		this->value = this->cc->createRegister(ot);
		GetSig *gs = new GetSig(pToSig, this->value, NULL);
		this->cc->addCode(gs);

		return this->value;
	}

	// not a signal
	if (this->value != NULL) {
		// value already present.
		return this->value;
		
	}

	// composite pointer referring to the location of a variable.
	// use an IndirectOperand and cache it in value
	assert(this->composite != NULL);
	Register *compositeReg = dynamic_cast<Register*>(this->composite);
	if (compositeReg == NULL) {
		compositeReg = this->cc->createRegister(OP_TYPE_POINTER);
		Mov *toReg = new Mov(this->composite, compositeReg);
		this->cc->addCode(toReg);
	}
	IndirectOperand *ir = new IndirectOperand(compositeReg, ot);
	this->value = ir;
	return this->value;
}

Operand *
RegisterSet::getDestination(enum BaseType bt)
{
	assert(this->composite != NULL);

	enum OpType ot;
	if (this->isSignal) {
		// return a pointer to the concrete driver
		ot = OP_TYPE_POINTER;
	} else {
		ot = toOpType(bt);
	}

	Register *compositeReg = dynamic_cast<Register *>(this->composite);
	if (compositeReg == NULL) {
		compositeReg = this->cc->createRegister(OP_TYPE_POINTER);
		Mov *m = new Mov(this->composite, compositeReg);
		this->cc->addCode(m);
		this->composite = compositeReg;
	}
	
	
	return new IndirectOperand(compositeReg, ot);
}

Operand *
RegisterSet::getPointer(void)
{
	assert(this->composite != NULL);
	return this->composite;
}

void
RegisterSet::setPointer(Operand *ptr)
{
	assert(ptr != NULL);
	util::MiscUtil::terminate(this->composite);
	util::MiscUtil::terminate(this->value);

	this->composite = ptr;
}

void
RegisterSet::setValue(Operand *val)
{
	assert(val != NULL);
	util::MiscUtil::terminate(this->composite);
	util::MiscUtil::terminate(this->value);

	this->value = val;
}

void
RegisterSet::subscribe(
	TypeDeclaration *arrayType,
	std::list<Operand *> indices
)
{
	assert(arrayType != NULL);
	ArrayHandling ah = 
		ArrayHandling(arrayType, this->getPointer(), *this->cc,
				this->leftBounds,
				this->rightBounds,
				this->directions);

	Register *result = ah.subscribe(indices);
	this->setPointer(result);

	this->leftBounds.clear();
	this->rightBounds.clear();
}

void
RegisterSet::setUnconstraintBounds(
	std::list<Operand *> lb,
	std::list<Operand *> rb,
	std::list<Operand *> ds
)
{
	this->leftBounds = lb;
	this->rightBounds = rb;
	this->directions = ds;
}


bool
RegisterSet::isUnconstraint(void) const
{
	return ! this->leftBounds.empty();
}

void
RegisterSet::getConstraints(
	std::list<Operand*> &lb,
	std::list<Operand*> &rb,
	std::list<Operand*> &ds
) const
{
	lb = this->leftBounds;
	rb = this->rightBounds;
	ds = this->directions;
}


}; /* namespace ast */
