/* $Id$ 
 *
 * NodeFactory: helper scripts for the Parser to create AST nodes, mainly
 *              useful to keep lots of c++ code out of the parser.
 *
 * 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/ast/NodeFactory.hpp"
#include "frontend/ast/SignalDeclaration.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/ast/VarDeclaration.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/WhileLoopStat.hpp"
#include "frontend/ast/SigAssignStat.hpp"
#include "frontend/ast/FunctionCall.hpp"
#include "frontend/reporting/ErrorRegistry.hpp"
#include "frontend/reporting/CompileError.hpp"
#include "frontend/reporting/UndefinedSymbol.hpp"
#include "frontend/misc/SymbolTable.hpp"
#include "frontend/misc/NameLookup.hpp"
#include "frontend/misc/BuiltinFunction.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/visitor/ResolveTypes.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/misc/Compiler.hpp"

namespace ast {

struct NodeFactory::IfHelperS& 
NodeFactory::createNestedElseIfs(
	struct NodeFactory::IfHelperS* remainder,
	Expression* condition,
	std::list<SeqStat*>* thenStats,
	Location loc
) 
{
	struct NodeFactory::IfHelperS* ret = NULL;

	/* no parent elsif statements... create one */
	if (remainder == NULL) {
		ret = new NodeFactory::IfHelperS();
		ret->topstat = new IfStat(condition, thenStats, NULL, loc);
		ret->bottomElse = &(ret->topstat->elseStats);
		return *ret;
	}
	
	/* parent present. attach to bottom else */
	IfStat *bottomIf = new IfStat(condition, thenStats, NULL, loc);
	std::list<SeqStat*>* l = new std::list<SeqStat*>();
	l->push_back(bottomIf);
	
	*(remainder->bottomElse) = l;

	/* set old bottom else to new one */
	remainder->bottomElse = &(bottomIf->elseStats);
	
	return *remainder;
}

IfStat& NodeFactory::createIfStat(
	Expression* condition,
	std::list<SeqStat*>* thenStats,
	struct NodeFactory::IfHelperS* elsIfs,
	std::list<SeqStat*>* elseStats,
	ast::Location loc
)
{
	IfStat* ret = NULL;

	/* trivial case: no eslIfs */
	if (elsIfs == NULL) {
		ret = new IfStat(condition, thenStats, elseStats, loc);
		return *ret;
	}

	/* remap elsIfs to nested if-else (part already done via elseIfs */
	/* sanity checks for parser */
	assert(elsIfs->topstat != NULL);
	assert(*(elsIfs->bottomElse) == NULL);

	/* elseif stats... should go to else part */
	std::list<SeqStat*>* ep = new std::list<SeqStat*>();
	ep->push_back(elsIfs->topstat);

	/* create real ifstat */
	ret = new IfStat(condition, thenStats, ep, loc);

	/* attach else-part to bottom most else-list */
	*(elsIfs->bottomElse) = elseStats;

	/* cleanup */
	delete elsIfs;

	return *ret;
}


std::list<SignalDeclaration*>& 
NodeFactory::makeSignalDecls(
	std::list<std::string*>& ids,
	enum ValDeclaration::Mode mode, 
	bool isBus, 
	Expression* varInit,
	SubtypeIndication* subtypeIndic,
	Location loc
)
{
	std::list<SignalDeclaration*> *v = 
			new std::list<SignalDeclaration*>();

	for (std::list<std::string*>::iterator i = ids.begin();
	     i != ids.end(); i++) {

		SignalDeclaration *s = 
			new SignalDeclaration(*i, mode, isBus, varInit,
					subtypeIndic, loc);
		v->push_back(s);
		assert(subtypeIndic);
	}
	util::MiscUtil::terminate(varInit);
	util::MiscUtil::terminate(subtypeIndic);

	return *v;
}

std::list<ConstantDeclaration*>& 
NodeFactory::makeConstantDecls(
	std::list<std::string*>& ids,
	Expression* varInit,
	SubtypeIndication* subtypeIndic,
	Location loc
)
{
	std::list<ConstantDeclaration*> *v = 
			new std::list<ConstantDeclaration*>();

	for (std::list<std::string*>::iterator i = ids.begin();
	     i != ids.end(); i++) {

		ConstantDeclaration *s = 
			new ConstantDeclaration(*i, varInit, subtypeIndic, 
						true, loc);
		v->push_back(s);
		assert(subtypeIndic);
	}

	util::MiscUtil::terminate(varInit);
	util::MiscUtil::terminate(subtypeIndic);

	return *v;
}

