/* $Id$ 
 *
 * DeclarativeRegion: One pocket to store symbols. Can be chained together.
 *
 * Copyright (C) 2007-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 <cassert>
#include <iostream>
#include "frontend/misc/DeclarativeRegion.hpp"
#include "frontend/reporting/DuplicateName.hpp"
#include "frontend/reporting/ErrorRegistry.hpp"

#define DEBUG 0
#define HEAVY_DEBUG 0

namespace ast {

DeclarativeRegion::DeclarativeRegion(
	DeclarativeRegion *p
	) :	parent(p)
{
}

DeclarativeRegion::~DeclarativeRegion()
{
	for (std::list<Symbol*>::const_iterator i = 
		this->declarations.begin();
		i != this->declarations.end(); i++) {

		if ((*i)->region) {
			delete (*i)->region;
		}
		delete *i;
	}
}

void
DeclarativeRegion::registerSymbol(Symbol *sym)
{
	//check for homographs.
	const Symbol *dup = this->containsHomograph(*sym);
	if (dup != NULL) {
		DuplicateName *dn = new DuplicateName(
						sym->declaration,
						dup->declaration,
						*sym->name);

		ErrorRegistry::addError(dn);
		return;
	}

	this->declarations.push_back(sym);
}

const Symbol* 
DeclarativeRegion::containsHomograph(const Symbol &sym) const
{
	for (std::list<Symbol*>::const_iterator i = 
		this->declarations.begin(); i != this->declarations.end();
		i++) {

		if ((*i)->isHomograph(sym)){
			return *i;
		}
	}

	return NULL;
}

bool
DeclarativeRegion::isEnclosingSymbol(const Symbol &sym) const
{
	if (sym.region == NULL) {
		return false;
	}

	if (sym.region == this) {
		return true;
	}

	if (this->parent != NULL) {
		return this->parent->isEnclosingSymbol(sym);
	}

	return false;
}

std::list<Symbol*>
DeclarativeRegion::lookup(const std::string &name) const
{
	std::list<Symbol*> result;

#if DEBUG
	std::cerr << "STARTING LOOKUP for " << name << std::endl;
#endif

	this->lookupRec(name, result);
	return result;
}

std::list<Symbol*>
DeclarativeRegion::lookupLocal(const std::string &name) const
{
	std::list<Symbol*> result = std::list<Symbol*>();

	for (std::list<Symbol*>::const_iterator i = 
		this->declarations.begin();
		i != this->declarations.end(); i++) {

		if ((*(*i)->name) == name) {
			result.push_back(*i);
		}
	}
	return result;
}

void
DeclarativeRegion::lookupRec(
	const std::string &name,
	std::list<Symbol*> &candidates
) const
{
	// lookup in local region
	for (std::list<Symbol*>::const_iterator i = 
			this->declarations.begin();
		i != this->declarations.end(); i++) {
#if HEAVY_DEBUG /* causes extreme debug output */
		std::cerr << "  c>" << (*(*i)->name) << std::endl;
#endif /* HEAVY_DEBUG */
		
		if ((*(*i)->name) == name) {
			// check visibility
			assert(*i);
			if (this->isVisible(**i, candidates)) {
				candidates.push_back(*i);
			}
		}
	}

	//lookup in imported symbols
	for (std::list<Symbol*>::const_iterator i = 
			this->imports.begin();
		i != this->imports.end(); i++) {

#if HEAVY_DEBUG /* causes extreme debug output */
		std::cerr << "  i>" << (*(*i)->name) << std::endl;
#endif /* HEAVY_DEBUG */
		
		if ((*(*i)->name) == name) {
			// check visibility
			assert(*i);
			if (this->isVisible(**i, candidates)) {
				candidates.push_back(*i);
			}
		}
	}

	// traverse to parent Declarative Region.
	if (this->parent) {
#if 0 /* not useful w.o. above output */
		std::cerr << " going up." << std::endl;
#endif /* 0 */
		this->parent->lookupRec(name, candidates);
	}
}

bool
DeclarativeRegion::isVisible(
	const Symbol &sym, 
	const std::list<Symbol*> &check
) const
{
	for (std::list<Symbol*>::const_iterator i = check.begin();
		i != check.end();
		i++) {
	
		assert(*i);
		if (sym.isHomograph(**i)) {
			return false;
		}
	}

	return true;
}


}; /* namespace ast */
