/* $Id$
 *
 * Print intermediate code to stream.
 *
 * Copyright (C) 2008-2010 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 "intermediate/visitor/PrintCode.hpp"
#include <cassert>
#include "intermediate/opcodes/Connect.hpp"
#include "intermediate/opcodes/Je.hpp"
#include "intermediate/opcodes/Jne.hpp"
#include "intermediate/opcodes/Jb.hpp"
#include "intermediate/opcodes/Jbe.hpp"
#include "intermediate/opcodes/Jmp.hpp"
#include "intermediate/opcodes/Mov.hpp"
#include "intermediate/opcodes/Abort.hpp"
#include "intermediate/opcodes/Add.hpp"
#include "intermediate/opcodes/Sub.hpp"
#include "intermediate/opcodes/IMul.hpp"
#include "intermediate/opcodes/Div.hpp"
#include "intermediate/opcodes/Call.hpp"
#include "intermediate/opcodes/Return.hpp"
#include "intermediate/opcodes/Proc.hpp"
#include "intermediate/opcodes/Update.hpp"
#include "intermediate/opcodes/GetSig.hpp"
#include "intermediate/opcodes/GetSimTime.hpp"
#include "intermediate/opcodes/ROffset.hpp"
#include "intermediate/opcodes/AOffset.hpp"
#include "intermediate/opcodes/Suspend.hpp"
#include "intermediate/opcodes/WakeOn.hpp"
#include "intermediate/opcodes/WakeAt.hpp"
#include "intermediate/opcodes/Log.hpp"
#include "intermediate/opcodes/BeginTransfer.hpp"
#include "intermediate/opcodes/EndTransfer.hpp"
#include "intermediate/opcodes/SetParam.hpp"
#include "intermediate/opcodes/GetParam.hpp"
#include "intermediate/operands/ImmediateOperand.hpp"
#include "intermediate/operands/IndirectOperand.hpp"
#include "intermediate/operands/Reference.hpp"
#include "intermediate/operands/Register.hpp"
#include "intermediate/container/CodeContainer.hpp"
#include "intermediate/container/Label.hpp"
#include "intermediate/container/Data.hpp"
#include "intermediate/container/TypeElement.hpp"
#include "intermediate/container/Type.hpp"

namespace intermediate {

PrintCode::PrintCode(std::ostream &destination) 
	: 	dst(destination)
{
}

void
PrintCode::visit(CodeContainer &node)
{
	this->dst << this->nest << "CONTAINER " << node.name << std::endl;
	this->dst << this->nest << ".REGISTERS " << node.getNumUsedRegs() 
		<< std::endl;

	if (! node.typeDefinitions.empty()) {
		this->dst << this->nest << ".TYPES" << std::endl;
		this->listTraverse(node.typeDefinitions);
	}

	if (! node.transferData.empty()) {
		this->dst << this->nest << ".TRANSFER" << std::endl;
		this->listTraverse(node.transferData);
	}

	if (! node.stackData.empty()) {
		this->dst << this->nest << ".STACK" << std::endl;
		this->listTraverse(node.stackData);
	}

	this->dst << std::endl;
	++this->nest;
	this->listTraverse(node.children);
	--this->nest;

	if (! node.code.empty()) {
		this->dst << this->nest << ".TEXT" << std::endl;
		this->listTraverse(node.code);
	}

	this->dst << this->nest << "END CONTAINER " << node.name << ';' 
		<< std::endl << std::endl;
}

void
PrintCode::visit(ImmediateOperand &node)
{
	switch(node.type) {
	case OP_TYPE_REAL:
		this->dst << '$' << node.rValue << 'F';
		break;
	case OP_TYPE_INTEGER:
		this->dst << '$' << node.iValue << 'L';
		break;
	case OP_TYPE_POINTER:
		// illegal adressing mode: immediate pointer
		assert(false);
		break;
	}
}

void
PrintCode::visit(BeginTransfer &node)
{
	this->dst << this->nest << "\tBEGINTR\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.comp->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(EndTransfer &node)
{
	this->dst << this->nest << "\tENDTR\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.cleanupStack->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(SetParam &node)
{
	this->dst << this->nest << "\tSETPARM\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.container->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(GetParam &node)
{
	this->dst << this->nest << "\tGETPARM\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.container->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Connect &node)
{
	this->dst << this->nest << "\tCONNECT\t";
	node.driver->accept(*this);
	this->dst << ", ";
	node.signal->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Mov &node)
{
	this->dst << this->nest << "\tMOV\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Je &node)
{
	this->dst << this->nest << "\tJE\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", @" << node.trg->name;
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Jbe &node)
{
	this->dst << this->nest << "\tJBE\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", @" << node.trg->name;
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Jne &node)
{
	this->dst << this->nest << "\tJNE\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", @" << node.trg->name;
	this->putAnnotation(node);
	this->dst << std::endl;
}


void
PrintCode::visit(Jb &node)
{
	this->dst << this->nest << "\tJB\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", @" << node.trg->name;
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Jmp &node)
{
	this->dst << this->nest << "\tJMP\t@" << node.trg->name ;
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Label &node)
{
	this->dst << this->nest << node.name << ':';
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Add &node)
{
	this->dst << this->nest << "\tADD\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Abort &node)
{
	this->dst << this->nest << "\tABORT\t";
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Sub &node)
{
	this->dst << this->nest << "\tSUB\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Call &node)
{
	assert(node.dst != NULL);

	this->dst << this->nest << "\tCALL\t";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Return &node)
{
	this->dst << this->nest << "\tRETURN";
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Proc &node)
{
	assert(node.dst != NULL);

	this->dst << this->nest << "\tPROC\t";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Update &node)
{
	this->dst << this->nest << "\tUPDATE\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.delay->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(GetSig &node)
{
	this->dst << this->nest << "\tGETSIG\t";
	node.src->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(GetSimTime &node)
{
	this->dst << this->nest << "\tGETTIME\t";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(IMul &node)
{
	this->dst << this->nest << "\tIMUL\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Div &node)
{
	this->dst << this->nest << "\tDIV\t";
	node.left->accept(*this);
	this->dst << ", ";
	node.right->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(ROffset &node)
{
	this->dst << this->nest << "\tROFFSET\t";
	node.base->accept(*this);
	this->dst << ", ";
	this->dst << node.rtype->name << "->";
	node.offset->accept(*this);
	this->dst << ", ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(AOffset &node)
{
	this->dst << this->nest << "\tAOFFSET\t";
	node.base->accept(*this);
	this->dst << ", " << node.atype->name << "[";
	node.offset->accept(*this);
	this->dst << "], ";
	node.dst->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Suspend &node)
{
	this->dst << this->nest << "\tSUSPEND";
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(WakeOn &node)
{
	this->dst << this->nest << "\tWAKEON\t";
	node.src->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(WakeAt &node)
{
	this->dst << this->nest << "\tWAKEAT\t";
	node.wakeTime->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(Log &node)
{
	this->dst << this->nest << "\tLOG\t";
	node.lvl->accept(*this);
	this->dst << ", ";
	node.c->accept(*this);
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(IndirectOperand &node)
{
	this->putOpType(node.type);
	this->dst << '(';
	node.src->accept(*this);
	this->dst << ')';
}

void
PrintCode::visit(Reference &node)
{
	this->dst << '"' << node.name << '"';
}

void
PrintCode::visit(Register &node)
{
	this->putOpType(node.type);
	this->dst << "%reg_" << node.num;
}

void
PrintCode::visit(Data &node)
{
	this->dst << this->nest << "DATA\t";

	if (node.resolver) {
		this->dst << "RESOLVEDBY "
			<< *node.resolver << " ";
	}

	switch (node.storage) {
	case STORAGE_TYPE_VARIABLE:
		this->dst << "VARIABLE\t";
		break;

	case STORAGE_TYPE_DRIVER:
		this->dst << "DRIVER\t";
		break;

	case STORAGE_TYPE_SIGNAL:
		this->dst << "SIGNAL\t";
		break;
	}

	this->dst << node.name << "\t";
	if (node.dataType != NULL) {
		node.dataType->accept(*this);
	}

	this->dst << ';';
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::visit(TypeElement &node)
{
	this->dst << node.name;
	if (node.ubound != 1) {
		this->dst << '[' << node.ubound << ']';
	}
	
	if (! node.init.empty()) {
		this->dst << " := ";
	}

	for (std::list<ImmediateOperand*>::iterator i = node.init.begin();
		i != node.init.end(); 
		i++) {

		if (i != node.init.begin()) {
			this->dst << " ";
		}
		(*i)->accept(*this);
	}

	this->putAnnotation(node);
}

void
PrintCode::visit(Type &node)
{
	this->dst << this->nest << "TYPE " << node.name << " is " 
		<< std::endl << this->nest << "\t\t";
	for (std::list<TypeElement*>::iterator i = node.elements.begin();
		i != node.elements.end(); i++) {

		if (i != node.elements.begin()) {
			this->dst << ',' << std::endl << this->nest << "\t\t";
		}
		(*i)->accept(*this);
	}

	this->dst << ';';
	this->putAnnotation(node);
	this->dst << std::endl;
}

void
PrintCode::putAnnotation(const Node &node) const
{
	if (node.intAnnotations.empty() && node.strAnnotations.empty()) {
		return;
	}

	this->dst << " { ";
	for (std::list<Node::intAnnoT>::const_iterator i = 
		node.intAnnotations.begin();
		i != node.intAnnotations.end(); i++) {

		this->dst << i->first << "=" << i->second << " ";
	}

	for (std::list<Node::strAnnoT>::const_iterator i = 
		node.strAnnotations.begin();
		i != node.strAnnotations.end(); i++) {

		this->dst << i->first << "=\"" << i->second << "\" ";
	}
	
	this->dst << "}";
}

void
PrintCode::putOpType(const enum OpType t)
{
	switch(t) {
	case OP_TYPE_INTEGER:
		this->dst << 'i';
		break;

	case OP_TYPE_REAL:
		this->dst << 'r';
		break;

	case OP_TYPE_POINTER:
		this->dst << 'p';
		break;

	}
}

std::ostream &
operator <<(std::ostream &stream, const PrintCode::Nesting &n)
{
	n.put(stream);
	return stream;
}

}; /* namespace ast */