std::list<SymbolDeclaration*>& 
NodeFactory::makeVarDecls(
	std::list<std::string*>& ids,
	Expression* varInit,
	SubtypeIndication* subtypeIndic,
	Location loc
)
{
	std::list<SymbolDeclaration*> *v = 
		new std::list<SymbolDeclaration*>();

	for (std::list<std::string*>::iterator i = ids.begin();
	     i != ids.end(); i++) {

		VarDeclaration *s = new VarDeclaration(
					ValDeclaration::MODE_INOUT,
					*i,
					varInit,
					subtypeIndic,
					loc
					);

		v->push_back(s);
		assert(subtypeIndic);
	}

	util::MiscUtil::terminate(varInit);
	util::MiscUtil::terminate(subtypeIndic);

	return *v;
}

std::list<ValDeclaration*>&
NodeFactory::makeFuncInterfaceDecls(
	std::list<std::string*>& ids,
	Expression* varInit,
	enum ValDeclaration::Mode mode,
	enum ValDeclaration::ObjClass cls,
	SubtypeIndication* subtypeIndic,
	Location loc
)
{
	std::list<ValDeclaration*> *v = new std::list<ValDeclaration*>();
	if (mode != ValDeclaration::MODE_IN) {
		assert(! ids.empty());
		// only in is allowed for functions.
		CompileError *ce = new CompileError(loc, 
				"Wrong parameter mode for argument <"
				+ *ids.front() + ">, setting to IN.");
		ErrorRegistry::addError(ce);
	}

	for (std::list<std::string*>::iterator i = ids.begin();
	     i != ids.end(); i++) {
		ValDeclaration *s = NULL;
		/* no class -> constant
  	           mode always in
		*/
		switch (cls) {
		case ValDeclaration::OBJ_CLASS_VARIABLE: {
			CompileError* ce = new CompileError(loc,
					"Object class VARIABLE not allowed "
					"for Function argument <"
					+ **i + ">, using CONSTANT.");
			ErrorRegistry::addError(ce);
			/* fall through */	
			ATTRIBUTE_FALLTHROUGH
		}
		case ValDeclaration::OBJ_CLASS_UNSPECIFIED:
			ATTRIBUTE_FALLTHROUGH
		case ValDeclaration::OBJ_CLASS_CONSTANT:
			s = new ConstantDeclaration(*i, varInit, 
						subtypeIndic, 
						false, loc);
			break;

		case ValDeclaration::OBJ_CLASS_SIGNAL:
			s = new SignalDeclaration(*i, 
						ValDeclaration::MODE_IN,
						false, 
						varInit,
						subtypeIndic,
						loc
						);
			break;

		}

		v->push_back(s);
		assert(subtypeIndic);
	}
	util::MiscUtil::terminate(varInit);
	util::MiscUtil::terminate(subtypeIndic);

	return *v;
}

std::list<ValDeclaration*>&
NodeFactory::makeProcInterfaceDecls(
	std::list<std::string*>& ids,
	Expression* varInit,
	enum ValDeclaration::Mode mode,
	enum ValDeclaration::ObjClass cls,
	SubtypeIndication* subtypeIndic,
	Location loc
)
{
	std::list<ValDeclaration*> *v = new std::list<ValDeclaration*>();

	for (std::list<std::string*>::iterator i = ids.begin();
	     i != ids.end(); i++) {
		ValDeclaration *s = NULL;
		/* no class -> in -> constant 
                               others -> variable 
		   NOTE: it's unclear what the default mode of an interface
                         element of a procedure is, in case no class is given.
                         FAUhdlc will choose MODE_IN here.
			 cf. IEEE Standard VHDL Language Reference Manual 
                         (ISBN 1-55397-376-8), p. 20
		*/
		switch (cls) {
		case ValDeclaration::OBJ_CLASS_UNSPECIFIED:
			if (mode == ValDeclaration::MODE_IN) {
				s = new ConstantDeclaration(*i, varInit, 
						subtypeIndic, false, loc);
				/* TODO issue warning if mode != MODE_IN */
			} else {
				s = new VarDeclaration(mode, *i, varInit,
							subtypeIndic, loc);
			}
			break;

		case ValDeclaration::OBJ_CLASS_VARIABLE:
			s = new VarDeclaration(mode, *i, varInit, 
						subtypeIndic, loc);
			break;

		case ValDeclaration::OBJ_CLASS_SIGNAL:
			s = new SignalDeclaration(*i, mode, false, varInit, 
						subtypeIndic, loc);
			break;

		case ValDeclaration::OBJ_CLASS_CONSTANT:
			s = new ConstantDeclaration(*i, varInit, 
						subtypeIndic, false, loc);
			switch (mode) {
			case ValDeclaration::MODE_IN:
				break;

			default: {
				CompileError *ce = new CompileError(loc, 
						"Object class constant for <"
						+ **i + "> must "
						"be of mode IN.");
				ErrorRegistry::addError(ce);
			    }
			}
			break;

		}

		assert(subtypeIndic);
		v->push_back(s);
	}

	util::MiscUtil::terminate(varInit);
	util::MiscUtil::terminate(subtypeIndic);
	
	return *v;
}


