package org.mozartoz.truffle.nodes.local;

import org.mozartoz.truffle.nodes.DerefIfBoundNode;
import org.mozartoz.truffle.nodes.OzNode;
import org.mozartoz.truffle.runtime.OzVar;
import org.mozartoz.truffle.runtime.Variable;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.CreateCast;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;

@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
public abstract class BindNode extends OzNode {

	@Child UnifyNode unifyNode;

	@CreateCast("left")
	protected OzNode derefLeft(OzNode var) {
		return DerefIfBoundNode.create(var);
	}

	@CreateCast("right")
	protected OzNode derefRight(OzNode var) {
		return DerefIfBoundNode.create(var);
	}

	@Specialization(guards = { "!left.isBound()", "!right.isBound()" })
	Object unboundUnbound(VirtualFrame frame, Variable left, Variable right) {
		left.link(right);
		return left;
	}

	@Specialization(guards = { "!left.isBound()", "right.isBound()" })
	Object bindLeft(VirtualFrame frame, OzVar left, OzVar right) {
		Object value = right.getBoundValue(this);
		left.bind(value);
		return value;
	}

	@Specialization(guards = { "!left.isBound()", "!isVariable(right)" })
	Object bindLeftValue(VirtualFrame frame, OzVar left, Object right) {
		left.bind(right);
		// TODO: Also write the value directly to the frame slot if writeLeft not null?
		return right;
	}

	@Specialization(guards = { "left.isBound()", "!right.isBound()" })
	Object bindRight(VirtualFrame frame, OzVar left, OzVar right) {
		Object value = left.getBoundValue(this);
		right.bind(value);
		return value;
	}

	@Specialization(guards = { "!isVariable(left)", "!right.isBound()" })
	Object bindRightValue(VirtualFrame frame, Object left, OzVar right) {
		right.bind(left);
		return left;
	}

	@Specialization(guards = { "!isVariable(left)", "!isVariable(right)" })
	Object unifyValues(VirtualFrame frame, Object left, Object right) {
		return unifyValues(left, right);
	}

	@Specialization(guards = { "isBound(left)", "!isVariable(right)" })
	Object unifyVarValue(VirtualFrame frame, OzVar left, Object right) {
		return unifyValues(left.getBoundValue(this), right);
	}

	@Specialization(guards = { "!isVariable(left)", "isBound(right)" })
	Object unifyValueVar(VirtualFrame frame, Object left, OzVar right) {
		return unifyValues(left, right.getBoundValue(this));
	}

	private Object unifyValues(Object left, Object right) {
		return unify(left, right);
	}

	private Object unify(Object a, Object b) {
		if (unifyNode == null) {
			CompilerDirectives.transferToInterpreter();
			unifyNode = insert(UnifyNode.create());
		}
		return unifyNode.executeUnify(a, b);
	}

}

@@ -462,58 +462,8 @@
 ion;
-%0Aimport com.oracle.truffle.api.frame.VirtualFrame;
 %0A%0A@N
@@ -808,24 +808,82 @@
 e(var);%0A%09%7D%0A%0A
+%09public abstract Object executeBind(Object a, Object b);%0A%0A
 %09@Specializa
@@ -962,36 +962,16 @@
 Unbound(
-VirtualFrame frame, 
 Variable
@@ -1119,36 +1119,16 @@
 indLeft(
-VirtualFrame frame, 
 OzVar le
@@ -1323,36 +1323,16 @@
 ftValue(
-VirtualFrame frame, 
 OzVar le
@@ -1559,36 +1559,16 @@
 ndRight(
-VirtualFrame frame, 
 OzVar le
@@ -1764,36 +1764,16 @@
 htValue(
-VirtualFrame frame, 
 Object l
@@ -1916,40 +1916,19 @@
 ect 
-unify
+bind
 Values(
-VirtualFrame frame, 
 Obje
@@ -2068,21 +2068,20 @@
 %09Object 
-unify
+bind
 VarValue
@@ -2081,36 +2081,16 @@
 arValue(
-VirtualFrame frame, 
 OzVar le
@@ -2241,29 +2241,28 @@
  %7D)%0A%09Object 
-unify
+bind
 ValueVar(Vir
@@ -2262,28 +2262,8 @@
 Var(
-VirtualFrame frame, 
 Obje

Simplify BindNode