/**
  *
  *                      OOAS Compiler
  *
  *       Copyright 2015, AIT Austrian Institute of Technology.
  * This code is based on the C# Version of the OOAS Compiler, which is
  * copyright 2015 by the Institute of Software Technology, Graz University
  * of Technology with portions copyright by the AIT Austrian Institute of
  * Technology. All rights reserved.
  *
  * SEE THE "LICENSE" FILE FOR THE TERMS UNDER WHICH THIS FILE IS PROVIDED.
  *
  * If you modify the file please update the list of contributors below to in-
  * clude your name. Please also stick to the coding convention of using TABs
  * to do the basic (block-level) indentation and spaces for anything after
  * that. (Enable the display of special chars and it should be pretty obvious
  * what this means.) Also, remove all trailing whitespace.
  *
  * Contributors:
  *               Willibald Krenn (AIT)
  *               Stephan Zimmerer (AIT)
  *               Markus Demetz (AIT)
  *               Christoph Czurda (AIT)
  *
  */


package org.momut.ooas.codegen.cadp;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Iterator;

import org.momut.ooas.ast.expressions.CallExpression;
import org.momut.ooas.ast.expressions.Expression;
import org.momut.ooas.ast.expressions.ExpressionKind;
import org.momut.ooas.ast.expressions.IdentifierExpression;
import org.momut.ooas.ast.expressions.TernaryOperator;
import org.momut.ooas.ast.identifiers.FunctionIdentifier;
import org.momut.ooas.ast.identifiers.Identifier;
import org.momut.ooas.ast.identifiers.ParameterIdentifier;
import org.momut.ooas.ast.statements.AbortStatement;
import org.momut.ooas.ast.statements.Assignment;
import org.momut.ooas.ast.statements.Call;
import org.momut.ooas.ast.statements.GuardedCommand;
import org.momut.ooas.ast.statements.KillStatement;
import org.momut.ooas.ast.statements.NondetBlock;
import org.momut.ooas.ast.statements.PrioBlock;
import org.momut.ooas.ast.statements.SeqBlock;
import org.momut.ooas.ast.statements.SkipStatement;
import org.momut.ooas.ast.statements.Statement;
import org.momut.ooas.ast.types.FloatType;
import org.momut.ooas.ast.types.FunctionType;
import org.momut.ooas.ast.types.IntType;
import org.momut.ooas.ast.types.ListType;
import org.momut.ooas.ast.types.Type;
import org.momut.ooas.codegen.OoasCodeEmitter;
import org.momut.ooas.visitors.OoaPrintVisitor;
import org.momut.ooas.visitors.OoaStatementVisitor;

/// <summary>
/// Knows how to construct a C-CADP statement from an AST-statement
/// (dumps out, e.g., named actions)
/// </summary>
public final class CadpStatement extends OoaStatementVisitor
{
	public final OoasCodeEmitter m_emitter = new OoasCodeEmitter();
	private final CadpActionInfo m_procInfo;
	private int m_statePosCount = 1;
	private static int m_debugPosCount = -1;
	private int m_blockCount;

	public int nondetAssignmentsToClose = 0;
	public int blockCount() { return m_blockCount; }

	public static String CallHelper(FunctionIdentifier fun, String helperName)
	{
		final StringBuilder paramStr = new StringBuilder("worklist");
		for (final ParameterIdentifier param : fun.parameter())
		{
			paramStr.append(", ");
			paramStr.append(String.format("%1$s", CadpIdentifier.GetIdentifierString(param)));
		}
		if (((FunctionType)fun.type()).returnType() != null)
		{
			paramStr.append(", ");
			paramStr.append(String.format("param_result"));
		}
		return String.format("%1$s(%2$s)", helperName, paramStr.toString());
	}

	public static String HelperFunctionHeader(FunctionIdentifier fun, String functionName)
	{
		final StringBuilder paramStr = new StringBuilder("struct STATE_LIST* worklist");
		for (final ParameterIdentifier param: fun.parameter())
		{
			//CadpType.EmitType(param.type);
			paramStr.append(", ");
			paramStr.append(String.format("%1$s %2$s", OoaCADPVisitor.DumpType(param.type()), CadpIdentifier.GetIdentifierString(param)));
		}
		if (((FunctionType)fun.type()).returnType() != null)
		{
			//CadpType.EmitType(((FunctionType)fun.type).returnType);
			paramStr.append(", ");
			paramStr.append(String.format("%1$s *param_result", OoaCADPVisitor.DumpType(((FunctionType)fun.type()).returnType())));
		}
		return String.format("struct STATE_LIST* %1$s (%2$s)", functionName, paramStr.toString());
	}