struct NodeFactory::ContextItemS&
NodeFactory::mergeContextItems(
	struct NodeFactory::ContextItemS& first, 
	struct NodeFactory::ContextItemS& second
)
{
	if (first.libClauses && second.libClauses) {
		listCombine(first.libClauses, second.libClauses);
		delete second.libClauses;
	}

	if (first.libClauses == NULL) {
		first.libClauses = second.libClauses;
	}

	if (first.useClauses && second.useClauses) {
		listCombine(first.useClauses, second.useClauses);
		delete second.useClauses;
	}
	
	if (first.useClauses == NULL) {
		first.useClauses = second.useClauses;
	}

	delete &second;

	return first;
}

CondalSigAssign&
NodeFactory::makeCondalSigAssign(Name& trg, SeqStat& conds, Location loc)
{
	CondalSigAssign& ret = *(new CondalSigAssign(&trg, &conds, loc));
	NodeFactory::CondalSigAssignSetter setter = 
		NodeFactory::CondalSigAssignSetter(trg);
	ret.accept(setter);
	return ret;
}


void
NodeFactory::CondalSigAssignSetter::visit(SigAssignStat &node)
{
	assert(node.target == NULL); // parser is wrong otherwise.
	node.target = &(this->target);
	/* _don't_ traverse here. (as it's not of our concern below.) */
}

std::list<RecordTypeElement*>&
NodeFactory::makeRecordTypeElements(
	std::list<std::string*> &idlist,
	SubtypeIndication &subtype,
	Location loc
)
{
	std::list<RecordTypeElement*> *result = 
			new std::list<RecordTypeElement*>();

	for (std::list<std::string*>::iterator i = idlist.begin();
		i != idlist.end(); i++) {

		result->push_back(
			new RecordTypeElement(*i, &subtype, loc));
	}

	SubtypeIndication* si = &subtype;
	util::MiscUtil::terminate(si);

	return *result;
}

Aggregate*
NodeFactory::makeAggregateFromString(
	const std::string &stringLit,
	Location loc,
	const NameLookup &nl
)
{
	std::list<ElementAssociation*> *elements = 
			new std::list<ElementAssociation*>();
	
	for (std::string::const_iterator i = stringLit.begin(); 
		i != stringLit.end(); i++) {

		std::string *s = new std::string("'");
		s->append(std::string(1, *i));
		s->append("'");

		std::list<Symbol*> cands = nl.lookup(*s);
		if (cands.empty()) {
			UndefinedSymbol *us = new UndefinedSymbol(*s, loc);
			ErrorRegistry::addError(us);
		}

		FunctionCall *fc = 
			new FunctionCall(
				new SimpleName(
					s,
					cands,
					loc),
				new std::list<AssociationElement*>(),
				loc);

		ElementAssociation* assoc = 
			new ElementAssociation(NULL, fc, loc);
		elements->push_back(assoc);
	}

	return new Aggregate(elements, loc);
}


void
NodeFactory::registerEnumElems(
	const EnumerationType *e,
	SymbolTable &symbolTable,
	Symbol *typeSym
)
{
	assert(e);
	assert(e->elements);

	unsigned int cnt = 0;
	for (std::list<FunctionDeclaration*>::const_iterator i = 
		e->elements->begin(); 
		i != e->elements->end();
		i++) {

		//register the corresponding function for enumeration
		//elements.
		SimpleName *sn = new SimpleName(new std::string(*e->name),
						e->location);
		sn->candidates.push_back(typeSym);
		SubtypeIndication *si = new SubtypeIndication(sn,
							(*i)->location);

		(*i)->returnType = si;
		(*i)->isBuiltin = true;
		(*i)->builtin = new ReturnValue(cnt, e);

		symbolTable.registerSymbol(SYMBOL_FUNCTION, **i);
		cnt++;
	}
}

