package orc.run.porce;

import com.oracle.truffle.api.CompilerDirectives.*;
import static com.oracle.truffle.api.CompilerDirectives.*;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.RootNode;

import java.util.logging.Level;

import orc.ast.porc.PorcAST;
import orc.error.runtime.ArityMismatchException;
import orc.error.runtime.HaltException;
import orc.run.porce.runtime.KilledException;
import scala.Option;

public class PorcERootNode extends RootNode implements HasPorcNode {
	private final static boolean assertionsEnabled = false;

	private Option<PorcAST> porcNode = Option.apply(null);

	public void setPorcAST(PorcAST ast) {
		porcNode = Option.apply(ast);
		Logger.fine(() -> {
			PorcAST body = null; 
			if (ast instanceof orc.ast.porc.Continuation)
				body = ((orc.ast.porc.Continuation)ast).body();
			if (ast instanceof orc.ast.porc.Method)
				body = ((orc.ast.porc.Method)ast).body();
			return this + ":\n" + (body != null ? body.prettyprintWithoutNested() : "");
		});
	}

	public Option<PorcAST> porcNode() {
		return porcNode;
	}

	protected @Child Expression body;
	private final int nArguments;
	private final int nCaptured;

	public PorcERootNode(FrameDescriptor descriptor, Expression body, int nArguments, int nCaptured) {
		super(null, descriptor);
		this.body = body;
		this.nArguments = nArguments;
		this.nCaptured = nCaptured;
	}

	@Override
	public Object execute(VirtualFrame frame) {
		if (assertionsEnabled) {
			Object[] arguments = frame.getArguments();
			if (arguments.length != nArguments + 1) {
				throwArityException(arguments.length - 1, nArguments);
			}
			Object[] captureds = (Object[]) arguments[0];
			if (captureds.length != nCaptured) {
				InternalPorcEError.capturedLengthError(nCaptured, captureds.length);
			}
		}

		try {
			Object ret = body.execute(frame);
			return ret;
		} catch (KilledException | HaltException e) {
			transferToInterpreter();
			Logger.log(Level.WARNING, () -> "Caught " + e + " from root node.", e);
			return PorcEUnit.SINGLETON;
		}
	}

	@TruffleBoundary(allowInlining = true)
	private static void throwArityException(int nReceived, int nExpected) {
		throw new ArityMismatchException(nExpected, nReceived);
	}

	public static PorcERootNode create(FrameDescriptor descriptor, Expression body, int nArguments, int nCaptured) {
		return new PorcERootNode(descriptor, body, nArguments, nCaptured);
	}
}

@@ -754,16 +754,18 @@
 ast);%0A%09%09
+//
 Logger.f
@@ -778,351 +778,529 @@
  -%3E 
-%7B%0A%09%09%09PorcAST body = null; %0A%09%09%09if (ast instanceof orc.ast.porc.Continuation)%0A%09%09%09%09body = ((orc.ast.porc.Continuation)ast).body();%0A%09%09%09if (ast instanceof orc.ast.porc.Method)%0A%09%09%09%09body = ((orc.ast.porc.Method)ast).body();%0A%09%09%09return this + %22:%5Cn%22 + (body != null ? body.prettyprintWithoutNested() : %22%22);%0A%09%09%7D);%0A%09%7D%0A%0A%09public Option%3CPorcAST%3E porcNode() %7B
+this + %22 is %22 + getName());%0A%09%7D%0A%0A%09public Option%3CPorcAST%3E porcNode() %7B%0A%09%09return porcNode;%0A%09%7D%0A%09%0A%09public String getName() %7B%0A%09%09String name = %22%3Cno AST%3E%22;%0A%09%09scala.Option%3CPorcAST%3E optAst = porcNode();%0A%09%09if (optAst.isDefined()) %7B%0A%09%09%09PorcAST ast = optAst.get();%0A%09%09%09name = %22%3CN/A%3E%22;%0A%09%09%09if (ast instanceof orc.ast.hasOptionalVariableName) %7B%0A%09%09%09%09scala.Option%3CString%3E optName = ((orc.ast.hasOptionalVariableName)ast).optionalVariableName();%0A%09%09%09%09if(optName.isDefined())%0A%09%09%09%09%09name = optName.get();%0A%09%09%09%09else%0A%09%09%09%09%09name = %22%3Cunset%3E%22;%0A%09%09%09%7D%0A%09%09%7D
 %0A%09%09r
@@ -1301,31 +1301,27 @@
 %09%7D%0A%09%09return 
-porcNod
+nam
 e;%0A%09%7D%0A%0A%09prot
@@ -2646,10 +2646,85 @@
 ed);%0A%09%7D%0A
+%09%0A%09@Override%0A%09public String toString() %7B%0A%09%09return %22PorcE.%22 + getName();%0A%09%7D%0A
 %7D%0A

Use Porc names in PorcE RootNodes.