	@Override
	public  void visit(NondetBlock nondetBlock)
	{
		m_blockCount++;
		final int nondetBlocknum = m_blockCount;
		m_emitter.AppendLine("{ /* begin nodet-choice block */");

		m_emitter.AppendLine(String.format("struct STATE_LIST *nondetInitial_%1$s = %2$s;", nondetBlocknum, m_procInfo.toDoListString));
		m_emitter.AppendLine(String.format("struct STATE_LIST *nondetFinal_%1$s = NULL;", nondetBlocknum));
		for (final Statement s : nondetBlock.statements())
		{
			s.Accept(this);
			m_emitter.AppendLine("/* nondet-choice block: save result and reset initial states for next choice */");
			m_emitter.AppendLine(String.format("LIST_INSERT_LIST(%3$s,%4$s); %1$s = %2$s; %4$s = NULL;",
					m_procInfo.toDoListString,
					String.format("nondetInitial_%1$s", nondetBlocknum),
					String.format("nondetFinal_%1$s", nondetBlocknum),
					m_procInfo.doneListString));
		}
		m_emitter.AppendLine("");
		m_emitter.AppendLine(String.format("%1$s = %2$s; /* nondet-choice block end: save all reached final states */",
				m_procInfo.doneListString, String.format("nondetFinal_%1$s", nondetBlocknum)));
		m_emitter.AppendLine("} /* end nodet-choice block */");
	}