NodeFactory::TypeDeclHelper::TypeDeclHelper(
	std::list<DiscreteRange*> *indexConstraint,
	SubtypeIndication *elementType,
	Location loc,
	SymbolTable &s
) :		enumType(NULL),
		typeDecl(NULL),
		conArrayBase(NULL),
		wasRecordType(false)
{

	std::list<TypeDeclaration*> *l = 
				new std::list<TypeDeclaration*>();

	for (std::list<DiscreteRange*>::const_iterator i = 
		indexConstraint->begin(); i != indexConstraint->end(); i++) {
		// FIXME this is not 100% correct:
		// in case the result is a universal type, it should get
		// converted to integer.
		// For now, just assume that it's a universal type,
		// and require integer as a result.
		//
		// One problem is, that the type resolution is non-const in
		// itself. Maybe it should be rewritten in a const part,
		// and in a non-const part.
		ResolveTypes r = ResolveTypes(s);
		TypeDeclaration *stdInt = s.getStdStandardType("integer");
		assert(stdInt != NULL);
		r.typeCandidates.insert(stdInt);
		(*i)->accept(r);

		if ((*i)->type == NULL) {
			// type error occured, bail out early
			util::MiscUtil::lterminate(l);
			return;
		}

		l->push_back((*i)->type);
	}
	
	// create base type: anonymous unconstrained array.
	std::string n = s.getMangledPathName() + "__constraint_base__";
	this->conArrayBase = new UnconstrainedArrayType(
						new std::string(n),
						l,
						elementType,
						loc);
	
	SubtypeIndication *t = new SubtypeIndication(this->conArrayBase, loc);
	t->indexConstraint = indexConstraint;
	this->typeDecl = t;
}

void
NodeFactory::TypeDeclHelper::registerType(
	SymbolTable &symTab,
	NodeFactory::Identifier *id
) const
{
	assert(id);
	assert(id->identifier);

	if (this->enumType) {
		assert(this->typeDecl == NULL);
		this->enumType->name = id->identifier;
		Symbol *sym = symTab.registerSymbol(SYMBOL_TYPE, 
							*this->enumType);
		NodeFactory::registerEnumElems(this->enumType, symTab, sym);
		delete id;
		return;
	}

	// no enumeration type
	assert(this->enumType == NULL);
	if (this->typeDecl == NULL) {
		// type error must have been registered
		assert(false); // FIXME for debugging.
		return;
	}

	this->typeDecl->name = id->identifier;
	delete id;

	if (this->wasRecordType) {
		symTab.lateRegisterAttach(SYMBOL_TYPE, *this->typeDecl);
		// leave scope of record type
		symTab.popRegion();
		return;
	}

	// base type for constrained array present?
	if (this->conArrayBase != NULL) {
		symTab.registerSymbol(SYMBOL_TYPE, *this->conArrayBase);
	}

	// fall through: neither a record type, nor an enumeration type
	symTab.registerSymbol(SYMBOL_TYPE, *this->typeDecl);
}

std::list<SymbolDeclaration*>*
NodeFactory::TypeDeclHelper::flatten(void) const
{
	std::list<SymbolDeclaration*> *ret = 
		new std::list<SymbolDeclaration*>();

	if (this->enumType) {
		ret->push_back(this->enumType);
		return ret;
	}

	if (this->conArrayBase != NULL) {
		ret->push_back(this->conArrayBase);
	}

	if (this->typeDecl == NULL) {
		// type error must have been registered.
		return ret;
	}
	ret->push_back(this->typeDecl);
	return ret;
}

AttributeSpecification *
NodeFactory::handleAttributeSpecs(
	SimpleName *designator,
	std::list<SimpleName *> entityList,
	enum entityClassE classFilter,
	Expression *init,
	const SymbolTable &symTab
)
{
	// find out attribute by designator.
	for (std::list<Symbol *>::iterator i = designator->candidates.begin();
		i != designator->candidates.end(); /* nothing */) {

		switch ((*i)->type) {
		case SYMBOL_ATTRIBUTE:
			i++;
			break;

		default:
			i = designator->candidates.erase(i);
		}
	}

	if (designator->candidates.size() != 1) {
		std::string s = "<" + *designator->name + "> does not refer"
			" to an Attribute.";
		CompileError *ce = new CompileError(*designator, s);
		ErrorRegistry::addError(ce);
		return NULL;
	}

	AttributeDeclaration *decl = 
		dynamic_cast<AttributeDeclaration *>(
			&designator->candidates.front()->declaration);
	assert(decl != NULL);

	AttributeSpecification *spec = 
		new AttributeSpecification(init, decl, designator->location);

	// attribute every referred to symbol that matches classFilter
	for (std::list<SimpleName*>::const_iterator i = entityList.begin(); 
		i != entityList.end(); i++) {

		assert((*i)->candidates.empty());
		assert((*i)->name != NULL);
		std::list<Symbol*> syms = symTab.lookup(*(*i)->name);

		if (syms.empty()) {
			UndefinedSymbol *us = 
				new UndefinedSymbol(*(*i)->name,
							(*i)->location);
			ErrorRegistry::addError(us);
			continue;
		}
		
		for (std::list<Symbol *>::const_iterator j = syms.begin(); 
			j != syms.end(); j++) {

			if (NodeFactory::isOfEntityClass(**j, classFilter)) {
				AttributableDeclaration *ad =
					dynamic_cast<AttributableDeclaration*>(
							&(*j)->declaration);
				assert(ad != NULL);
				// FIXME check if attribute already present
				// and report error if so.
				ad->attributes[*decl->name] = spec;
			} else {
				// FIXME if no "others"/"all" is present,
				// report an error (LRM 5.1)
			}
		}
	}

	return spec;
}

