/* $Id$
 *
 * Resolve ranges of positional and named aggregates, where the type
 * is an unconstraint array.
 *
 * Copyright (C) 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.
 */

#if 1 /* that is obsolete... use <cstdint> instead. */
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif /* __STDC_LIMIT_MACROS */
extern "C" {
#include "stdint.h"
};
#endif

#include "frontend/visitor/UnconstraintBounds.hpp"
#include "frontend/ast/Aggregate.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/visitor/ConstantPropagation.hpp"
#include "frontend/visitor/ResolveTypes.hpp"

namespace ast {

UnconstraintBounds::UnconstraintBounds() : bounds(NULL),
					low(INT64_MAX), 
					high(INT64_MIN),
					numElements(0),
					positional(true) 
{
}


void
UnconstraintBounds::visit(Aggregate &node)
{
	// first fold constants (to reduce locally static expressions
	// in choices)
#if 0 /* FIXME */
	ConstantPropagation cp;
	node.accept(cp);
#endif
	assert(node.associations != NULL);
	this->listTraverse(*node.associations);

	if (this->positional) {
		// lrm 7.3.2.2: need to determine bounds
		// by base type.
		std::list<DiscreteRange*> drl;

		const UnconstrainedArrayType *ua =
			ResolveTypes::pickupIndexConstraint(node.type, drl);

		// constraint mustn't be set yet by type.
		assert(drl.empty());
		DiscreteRange *dr = UnconstraintBounds::findIndexRange(ua);

		// FIXME downto...
		switch (dr->direction) {
		case DiscreteRange::DIRECTION_DOWN:
			assert(false);
			break;

		default:
			break;
		}

		this->low = dr->getLowerBound();
		this->high = this->low - 1 + this->numElements;

		// check for overflow of NULL array
		if (this->numElements == 0) {
			assert(this->high < this->low);
		}
	}

	ConstInteger *lowBound = new ConstInteger(this->low, node.location);
	ConstInteger *upBound = new ConstInteger(this->high, node.location);
	this->bounds = new DiscreteRange(
				lowBound, 
				upBound,
				DiscreteRange::DIRECTION_UP,
				node.location);
}

void
UnconstraintBounds::visit(ElementAssociation &node)
{
	if (node.choices != NULL) {
		// named association
		this->positional = false;
		this->listTraverse(*node.choices);
	} else {
		this->numElements++;
	}
}

void
UnconstraintBounds::visit(Others &node)
{
	//TODO register error
	assert(false);
}

void
UnconstraintBounds::visit(ConstInteger &node)
{
	// must have come from a formal choice.
	if (node.value < this->low) {
		this->low = node.value;
	}

	if (this->high < node.value) {
		this->high = node.value;
	}
}

void
UnconstraintBounds::visit(DiscreteRange &node)
{
	assert(node.from != NULL);
	assert(node.to != NULL);

	node.from->accept(*this);
	node.to->accept(*this);
}

void
UnconstraintBounds::process(AstNode &node)
{
	// FIXME register error? or is it a logic error?
	assert(false);
}

DiscreteRange *
UnconstraintBounds::findIndexRange(const UnconstrainedArrayType *at)
{
	// FIXME specification not yet clear to me about 
	// multidimensional arrays... ignore for now.
	assert(at->indexTypes->size() == 1);

	const TypeDeclaration *it = at->indexTypes->front();
	assert(it != NULL);

	/* try to determine constraint from SubtypeIndication */
	const SubtypeIndication *sit = 
		dynamic_cast<const SubtypeIndication*>(it);
	
	while (sit->constraint == NULL) {
		const SubtypeIndication *sit2 = 
				dynamic_cast<const SubtypeIndication*>(
							sit->declaration);
		if (sit2 == NULL) {
			break;
		}
		sit = sit2;
	}
	if (sit->constraint != NULL) {
		return sit->constraint;
	}

	// not a subtype
	const RangeConstraintType *rt = 
		dynamic_cast<const RangeConstraintType*>(sit->declaration);

	assert(rt != NULL);
	assert(rt->constraint != NULL);
	return rt->constraint;
}

}; /* namespace ast */