	@Override
	public  void visit(SeqBlock seqBlock)
	{
		int loopcount = 0;
		final boolean haveNondeterminism = seqBlock.symbols().symbolList().size() > 0;
		final boolean splittingEnabled = m_procInfo.splitBlockSize > 0;

		final int blockNum = haveNondeterminism ? m_blockCount++ : m_blockCount;

		m_emitter.AppendLineIncIndent("{ /* begin seqblock */");

		if (haveNondeterminism)
		{
			m_blockCount++;
			m_emitter.AppendLine(String.format("struct STATE_LIST *seqInitial_%1$s = %2$s;", blockNum, m_procInfo.toDoListString));
			m_emitter.AppendLine(String.format("struct STATE_LIST *seqFinal_%1$s = NULL;", blockNum));
			for (final Identifier sym :seqBlock.symbols().symbolList())
			{
				CadpType.EmitType(sym.type());
				final CadpType atype = new CadpType();
				sym.type().Accept(atype);
				final CadpIdentifier anIdent = new CadpIdentifier(OoaCADPVisitor.StateVariablePrefix);
				sym.Accept(anIdent);
				m_emitter.AppendLine("/* local (free) variables of seq block */");

				m_emitter.AppendLine(String.format("      long long iterator_%1$s;", anIdent.toString()));
				m_emitter.AppendLine(String.format("      for (iterator_%1$s = %2$s; iterator_%1$s <= %3$s; iterator_%1$s++) {",
						anIdent.toString(), CadpType.GetMinVal(sym.type()), CadpType.GetMaxVal(sym.type())));
				m_emitter.AppendLine(String.format("      %1$s %2$s = %3$s%4$s(iterator_%2$s);",
						atype.ToString(), anIdent.toString(), CadpType.enumString, CadpIdentifier.GetIdentifierString(sym.type().identifier())));


				loopcount++;
			}

			if (!(seqBlock.filter() == null))
				throw new UnsupportedOperationException();
		}

		if (!splittingEnabled || seqBlock.statements().size() < m_procInfo.splitBlockSize)
		{
			// no splitting, write out to emitter
			int i = 0;
			for (final Statement s : seqBlock.statements())
			{
				if (i != 0)
				{
					m_emitter.AppendLine("/* sequential composition: all states reached need to go into todo list */");
					m_emitter.AppendLine(String.format("%1$s = %2$s; %2$s = NULL;", m_procInfo.toDoListString, m_procInfo.doneListString));
				}
				i++;
				s.Accept(this);
			}
		}
		else
		{
			// splitting, we will create functions that take at max. m_procInfo.splitBlockSize number of children
			int splitCount = 0;
			final Iterator<Statement> enumerator = seqBlock.statements().iterator();
			for (int elemCntr = 0; elemCntr < seqBlock.statements().size(); elemCntr++)
			{
				final CadpStatement stmnt = new CadpStatement(this);
				final Statement currentStatement = enumerator.next();
				currentStatement.Accept(stmnt);
				final boolean newSubGroup = elemCntr % m_procInfo.splitBlockSize == 0;
				if (newSubGroup)
				{
					splitCount++;
					final String helperName = String.format("%1$s_%2$s", m_procInfo.action.tokenText(), splitCount);
					// call the helper
					if (elemCntr > 0)
					{
						m_emitter.AppendLine("/* sequential composition: all states reached need to go into todo list */");
						m_emitter.AppendLine(String.format("%1$s = %2$s; %2$s = NULL;", m_procInfo.toDoListString, m_procInfo.doneListString));
					}
					m_emitter.AppendLine(String.format("%1$s = %2$s;", m_procInfo.doneListString, CallHelper(m_procInfo.action, helperName)));
					// make a header for the helper
					m_procInfo.m_helperMethods.AppendLine();
					m_procInfo.m_helperMethods.AppendLine(HelperFunctionHeader(m_procInfo.action, helperName));
					m_procInfo.m_helperMethods.IncIndent();
					m_procInfo.m_helperMethods.AppendLine("{");
					m_procInfo.m_helperMethods.AppendLine("struct STATE_LIST *newstatelist = NULL;");
				}
				if (!newSubGroup)
				{
					m_procInfo.m_helperMethods.AppendLine("/* sequential composition: all states reached need to go into todo list */");
					m_procInfo.m_helperMethods.AppendLine(String.format("%1$s = %2$s; %2$s = NULL;", m_procInfo.toDoListString, m_procInfo.doneListString));
				}
				m_procInfo.m_helperMethods.AppendLine(stmnt.toString());
				if ((elemCntr + 1) % m_procInfo.splitBlockSize == 0 || (elemCntr + 1) >= seqBlock.statements().size())
				{
					// close the helper
					m_procInfo.m_helperMethods.AppendLine(String.format("return %1$s;",m_procInfo.doneListString));
					m_procInfo.m_helperMethods.DecIndent();
					m_procInfo.m_helperMethods.AppendLine("}");
				}
			}
		}
		// we need to do some loop-calling here..
		if (haveNondeterminism)
		{

			m_emitter.AppendLine("/* save result state and reset initial state for next choice */");
			m_emitter.AppendLine(String.format("LIST_INSERT_LIST(%3$s,%4$s); %1$s = %2$s; %4$s = NULL;",
					m_procInfo.toDoListString,
					String.format("seqInitial_%1$s", blockNum),
					String.format("seqFinal_%1$s", blockNum),
					m_procInfo.doneListString));

			while (loopcount > 0)
			{
				m_emitter.AppendLine("      }");
				loopcount--;
			}
			m_emitter.AppendLine("/* all final states reached after the seq-block are on our done list */");
			m_emitter.AppendLine(String.format("%1$s = %2$s;", m_procInfo.doneListString, String.format("seqFinal_%1$s", blockNum)));
		}
		m_emitter.DecIndent();
		m_emitter.AppendLine("} /* end seqblock */");
	}

