package jetbrains.mps.lang.test.runtime;

/*Generated by MPS */

import org.jetbrains.mps.openapi.model.SNode;
import java.lang.reflect.InvocationTargetException;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.internal.collections.runtime.Sequence;
import org.jetbrains.mps.util.Condition;
import jetbrains.mps.openapi.intentions.IntentionExecutable;
import jetbrains.mps.util.Pair;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.Collection;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import jetbrains.mps.intentions.IntentionsManager;

public class IntentionTester {
  private final BaseEditorTestBody myEditorTest;
  private final boolean mySurroundWith;

  public IntentionTester(BaseEditorTestBody editorTest) {
    this(editorTest, false);
  }

  public IntentionTester(BaseEditorTestBody editorTest, boolean surroundWith) {
    myEditorTest = editorTest;
    mySurroundWith = surroundWith;
  }

  public boolean isIntentionApplicable(final String id, final SNode node) throws InterruptedException, InvocationTargetException {
    final Wrappers._boolean result = new Wrappers._boolean(false);
    myEditorTest.runUndoableCommandInEDTAndWait(() -> result.value = Sequence.fromIterable(getMatchingIntentions(node, new MatchIntentionById(id))).isNotEmpty());
    return result.value;
  }

  public void invokeMatchingIntention(final SNode node, final Condition<IntentionExecutable> intentionCondition) throws InterruptedException, InvocationTargetException {
    myEditorTest.runUndoableCommandInEDTAndWait(() -> {
      Pair<IntentionExecutable, SNode> singleMatch = getSingleMatchingIntention(node, intentionCondition);
      singleMatch.o1.execute(singleMatch.o2, myEditorTest.getEditorContext());
    });
  }

  public void invokeMatchingIntention(final Condition<IntentionExecutable> intentionCondition) throws InterruptedException, InvocationTargetException {
    myEditorTest.runUndoableCommandInEDTAndWait(() -> {
      List<SNode> selectedNodes = myEditorTest.getEditorContext().getSelectedNodes();
      if (selectedNodes == null || selectedNodes.isEmpty()) {
        return;
      }
      Pair<IntentionExecutable, SNode> singleMatch = getSingleMatchingIntention(selectedNodes.get(0), intentionCondition);
      singleMatch.o1.execute(singleMatch.o2, myEditorTest.getEditorContext());
    });
  }

  private Pair<IntentionExecutable, SNode> getSingleMatchingIntention(final SNode node, Condition<IntentionExecutable> intentionCondition) {
    List<Pair<IntentionExecutable, SNode>> matches = Sequence.fromIterable(getMatchingIntentions(node, intentionCondition)).toList();

    if (ListSequence.fromList(matches).count() != 1) {
      throw new RuntimeException("Expected one, found " + ListSequence.fromList(matches).count() + " intentions matching " + intentionCondition);
    }

    return ListSequence.fromList(matches).getElement(0);
  }

  private Iterable<Pair<IntentionExecutable, SNode>> getMatchingIntentions(SNode node, final Condition<IntentionExecutable> condition) {
    Collection<Pair<IntentionExecutable, SNode>> intentions = getAvailableIntentions(node);
    return CollectionSequence.fromCollection(intentions).where((it) -> condition.met(it.o1));
  }

  private Collection<Pair<IntentionExecutable, SNode>> getAvailableIntentions(final SNode node) {
    IntentionsManager.QueryDescriptor query = new IntentionsManager.QueryDescriptor();
    query.setCurrentNodeOnly(false);
    query.setSurroundWith(mySurroundWith);
    return IntentionsManager.getInstance().getAvailableIntentions(query, node, myEditorTest.getEditorContext());
  }
}
