package jetbrains.mps.lang.test.runtime;

/*Generated by MPS */

import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.typechecking.TypecheckingSession;
import jetbrains.mps.typechecking.TypecheckingFacade;
import java.util.Collection;
import jetbrains.mps.smodel.SNodeMatcher;
import org.junit.Assert;

public abstract class CheckTypesAction {
  protected final SNode myNodeToCheck;

  public CheckTypesAction(SNode nodeToCheck) {
    this.myNodeToCheck = nodeToCheck;
  }

  public void checkNodeWithTypeCheckingAction(final SNode node, Runnable checkingAction) {
    SNode containingRoot = node.getContainingRoot();
    TypecheckingSession.Flags flags = TypecheckingSession.Flags.forRoot(containingRoot).incremental();
    TypecheckingFacade.getFromContext().runWithSession(TypecheckingFacade.getFromContext().requestNewSession(flags).session(), checkingAction);
  }

  public void checkTypeIs(final SNode typeToCompare) {
    checkNodeWithTypeCheckingAction(myNodeToCheck, () -> {
      SNode computedType = computeType();
      NodeCheckerUtil.assertTypesAreTheSame(myNodeToCheck, computedType, typeToCompare);
    });
  }
  public void checkTypeIn(final Collection<SNode> allowedTypes) {
    checkNodeWithTypeCheckingAction(myNodeToCheck, () -> {
      SNode computedType = computeType();
      boolean hasType = false;
      for (SNode typeToCompare : allowedTypes) {
        if (new SNodeMatcher().match(computedType, typeToCompare)) {
          hasType = true;
          break;
        }
      }
      Assert.assertTrue("node type <" + NodeCheckerUtil.nodeWithIdToString(computedType) + "> is not in <" + allowedTypes + ">", hasType);

    });
  }

  public abstract SNode computeType();

  public static class CheckComputedType extends CheckTypesAction {
    public CheckComputedType(SNode nodeToCheck) {
      super(nodeToCheck);
    }
    @Override
    public SNode computeType() {
      return TypecheckingFacade.getFromContext().getTypeOf(myNodeToCheck);
    }
  }
  public static class CheckExpectedType extends CheckTypesAction {
    public CheckExpectedType(SNode nodeToCheck) {
      super(nodeToCheck);
    }
    @Override
    public SNode computeType() {
      return TypecheckingFacade.getFromContext().getInferredType(myNodeToCheck);
    }
  }

}