	@Override
	public  void visit(PrioBlock prioBlock)
	{
		m_blockCount++;
		final int prioBlocknum = m_blockCount;
		m_emitter.AppendLine("{ /* begin prioritized composition block */");
		m_emitter.AppendLine(String.format("struct STATE_LIST *prioInitial_%1$s = %2$s;", prioBlocknum, m_procInfo.toDoListString));
		m_emitter.AppendLine(String.format("struct STATE_LIST *prioFinal_%1$s = NULL;", prioBlocknum));
		for (final Statement s : prioBlock.statements())
		{
			s.Accept(this);
			m_emitter.AppendLine("/* prio block: save result and reset initial states for next choice */");

			m_emitter.AppendLine(String.format("%1$s = GetPrioritizedWorkList(prioInitial_%2$s, %3$s);",
					m_procInfo.toDoListString, prioBlocknum, m_procInfo.doneListString));
			m_emitter.AppendLine(String.format("LIST_INSERT_LIST(prioFinal_%1$s, %2$s);",
					prioBlocknum, m_procInfo.doneListString));
			m_emitter.AppendLine(String.format("if (%3$s == NULL) goto prioritizedCompositionExit_%2$s; else %1$s = NULL;",
					m_procInfo.doneListString, prioBlocknum, m_procInfo.toDoListString));
		}
		m_emitter.AppendLine("");
		m_emitter.AppendLine(String.format("prioritizedCompositionExit_%1$s:", prioBlocknum));
		m_emitter.AppendLine(String.format("%1$s = %2$s; /* prioritized composition block end: save all reached final states */",
				m_procInfo.doneListString, String.format("prioFinal_%1$s", prioBlocknum)));
		m_emitter.AppendLine("} /* end prioritized composition block */");
	}

	@Override
	public  void visit(GuardedCommand guardedCommand)
	{
		String body;
		final CadpExpression expr = new CadpExpression(m_procInfo);
		final CadpStatement statement = new CadpStatement(this);
		guardedCommand.guard().Accept(expr);
		guardedCommand.body().Accept(statement);
		m_blockCount = statement.blockCount() + 1;

		body = String.format("%3$s if (%1$s) { LIST_INSERT_SINGLELIST(%2$s,elem); };", expr.toString(), m_procInfo.doneListString, expr.VariableDeclarations());

		m_emitter.AppendLineIncIndent("{");
		m_emitter.AppendLine("/* guarded command */");
		m_emitter.AppendLine(String.format("ITERATE_STATES(%1$s, %2$s );", m_procInfo.toDoListString, body));
		m_emitter.AppendLine(String.format("%1$s = %2$s; %2$s = NULL;", m_procInfo.toDoListString, m_procInfo.doneListString));
		m_emitter.AppendLine(String.format(" if (%1$s == NULL) goto guardedCommandExit_%2$s;", m_procInfo.toDoListString, OoaCADPVisitor.getUIntHashCode(guardedCommand)));
		m_emitter.AppendLineDecIndent("}");
		m_emitter.Append(statement.toString());
		m_emitter.AppendLine(String.format("guardedCommandExit_%2$s: %1$s = %1$s;", m_procInfo.toDoListString, OoaCADPVisitor.getUIntHashCode(guardedCommand)));
	}