bool
NodeFactory::isOfEntityClass(const Symbol &sym, enum entityClassE ec)
{
	switch (ec) {
	case EC_ENTITY:
		switch (sym.type) {
		case SYMBOL_ENTITY:
			return true;

		default:
			return false;
		}
		/* not reached */

	case EC_ARCHITECTURE:
		switch (sym.type) {
		case SYMBOL_ARCHITECTURE:
			return true;

		default:
			return false;
		}
		/* not reached */

	case EC_CONFIGURATION:
		assert(false);
		/* not reached */
	
	case EC_PROCEDURE:
		switch (sym.type) {
		case SYMBOL_PROCEDURE:
			return true;

		default:
			return false;
		}
		/* not reached */

	case EC_FUNCTION:
		switch (sym.type) {
		case SYMBOL_FUNCTION:
			return true;

		default:
			return false;
		}
		/* not reached */

	case EC_PACKAGE:
		switch (sym.type) {
		case SYMBOL_PACKAGE:
			return true;

		default:
			return false;
		}
		/* not reached */

	case EC_TYPE:
		switch (sym.type) {
		case SYMBOL_TYPE: {
			/* must not be a subtype */
			SubtypeIndication *si = 
				dynamic_cast<SubtypeIndication*>(
							&sym.declaration);
			return si == NULL;
		    }
		default:
			return false;
		}
		/* not reached */

	case EC_SUBTYPE:
		switch (sym.type) {
		case SYMBOL_TYPE: {
			/* must be a subtype */
			SubtypeIndication *si = 
				dynamic_cast<SubtypeIndication*>(
							&sym.declaration);
			return si != NULL;
		    }
		default:
			return false;
		}
		/* not reached */

	case EC_CONSTANT:
		switch (sym.type) {
		case SYMBOL_PARAMETER:
		case SYMBOL_VARIABLE: {
			ValDeclaration *vd = 
				dynamic_cast<ValDeclaration*>(
							&sym.declaration);
			assert(vd != NULL);
			switch (vd->storageClass) {
			case ValDeclaration::OBJ_CLASS_CONSTANT:
				return true;

			default:
				return false;
			}
			/* not reached */
		    }
		default:
			return false;
		}
		/* not reached */

	case EC_SIGNAL:
		switch (sym.type) {
		case SYMBOL_PORT:
		case SYMBOL_PARAMETER:
		case SYMBOL_SIGNAL: {
			ValDeclaration *vd = 
				dynamic_cast<ValDeclaration*>(
							&sym.declaration);
			assert(vd != NULL);
			switch (vd->storageClass) {
			case ValDeclaration::OBJ_CLASS_SIGNAL:
				return true;

			default:
				return false;
			}
			/* not reached */
		    }
		default:
			return false;
		}
		/* not reached */

	case EC_VARIABLE:
		switch (sym.type) {
		case SYMBOL_PARAMETER:
		case SYMBOL_VARIABLE: {
			ValDeclaration *vd = 
				dynamic_cast<ValDeclaration*>(
							&sym.declaration);
			assert(vd != NULL);
			switch (vd->storageClass) {
			case ValDeclaration::OBJ_CLASS_VARIABLE:
				return true;

			default:
				return false;
			}
			/* not reached */
		    }
		default:
			return false;
		}
		/* not reached */

	case EC_COMPONENT:
	case EC_LABEL:
	case EC_LITERAL:
	case EC_UNITS:
	case EC_GROUP:
	case EC_FILE:
		assert(false);
		/* not reached */	
	}

	/* not reached */
	return false;
}

}; /* namespace ast */
