package som.interpreter.actors;

import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.Instrumentable;
import com.oracle.truffle.api.source.SourceSection;

import som.VM;
import som.interpreter.actors.SPromise.Resolution;
import som.interpreter.actors.SPromise.SResolver;
import som.interpreter.nodes.nary.TernaryExpressionNode;
import som.interpreter.nodes.nary.UnaryExpressionNode;


@Instrumentable(factory = AbstractPromiseResolutionNodeWrapper.class)
public abstract class AbstractPromiseResolutionNode extends TernaryExpressionNode {

  @Child protected WrapReferenceNode wrapper = WrapReferenceNodeGen.create();
  @Child protected UnaryExpressionNode haltNode;

  protected AbstractPromiseResolutionNode(final boolean eagWrap, final SourceSection source) {
    super(eagWrap, source);
    haltNode = insert(SuspendExecutionNodeGen.create(false, source, 2, null));
    VM.insertInstrumentationWrapper(haltNode);
  }

  protected AbstractPromiseResolutionNode(final AbstractPromiseResolutionNode node) {
    super(node);
    haltNode = insert(SuspendExecutionNodeGen.create(false, node.getSourceSection(), 2, null));
    VM.insertInstrumentationWrapper(haltNode);
  }

  public abstract Object executeEvaluated(final VirtualFrame frame,
      final SResolver receiver, Object argument,
      final boolean isBreakpointOnPromiseResolution);

  /**
   * To avoid cycles in the promise chain, do nothing when a promise is resolved with itself.
   */
  @Specialization(guards = {"resolver.getPromise() == result"})
  public SResolver selfResolution(final SResolver resolver, final SPromise result,
      final boolean isBreakpointOnPromiseResolution) {
    return resolver;
  }

  /**
   * Handle the case that a promise is resolved with another promise, which is not itself.
   */
  @Specialization(guards = {"resolver.getPromise() != promiseValue"})
  public SResolver chainedPromise(final VirtualFrame frame,
      final SResolver resolver, final SPromise promiseValue,
      final boolean isBreakpointOnPromiseResolution) {
    chainPromise(resolver, promiseValue, isBreakpointOnPromiseResolution);
    return resolver;
  }

  protected static boolean notAPromise(final Object result) {
    return !(result instanceof SPromise);
  }

  protected void chainPromise(final SResolver resolver,
      final SPromise promiseValue,
      final boolean isBreakpointOnPromiseResolution) {
    assert resolver.assertNotCompleted();
    SPromise promiseToBeResolved = resolver.getPromise();
    boolean breakpointOnResolution = isBreakpointOnPromiseResolution;
    synchronized (promiseValue) {
      Resolution state = promiseValue.getResolutionStateUnsync();
      if (SPromise.isCompleted(state)) {
        resolvePromise(state, resolver, promiseValue.getValueUnsync(),
            breakpointOnResolution);
      } else {
        synchronized (promiseToBeResolved) { // TODO: is this really deadlock free?
          // TODO: check this condition.
          if (promiseValue.isTriggerPromiseResolutionBreakpoint() && !breakpointOnResolution) {
            breakpointOnResolution = promiseValue.isTriggerPromiseResolutionBreakpoint();
          }
          promiseToBeResolved.setTriggerResolutionBreakpointOnUnresolvedChainedPromise(breakpointOnResolution);
          promiseValue.addChainedPromise(promiseToBeResolved);
        }
      }
    }
  }

  protected void resolvePromise(final Resolution type,
      final SResolver resolver, final Object result,
      final boolean isBreakpointOnPromiseResolution) {
    SPromise promise = resolver.getPromise();
    Actor current = EventualMessage.getActorCurrentMessageIsExecutionOn();

    resolve(type, wrapper, promise, result, current,
        isBreakpointOnPromiseResolution);
  }

  public static void resolve(final Resolution type,
      final WrapReferenceNode wrapper,
      final SPromise promise, final Object result, final Actor current,
      final boolean isBreakpointOnPromiseResolution) {
    Object wrapped = wrapper.execute(result, promise.owner, current);
    SResolver.resolveAndTriggerListenersUnsynced(type, result, wrapped, promise,
        current, isBreakpointOnPromiseResolution);
  }
}

@@ -2992,49 +2992,8 @@
 ee?%0A
-          // TODO: check this condition.%0A
     

Remove TODO