package som.interpreter.nodes.specialized;

import som.interpreter.Invokable;
import som.interpreter.nodes.nary.BinaryExpressionNode;
import som.vm.constants.Nil;
import som.vmobjects.SBlock;
import som.vmobjects.SObject;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.SourceSection;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;


public abstract class AbstractWhileNode extends BinaryExpressionNode {
  @Child protected DirectCallNode conditionValueSend;
  @Child protected DirectCallNode bodyValueSend;

  protected final boolean predicateBool;

  public AbstractWhileNode(final SBlock rcvr, final SBlock arg,
      final boolean predicateBool, final SourceSection source) {
    super(source);

    CallTarget callTargetCondition = rcvr.getMethod().getCallTarget();
    conditionValueSend = Truffle.getRuntime().createDirectCallNode(
        callTargetCondition);

    CallTarget callTargetBody = arg.getMethod().getCallTarget();
    bodyValueSend = Truffle.getRuntime().createDirectCallNode(
        callTargetBody);

    this.predicateBool = predicateBool;
  }

  @Override
  public final Object executeEvaluated(final VirtualFrame frame,
      final Object rcvr, final Object arg) {
    return doWhileConditionally(frame, (SBlock) rcvr, (SBlock) arg);
  }

  @Override
  public final void executeEvaluatedVoid(final VirtualFrame frame,
      final Object rcvr, final Object arg) {
    doWhileConditionally(frame, (SBlock) rcvr, (SBlock) arg);
  }

  protected final SObject doWhileUnconditionally(final VirtualFrame frame,
      final SBlock loopCondition, final SBlock loopBody) {
    long iterationCount = 0;

    boolean loopConditionResult = (boolean) conditionValueSend.call(
        frame, new Object[] {loopCondition});

    try {
      if (loopConditionResult == predicateBool) {
        bodyValueSend.call(frame, new Object[] {loopBody});
        loopConditionResult = (boolean) conditionValueSend.call(
            frame, new Object[] {loopCondition});

        if (CompilerDirectives.inInterpreter()) { iterationCount++; }

        // TODO: this is a simplification, we don't cover the case receiver isn't a boolean
        while (loopConditionResult == predicateBool) {
          bodyValueSend.call(frame, new Object[] {loopBody});
          loopConditionResult = (boolean) conditionValueSend.call(
              frame, new Object[] {loopCondition});

          if (CompilerDirectives.inInterpreter()) { iterationCount++; }
        }
      }
    } finally {
      if (CompilerDirectives.inInterpreter()) {
        reportLoopCount(iterationCount);
      }
    }
    return Nil.nilObject;
  }

  protected abstract SObject doWhileConditionally(final VirtualFrame frame,
      final SBlock loopCondition, final SBlock loopBody);

  protected final void reportLoopCount(final long count) {
    CompilerAsserts.neverPartOfCompilation("reportLoopCount");
    Node current = getParent();
    while (current != null && !(current instanceof RootNode)) {
      current = current.getParent();
    }
    if (current != null) {
      ((Invokable) current).propagateLoopCountThroughoutLexicalScope(count);
    }
  }
}

@@ -2049,18 +2049,111 @@
 %7B%0A      
-if
+// TODO: this is a simplification, we don't cover the case receiver isn't a boolean%0A      while
  (loopCo
@@ -2354,32 +2354,32 @@
 opCondition%7D);%0A%0A
+
         if (Comp
@@ -2435,420 +2435,8 @@
 +; %7D
-%0A%0A        // TODO: this is a simplification, we don't cover the case receiver isn't a boolean%0A        while (loopConditionResult == predicateBool) %7B%0A          bodyValueSend.call(frame, new Object%5B%5D %7BloopBody%7D);%0A          loopConditionResult = (boolean) conditionValueSend.call(%0A              frame, new Object%5B%5D %7BloopCondition%7D);%0A%0A          if (CompilerDirectives.inInterpreter()) %7B iterationCount++; %7D%0A        %7D
 %0A   

Remove the peeled iteration from while loop