/* $Id$ 
 *
 * For different symbol bearing nodes, determine the full vhdl path name.
 *
 * 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/SetPathName.hpp"
#include <cassert>
#include "frontend/visitor/ResolveTypes.hpp"
#include "frontend/ast/Library.hpp"
#include "frontend/ast/Entity.hpp"
#include "frontend/ast/Architecture.hpp"
#include "frontend/ast/Package.hpp"
#include "frontend/ast/Process.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/ast/ProcedureDeclaration.hpp"
#include "util/MiscUtil.hpp"

namespace ast {

SetPathName::SetPathName() : anonCounter(0) 
{
}

void
SetPathName::visit(Library &node)
{
	this->currentPath.push_back(*node.name);
	this->listTraverse(node.units);
	this->currentPath.pop_back();
}

void
SetPathName::visit(Entity &node)
{
	//FIXME? LRM, p202 doesn't show the library path name?
	this->currentPath.push_back(*node.name);
	if (node.generics != NULL) {
		this->listTraverse(*node.generics);
	}

	if (node.ports != NULL) {
		this->listTraverse(*node.ports);
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}

	this->currentPath.pop_back();
	this->process(node);
}

void
SetPathName::visit(Architecture &node)
{
	assert(node.entity != NULL);
	this->currentPath.push_back(*node.entity->name);

	//skip use and library clauses
	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}

	//FIXME two architectures for same entity not handled.
	this->anonCounter = 0;

	if (node.concurrentStats != NULL) {
		this->listTraverse(*node.concurrentStats);
	}

	this->currentPath.pop_back();
	this->process(node);
}

void
SetPathName::visit(Package &node)
{
	assert(node.name != NULL);
	this->currentPath.push_back(*node.name);

	TopDownVisitor::visit(node);

	this->currentPath.pop_back();
	this->process(node);
}

void
SetPathName::visit(Process &node)
{
	if (node.name != NULL) {
		this->process(node);
		this->currentPath.push_back(*node.name);
	} else {
		std::string anonName = "__anonymous__" 
			+ util::MiscUtil::toString(this->anonCounter);

		this->anonCounter++;
		this->currentPath.push_back(anonName);
		node.pathName = this->getPath();
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}

	if (node.seqStats != NULL) {
		this->listTraverse(*node.seqStats);
	}

	this->currentPath.pop_back();
}

void
SetPathName::visit(FunctionDeclaration &node)
{
	assert(node.returnType != NULL);
	const TypeDeclaration *returnType = 
		ResolveTypes::findBaseType(node.returnType);

	assert(returnType != NULL);
	
	std::string suffix = "return " + *(returnType->name);
	this->processCallable(node, &suffix);
}

void
SetPathName::visit(ProcedureDeclaration &node)
{
	this->processCallable(node, NULL);
}

void
SetPathName::process(SymbolDeclaration &node)
{
	if (node.name != NULL) {
		node.pathName = this->getPath() + *node.name + ':';
	}
}


void
SetPathName::processCallable(Callable &node, const std::string *pSuffix)
{
	std::string argPathName = *node.name;

	assert(node.arguments != NULL);
	argPathName += "[";

	for (std::list<ValDeclaration*>::const_iterator i = 
		node.arguments->begin(); i != node.arguments->end(); i++) {

		if (i != node.arguments->begin()) {
			argPathName += ",";
		}
		
		const TypeDeclaration *baseType = 
			ResolveTypes::findBaseType((*i)->subtypeIndic);
		argPathName += *baseType->name;
	}

	if (! (node.arguments->empty() || (pSuffix == NULL))) {
		argPathName += ' ';
	}

	if (pSuffix != NULL) {
		argPathName += (*pSuffix);
	}
	argPathName += "]";

	this->currentPath.push_back(argPathName);
	node.pathName = this->getPath();

	this->listTraverse(*node.arguments);
	if (node.definition != NULL) {
		node.definition->accept(*this);
	}
	this->currentPath.pop_back();
}


std::string
SetPathName::getPath(void) const
{
	std::string ret;

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

		ret += ":";
		ret += *i;
	}

	ret += ":";
	return ret;
}

}; /* namespace ast */