	@Override
	public  void visit(Assignment assignment)
	{
		final OoaPrintVisitor pvis = new OoaPrintVisitor(null);
		assignment.Accept(pvis);
		m_emitter.AppendLine(String.format("{ /* assignment: %1$s */", pvis.toString().replace("/*", "").replace("*/", "")));

		m_emitter.AppendLine(String.format("struct STATE_LIST *elems = %1$s;", m_procInfo.toDoListString));
		m_emitter.AppendLine("CAESAR_TYPE_STATE newState = NULL;");
		m_emitter.AppendLine("CAESAR_TYPE_STATE oldState;");
		m_emitter.AppendLine("while (elems != NULL)");
		m_emitter.AppendLineIncIndent("{");
		m_emitter.AppendLine("oldState = (CAESAR_TYPE_STATE) elems->aState;");

		final ArrayList<SimpleEntry<String, Type>> nondetEnumerators = new ArrayList<>();
		if (assignment.symbols().symbolList().size() > 0)
		{
			for (final Identifier sym : assignment.symbols().symbolList())
			{
				final CadpType typevis = new CadpType();
				final CadpIdentifier idvis = new CadpIdentifier("oldState");
				sym.type().Accept(typevis);
				sym.Accept(idvis);
				m_emitter.AppendLine(String.format("%1$s %2$s; /* unbound in expression */", typevis.ToString(), idvis.toString()));
				nondetEnumerators.add(new SimpleEntry<String, Type>(idvis.toString(), sym.type()));
			}
		}

		if (assignment.nondetExpression() != null)
		{
			for (final SimpleEntry<String, Type> key : nondetEnumerators)
			{
				m_emitter.AppendLine(String.format("for (%1$s = %2$s; %1$s <= %3$s; %1$s++) ",
						key.getKey(), Type.Low(key.getValue()), Type.High(key.getValue())));
				m_emitter.AppendLineIncIndent("{");
			}

			final CadpExpression exprvis = new CadpExpression(new CadpActionInfo(null, -1, 0, "", "", "oldState"));
			assignment.nondetExpression().Accept(exprvis);
			m_emitter.AppendLine(String.format("if (%1$s)", exprvis.toString()));
		}

		m_emitter.AppendLine("{");

		m_emitter.AppendLine("/* setup new state */");
		m_emitter.AppendLine("CAESAR_CREATE_STATE(&newState);");
		m_emitter.AppendLine("memcpy(newState,oldState,CAESAR_SIZE_STATE());");
		m_emitter.AppendLine("newState->_hidden = 1;");
		m_emitter.AppendLine("newState->_predecessor = oldState;");
		m_emitter.AppendLine("struct STATE_LIST *newListElem;");
		m_emitter.AppendLine("LIST_CREATE(newListElem);");
		m_emitter.AppendLine("newListElem->aState = newState;");
		m_emitter.AppendLine("newState->_successors = NULL;");
		m_emitter.AppendLine(String.format("newState->_statenum = (char*)%1$s;", m_debugPosCount--));



		final Iterator<Expression> place = assignment.places().iterator();
		final Iterator<Expression> value = assignment.values().iterator();
		while (place.hasNext() && value.hasNext())
		{
			final Expression currValue = value.next();
			final Expression currPlace = place.next();
			CadpExpression vis = new CadpExpression(new CadpActionInfo(null, -1, 0, "", "", "oldState"));
			currValue.Accept(vis);
			final String from = vis.toString();
			final String fromdecls = vis.VariableDeclarations();

			vis = new CadpExpression(new CadpActionInfo(null, -1, 0, "", "", "newState"));
			currPlace.Accept(vis);
			final String to = vis.toString();
			/*if (to.Equals("param_result"))
                    to = "*param_result";*/
			m_emitter.AppendLine(vis.VariableDeclarations());
			m_emitter.AppendLine(fromdecls);
			m_emitter.AppendLine(String.format("%1$s = %2$s;", to, from));

			// code that checks wether we're in range..
			switch (currPlace.type().kind())
			{
			case IntType:
				final IntType placetype_int = (IntType)currPlace.type();
				m_emitter.AppendLine(String.format("/*Range of %3$s: %1$s..%2$s*/", placetype_int.rangeLow(), placetype_int.rangeHigh(), to));
				m_emitter.AppendLine(String.format("if (%1$s < %2$sLL || %1$s > %3$sLL)", to, placetype_int.rangeLow(), placetype_int.rangeHigh()));
				m_emitter.AppendLine(String.format("{CAESAR_DELETE(newState); free(newListElem); newListElem = NULL; goto rangeExit_%1$s;}", OoaCADPVisitor.getUIntHashCode(assignment)));
				break;
			case FloatType:
				final FloatType placetype_float = (FloatType)currPlace.type();
				m_emitter.AppendLine(String.format("/*Range of %3$s: %1$s..%2$s*/", placetype_float.low(), placetype_float.high(), to));
				m_emitter.AppendLine(String.format("if (%1$s < %2$s || %1$s > %3$s)", to, placetype_float.low(), placetype_float.high()));
				m_emitter.AppendLine(String.format("{CAESAR_DELETE(newState); free(newListElem); newListElem = NULL; goto rangeExit_%1$s;}", OoaCADPVisitor.getUIntHashCode(assignment)));
				break;
			default:
				m_emitter.AppendLine(String.format("if (CALC_INVALID == 1)"));
				m_emitter.AppendLine(String.format("{ CALC_INVALID = 0; CAESAR_DELETE(newState); free(newListElem); newListElem = NULL; goto rangeExit_%1$s;}", OoaCADPVisitor.getUIntHashCode(assignment)));
				break;
			}
		}
		m_emitter.AppendLine("LISTLIST_INSERT_LIST(ARGOS_ALL_STATES,newListElem);");
		m_emitter.AppendLine(String.format("rangeExit_%1$s:", OoaCADPVisitor.getUIntHashCode(assignment)));
		m_emitter.AppendLine(String.format("LIST_INSERT_SINGLELIST(%1$s,newListElem);", m_procInfo.doneListString));
		m_emitter.AppendLine("}");
		if (assignment.nondetExpression() != null)
			for (@SuppressWarnings("unused") final SimpleEntry<String, Type> key : nondetEnumerators)
				m_emitter.AppendLine("} /* closing nondet assignment for loop */");

		m_emitter.AppendLine("elems = elems->next;");
		m_emitter.AppendLineDecIndent("} /*while loop, iterating through all states*/");
		m_emitter.AppendLine("} /* end assignment*/");
	}

