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


#include "frontend/visitor/GCTypes.hpp"

#include <cassert>
#include "frontend/ast/RecordType.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/PhysicalType.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/ConstReal.hpp"
#include "frontend/ast/ConstArray.hpp"
#include "frontend/reporting/ErrorRegistry.hpp"
#include "frontend/reporting/CompileError.hpp"
#include "util/MiscUtil.hpp"
#include "intermediate/operands/ImmediateOperand.hpp"
#include "intermediate/operands/IndirectOperand.hpp"
#include "intermediate/operands/RegisterFactory.hpp"
#include "intermediate/opcodes/Mov.hpp"
#include "intermediate/opcodes/Je.hpp"
#include "intermediate/opcodes/Jne.hpp"
#include "intermediate/opcodes/Jb.hpp"
#include "intermediate/opcodes/Jbe.hpp"
#include "intermediate/opcodes/Jmp.hpp"
#include "intermediate/opcodes/IMul.hpp"
#include "intermediate/opcodes/Add.hpp"
#include "intermediate/opcodes/Sub.hpp"
#include "intermediate/opcodes/Call.hpp"
#include "intermediate/container/Label.hpp"
#include "intermediate/container/LabelFactory.hpp"
#include "intermediate/container/Data.hpp"
#include "intermediate/container/TypeFactory.hpp"
#include "intermediate/container/Type.hpp"
#include "intermediate/container/TypeElement.hpp"
#include "intermediate/container/TypeFactory.hpp"


