/* $Id$
 *
 * Symbol: A symbol refers to a named entity.
 *
 * 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 "frontend/misc/Symbol.hpp"
#include "frontend/misc/DeclarativeRegion.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/visitor/ResolveTypes.hpp"

namespace ast {

/*
 * same name and:
 * 	<= 1 is overloadable -> true
 * 	2 is overloadable and 
 * 		same parameter/result type profile -> true
 * 	falltrhough: false
 */
bool
Symbol::isHomograph(const Symbol& other) const {
	if (*(this->name) != *(other.name)) {
		return false;
	}

	unsigned int overloadable = SYMBOL_FUNCTION | SYMBOL_PROCEDURE;

	if ((this->type | other.type | overloadable) != overloadable) {
		//at most one is overloadable
		//i.o.w: at least on is not overloadable
		return true;
	}

	//both are overloadable
	//are both procedures?
	if ((this->type | other.type) == SYMBOL_PROCEDURE) {
		return this->sameParameterProfile(other);
	}

	//is only one a procedure?
	if (((this->type | other.type) & SYMBOL_PROCEDURE) != 0) {
		// cannot match on result type profile -> not homographic
		return false;
	}

	//are both functions?
	if ((this->type | other.type) == SYMBOL_FUNCTION) {
		bool ret = this->sameResultProfile(other);
		ret = ret && this->sameParameterProfile(other);
		return ret;
	}

	// not overloadable: -> homographs.
	return false;
}

bool
Symbol::sameParameterProfile(const Symbol& other) const
{
	Callable* c1 = dynamic_cast<Callable*>(&this->declaration);
	Callable* c2 = dynamic_cast<Callable*>(&other.declaration);
	assert(c1);
	assert(c2);

	if (c1->arguments == NULL) {
		return c2->arguments == NULL;
	}

	if (c2->arguments == NULL) {
		return false;
	}

	if (c1->arguments->size() != c2->arguments->size()) {
		return false;
	}
	
	std::list<ValDeclaration*>::const_iterator i = c1->arguments->begin();
	std::list<ValDeclaration*>::const_iterator j = c2->arguments->begin();

	// check base type of subtype of both
	while ((i != c1->arguments->end()) && (j != c2->arguments->end())) {
		assert((*i)->subtypeIndic);
		assert((*j)->subtypeIndic);

		if (! this->baseTypeEqual(
			*(*i)->subtypeIndic,
			*(*j)->subtypeIndic)) {

			return false;
		}
		
		i++; j++;
	}
	// same number of parameters?
	return true;
}

bool
Symbol::sameResultProfile(const Symbol& other) const
{
	FunctionDeclaration *f1 = 
		dynamic_cast<FunctionDeclaration*>(&this->declaration);
	FunctionDeclaration *f2 = 
		dynamic_cast<FunctionDeclaration*>(&other.declaration);

	assert(f1);
	assert(f2);

	// there must have been a compile error, otherwise the
	// returnType s must have been set.
	if (f1->returnType == NULL) {
		return false;
	}

	if (f2->returnType == NULL) {
		return false;
	}

	return this->baseTypeEqual(*(f1->returnType), *(f2->returnType));
}

bool
Symbol::baseTypeEqual(
	const SubtypeIndication& s1,
	const SubtypeIndication& s2
) 
{
	// must have thrown an error during resolving.
	if (s1.declaration == NULL) {
		return false;
	}
	// must have thrown an error during resolving.
	if (s2.declaration == NULL) {
		return false;
	}

	return ResolveTypes::baseTypeEqual(*s1.declaration,
					   *s2.declaration);
}

void 
Symbol::put(std::ostream& stream) const
{
	stream << *name << ": ";
	switch(this->type) {
	case SYMBOL_ENTITY:
		stream << "Entity";
		break;
	case SYMBOL_PROCEDURE:
		stream << "Procedure";
		break;
	case SYMBOL_FUNCTION:
		stream << "Function";
		break;
	case SYMBOL_SIGNAL:
		stream << "Signal";
		break;
	case SYMBOL_VARIABLE:
		stream << "Variable/Constant";
		break;
	case SYMBOL_TYPE:
		stream << "Type";
		break;
	case SYMBOL_UNIT:
		stream << "Physical Unit";
		break;
	case SYMBOL_ELEMENT:
		stream << "Record element";
		break;
	case SYMBOL_ATTRIBUTE:
		stream << "Attribute";
		break;
	case SYMBOL_ARCHITECTURE:
		stream << "Architecture";
		break;
	case SYMBOL_PROCESS:
		stream << "Process";
		break;
	case SYMBOL_LOOP:
		stream << "Loop";
		break;
	case SYMBOL_LIBRARY:
		stream << "Library";
		break;
	case SYMBOL_PACKAGE:
		stream << "Package";
		break;
	case SYMBOL_ALL:
		stream << "ALL token";
		break;
	case SYMBOL_PARAMETER:
		stream << "Parameter";
		break;
	case SYMBOL_PORT:
		stream << "Port";
		break;
	}
}


std::ostream& operator<<(std::ostream& stream, const Symbol& sym)
{
	sym.put(stream);
	return stream;
}

}; /* namespace ast */