	@Override
	public  void visit(Call call)
	{
		final Expression ce = call.callExpression();
		String callstatement = "";

		/*for each state in the worklist..*/

		m_emitter.AppendLineIncIndent("{ /* begin named action call*/");
		m_emitter.AppendLine("struct STATE_LIST *callresult = NULL;");
		m_emitter.AppendLine(String.format("struct STATE_LIST *elem = %1$s;", m_procInfo.toDoListString));
		m_emitter.AppendLine("while(elem != NULL)");
		m_emitter.AppendLineIncIndent("{");
		m_emitter.AppendLine("struct STATE_LIST *next = elem->next;");

		final StringBuilder parameter = new StringBuilder("(CAESAR_TYPE_STATE)elem->aState");

		if (ce.kind() == ExpressionKind.Identifier)
			callstatement = ((IdentifierExpression)ce).identifier().tokenText();
		else if (ce.kind() == ExpressionKind.Call)
		{
			final CallExpression parens = (CallExpression)ce;

			final CadpExpression cadpexpr = new CadpExpression(m_procInfo);
			parens.child().Accept(cadpexpr);

			m_emitter.Append(cadpexpr.VariableDeclarations());

			callstatement = cadpexpr.toString();
			for (final Expression arg : parens.arguments())
			{
				parameter.append(", ");
				final CadpExpression paramexpr = new CadpExpression(m_procInfo);
				arg.Accept(paramexpr);
				m_emitter.AppendLine(paramexpr.VariableDeclarations());
				parameter.append(paramexpr.toString());
			}
			if (((FunctionType)parens.child().type()).returnType() != null)
			{
				final String tmpresult = String.format("detcallresult_%1$s", OoaCADPVisitor.getUIntHashCode(parens));
				m_emitter.AppendLine(String.format("%1$s %2$s;", CadpType.DumpType(((FunctionType)parens.child().type()).returnType()), tmpresult));
				parameter.append(String.format(", &(%1$s)", tmpresult));
			}

		}
		else if (ce.kind() == ExpressionKind.foldRL
				|| ce.kind() == ExpressionKind.foldLR)
		{
			// intermediate hack for map..
			final CallExpression callexpr = (CallExpression)((TernaryOperator)ce).left();
			final Expression alist = ((TernaryOperator)ce).right();

			final CadpExpression cadplist = new CadpExpression(m_procInfo);
			alist.Accept(cadplist);
			final String listname = Integer.toString(OoaCADPVisitor.getUIntHashCode(alist)) + Integer.toString(OoaCADPVisitor.getUIntHashCode(callexpr));
			//m_emitter.AppendLine("struct STATE_LIST *_map_tmp_result = elem;");
			m_emitter.AppendLine(String.format("%1$s list_%2$s = %3$s;",
					CadpType.DumpType(alist.type()), listname, cadplist.toString()));
			m_emitter.AppendLine(String.format("int list_iterator_%1$s;", listname));
			if (ce.kind() == ExpressionKind.foldLR)
				m_emitter.AppendLine(String.format("for (list_iterator_%1$s = 0; list_iterator_%1$s < list_%1$s.length; list_iterator_%1$s++) {", listname));
			else
				m_emitter.AppendLine(String.format("for (list_iterator_%1$s = list_%1$s.length - 1; list_iterator_%1$s >= 0 ; list_iterator_%1$s--) {", listname));

			m_emitter.AppendLine(String.format("%1$s list_elem%2$s = list_%2$s.elements[list_iterator_%2$s];", CadpType.DumpType(((ListType)alist.type()).innerType()),
					listname));


			final CadpExpression cadpexpr = new CadpExpression(m_procInfo);
			callexpr.child().Accept(cadpexpr);
			callstatement = cadpexpr.toString();
			for (final Expression arg: callexpr.arguments())
			{
				parameter.append(", ");
				final CadpExpression paramexpr = new CadpExpression(m_procInfo);
				arg.Accept(paramexpr);
				parameter.append(paramexpr.toString());
			}
			parameter.append(String.format(", list_elem%1$s", listname));

		}
		else
			throw new UnsupportedOperationException();

		m_emitter.AppendLine(String.format("callresult = %1$s(%2$s);", callstatement, parameter));
		if (ce.kind() == ExpressionKind.foldRL || ce.kind() == ExpressionKind.foldLR)
		{
			m_emitter.AppendLine("if (callresult == NULL) break; /*if function of map/fold not enabled - abort*/");
			m_emitter.AppendLine("if (callresult->next != NULL) { fprintf(stderr,\"\\nnon-deterministic fold/map not supported!\\n\");  CAESAR_TERMINATE(1); }");
			m_emitter.AppendLine("elem = callresult; }");
		}
		m_emitter.AppendLine(String.format("LIST_INSERT_LIST(%1$s, callresult);", m_procInfo.doneListString));
		m_emitter.AppendLine("elem = next;");
		m_emitter.AppendLineDecIndent("}");
		m_emitter.AppendLineDecIndent("} /* end named action call*/");
	}