namespace ast {

/* import some names into current namespace, just to avoid lengthy types */
using namespace intermediate;

universal_integer
GCTypes::calcArrayBound(
	const std::list<DiscreteRange*> &indices
)
{
	universal_integer card = 1;

	for (std::list<DiscreteRange*>::const_iterator i = indices.begin(); 
		i != indices.end(); i++) {
	
		card *= (*i)->getArraySize();
	}

	if (card < 0) {
		// null array
		return 0;
	}

	return card;
}



/*
 * ================== GENTYPEELEMENTS ===============
 */

GCTypes::GenTypeElements::GenTypeElements(
	bool addInitializer,
	const char *uErrMsg,
	const AstNode &errNode,
	const AstNode *constInitExp
) : 		composite(std::list<TypeElement*>()),
		constraint(NULL),
		addInit(addInitializer),
		mungeIndices(false),
		uAErrMsg(uErrMsg),
		errorNode(errNode),
		isForeign(false),
		constInit(constInitExp)
{
}

void
GCTypes::GenTypeElements::visit(RangeConstraintType &node)
{
	switch(node.baseType) {
	case BASE_TYPE_INTEGER:
		this->processDR<ConstInteger>(*node.constraint, 
						&node);
		break;

	case BASE_TYPE_REAL:
		this->processDR<ConstReal>(*node.constraint, &node);
		break;

	default:
		assert(false);
	}
}

template <typename T>
void
GCTypes::GenTypeElements::processDR(
	DiscreteRange &node, 
	TypeDeclaration *actualType
)
{
	std::list<ImmediateOperand*> initVs = std::list<ImmediateOperand*>();
	ImmediateOperand *init = NULL;
	if (this->addInit) {
		const T *iv;
		if (this->constInit != NULL) {
			iv = dynamic_cast<const T*>(this->constInit);
		} else {
			assert(node.from != NULL);
			assert(node.to != NULL);
			iv = dynamic_cast<const T*>(node.from);
		}

		// FIXME can fail, if not const initializer from if-path
		assert(iv != NULL);

		init = new ImmediateOperand(iv->value);
		initVs.push_back(init);
	}
	
	TypeElement *te = 
		new TypeElement(TypeFactory::getTypeName(
						actualType->baseType),
				initVs,
				this->calcArrayBound());
	
	this->composite.push_back(te);
	this->referredTypes.push_back(actualType);
}

void
GCTypes::GenTypeElements::visit(EnumerationType &node)
{
	std::list<ImmediateOperand*> initVs = this->getInitValue();
	TypeElement *te = 
		new TypeElement(TypeFactory::getTypeName(BASE_TYPE_INTEGER),
				initVs, this->calcArrayBound());

	this->composite.push_back(te);
	this->referredTypes.push_back(&node);
}

void
GCTypes::GenTypeElements::visit(SubtypeIndication &node)
{
	switch (node.baseType) {
	case BASE_TYPE_INTEGER:
	case BASE_TYPE_ENUM:
		this->processSI<ConstInteger>(node);
		// return early.
		return;

	case BASE_TYPE_REAL:
		this->processSI<ConstReal>(node);
		// return early.
		return;

	case BASE_TYPE_ARRAY:
		this->processSIArray(node);
		break;

	case BASE_TYPE_RECORD:
		// doesn't really make sense imho... but let's traverse to the
		// base type. (but it is allowed by lrm)
		break;

	default:
		// must not happen
		assert(false);
	}

	TypeDeclaration *t = const_cast<TypeDeclaration*>(node.declaration);
	t->accept(*this);
}

template <typename T>
void
GCTypes::GenTypeElements::processSI(SubtypeIndication &node)
{
	if (node.constraint != NULL) {
		this->processDR<T>(*node.constraint, &node);
		return;
	}

	// no constraint set, traverse to parent
	TypeDeclaration *fixme = 
		const_cast<TypeDeclaration*>(node.declaration);
	fixme->accept(*this);
}

void
GCTypes::GenTypeElements::visit(RecordType &node)
{
	assert(this->indexConstraint.empty() || this->mungeIndices);
	TypeElement *te = new TypeElement(node.getICName(), 
					std::list<ImmediateOperand*>(),
					this->calcArrayBound());
	this->composite.push_back(te);
	this->referredTypes.push_back(&node);
}

void
GCTypes::GenTypeElements::visit(UnconstrainedArrayType &node)
{
	if (this->indexConstraint.empty() && (this->uAErrMsg != NULL)) {
		CompileError *ce = 
			new CompileError(this->errorNode, this->uAErrMsg);
		ErrorRegistry::addError(ce);
		return;
	}

	std::string name = "";
	assert(node.elementType != NULL);

	switch (node.elementType->baseType) {
	case BASE_TYPE_INTEGER:
	case BASE_TYPE_REAL:
	case BASE_TYPE_ENUM:
		node.elementType->accept(*this);
		return;

	case BASE_TYPE_RECORD: {
		name = this->findName(*node.elementType);
		TypeElement *te = new TypeElement(name, 
					std::list<ImmediateOperand*>(), 
					this->calcArrayBound());
		this->composite.push_back(te);
		this->referredTypes.push_back(node.elementType);
		return;
	    }
	case BASE_TYPE_ARRAY: {
		bool mibackup = this->mungeIndices;

		this->mungeIndices = true;
		node.elementType->accept(*this);
		this->mungeIndices = mibackup;

		return;
	    }
	default:
		break;
	}

	/* not reached */
	assert(false);
}

void
GCTypes::GenTypeElements::visit(PhysicalType &node)
{
	assert(node.constraint != NULL);
	assert(node.baseType == BASE_TYPE_INTEGER);
	this->processDR<ConstInteger>(*node.constraint, &node);
}

void
GCTypes::GenTypeElements::processSIArray(SubtypeIndication &node)
{
	if (node.indexConstraint != NULL) {
		if ((! (this->indexConstraint.empty())) 
		 && (! this->mungeIndices)) {
			// only one index constraint allowed, 
			// lrm 3.2.1.1
			std::string msg = std::string("Index "
				"constraint already defined at ");
			DiscreteRange *f = 
				this->indexConstraint.front();
			msg += util::MiscUtil::toString(f->location);
			CompileError *ce = 
				new CompileError(node, msg);
			ErrorRegistry::addError(ce);
			return;
		}

		// munge indices together
		this->indexConstraint.insert(this->indexConstraint.end(),
				node.indexConstraint->begin(), 
				node.indexConstraint->end());
		this->mungeIndices = false;
	}
}


void
GCTypes::GenTypeElements::reset(void)
{
	// FIXME what about other member? (e.g. mungeIndices)
	this->indexConstraint.clear();
	this->constraint = NULL;
}

std::list<DiscreteRange*>
GCTypes::GenTypeElements::getIndices(void) const
{
	return this->indexConstraint;
}

std::string
GCTypes::GenTypeElements::findName(const TypeDeclaration &node)
{
	// make sure it's an array or record
	switch (node.baseType) {
	case BASE_TYPE_ARRAY:
	case BASE_TYPE_RECORD:
		break;
	default:
		assert(false);
	}

	if (node.name != NULL) {
		assert(! node.pathName.empty());
		return node.getICName();
	}

	// unnamed, so assume that it is a SubtypeIndication
	const SubtypeIndication *si = 
		dynamic_cast<const SubtypeIndication*>(&node);

	if (si == NULL) {
		// FIXME possible that it is an anonymous type?
		assert(false);
	}

	return GCTypes::GenTypeElements::findName(*(si->declaration));
}

std::list<ImmediateOperand*>
GCTypes::GenTypeElements::getInitValue(void) const
{
	std::list<ImmediateOperand*> initVs = std::list<ImmediateOperand*>();

	if (! this->addInit) {
		return initVs;
	}

	if (this->constInit == NULL) {
		// FIXME why does this happen?
		return initVs;
	}

	const ConstInteger *ci = 
		dynamic_cast<const ConstInteger*>(this->constInit);
	if (ci != NULL) {
		initVs.push_back(new ImmediateOperand(ci->value));
		return initVs;
	}

	const ConstArray *ca = 
		dynamic_cast<const ConstArray*>(this->constInit);
	if (ca != NULL) {
		assert(ca->elements != NULL);

		for (std::vector<ConstInteger*>::const_iterator i 
			= ca->elements->begin();
			i != ca->elements->end();
			i++) {

			initVs.push_back(new ImmediateOperand((*i)->value));
		}

		return initVs;
	}

	//FIXME ConstReal
	assert(false);
}

universal_integer
GCTypes::GenTypeElements::calcArrayBound(void) const
{
	return GCTypes::calcArrayBound(this->indexConstraint);
}

/*
 * =====================  GENTYPES  =================
 */
void
GCTypes::GenTypes::visit(EnumerationType &node)
{
	// not a composite, don't create type for this one.
}

void
GCTypes::GenTypes::visit(RangeConstraintType &node)
{
	// not a composite, don't create type for this one.
}

void
GCTypes::GenTypes::visit(PhysicalType &node)
{
	// not a composite, don't create type for this one.
}

void
GCTypes::GenTypes::visit(RecordType &node)
{
	assert(node.elements != NULL);
	assert(node.name != NULL);

	GenTypeElements gte = 
		GenTypeElements(false, 
				"Unconstraint array not allowed in a "
				"record type",
				node,
				NULL);

	for (std::list<RecordTypeElement*>::iterator i = 
		node.elements->begin(); i != node.elements->end(); i++) {

		assert((*i)->subtype != NULL);
		(*i)->subtype->accept(gte);
		gte.reset();
	}

	this->type = TypeFactory::getType(node.getICName(), gte.composite);
}

void
GCTypes::GenTypes::visit(UnconstrainedArrayType &node)
{
	// do nothing for unconstraint arrays, as these can never
	// result in a intermediate code type.
}

void
GCTypes::GenTypes::visit(SubtypeIndication &node)
{
	if (node.name == NULL) {
		// must not be an anonymous type!
		assert(false);
		return;
	}

	switch (node.baseType) {
	case BASE_TYPE_RECORD:
		// FIXME this is allowed by LRM!
		assert(false);
		return;

	case BASE_TYPE_ARRAY:
		break;
	default:
		// not composite -> do nothing.
		// (non-composite types don't need to be reflected
		// in the intermediate code, as these will always 
		// be represented with universal_integer/universal_real)
		return;
	}

	// composite type
	GenTypeElements gte = 
		GenTypeElements(false, 
				"Unconstraint array not allowed in a "
				"composite type",
				node,
				NULL);
	node.accept(gte);
	this->type = TypeFactory::getType(node.getICName(), gte.composite);
}

}; /* namespace ast */