	@Override
	public  void visit(SkipStatement skipStatement)
	{
		m_emitter.AppendLine("{ /* skip */");
		m_emitter.AppendLine(String.format("struct STATE_LIST *elems = %1$s;", m_procInfo.toDoListString));
		m_emitter.AppendLine("CAESAR_TYPE_STATE newState = NULL;");
		m_emitter.AppendLine("CAESAR_TYPE_STATE oldState;");
		m_emitter.AppendLine("while (elems != NULL)");
		m_emitter.AppendLineIncIndent("{");
		m_emitter.AppendLine("oldState = (CAESAR_TYPE_STATE) elems->aState;");

		m_emitter.AppendLine("{");

		m_emitter.AppendLine("/* setup new state */");
		m_emitter.AppendLine("CAESAR_CREATE_STATE(&newState);");
		m_emitter.AppendLine("memcpy(newState,oldState,CAESAR_SIZE_STATE());");
		m_emitter.AppendLine("newState->_hidden = 1;");
		m_emitter.AppendLine("newState->_predecessor = oldState;");
		m_emitter.AppendLine("struct STATE_LIST *newListElem;");
		m_emitter.AppendLine("LIST_CREATE(newListElem);");
		m_emitter.AppendLine("newListElem->aState = newState;");
		m_emitter.AppendLine("newState->_successors = NULL;");
		m_emitter.AppendLine(String.format("newState->_statenum = (char*)%1$s;", m_debugPosCount--));

		m_emitter.AppendLine("LISTLIST_INSERT_LIST(ARGOS_ALL_STATES,newListElem);");
		m_emitter.AppendLine(String.format("LIST_INSERT_SINGLELIST(%1$s,newListElem);", m_procInfo.doneListString));
		m_emitter.AppendLine("}");
		m_emitter.AppendLine("elems = elems->next;");
		m_emitter.AppendLineDecIndent("} /*while loop, iterating through all states*/");
		m_emitter.AppendLine("} /* end skip*/");
	}

	@Override
	public  void visit(AbortStatement abortStatement)
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public  void visit(KillStatement killStatement)
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public  String toString()
	{
		return m_emitter.toString();
	}

	public CadpStatement(CadpActionInfo aProcInfo)
	{
		super();
		if (aProcInfo == null)
			throw new IllegalArgumentException();
		m_procInfo = aProcInfo;
		m_blockCount = 0;
	}

	public CadpStatement(CadpActionInfo aProcInfo, boolean labelHiddenStates)
	{
		this(aProcInfo);
		//m_labelHiddenStates = labelHiddenStates;
	}


	public CadpStatement(CadpStatement toCopy)
	{
		super();
		m_procInfo = toCopy.m_procInfo;
		m_blockCount = toCopy.m_blockCount;
		m_statePosCount = toCopy.m_statePosCount;
		//m_labelHiddenStates = toCopy.m_labelHiddenStates;
	}
}
