package jetbrains.mps.lang.constraints.rules.typesystem;

/*Generated by MPS */

import jetbrains.mps.lang.typesystem.runtime.AbstractNonTypesystemRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.NonTypesystemRule_Runtime;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.typesystem.inference.TypeCheckingContext;
import jetbrains.mps.lang.typesystem.runtime.IsApplicableStatus;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import java.util.Deque;
import jetbrains.mps.internal.collections.runtime.DequeSequence;
import java.util.LinkedList;
import java.util.List;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.errors.messageTargets.MessageTarget;
import jetbrains.mps.errors.messageTargets.NodeMessageTarget;
import jetbrains.mps.errors.IErrorReporter;
import org.jetbrains.mps.openapi.language.SConcept;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SReferenceLink;

public class CheckNoCyclesInDefs_NonTypesystemRule extends AbstractNonTypesystemRule_Runtime implements NonTypesystemRule_Runtime {
  public CheckNoCyclesInDefs_NonTypesystemRule() {
  }
  public void applyRule(final SNode root, final TypeCheckingContext typeCheckingContext, IsApplicableStatus status) {
    final Set<SNode> visited = SetSequence.fromSet(new HashSet<SNode>());
    final Set<SNode> visiting = SetSequence.fromSet(new HashSet<SNode>());
    final Deque<SNode> stack = DequeSequence.fromDeque(new LinkedList<SNode>());
    List<SNode> allDefs = SNodeOperations.getNodeDescendants(root, CONCEPTS.DefForRule$_k, false, new SAbstractConcept[]{});
    while (ListSequence.fromList(allDefs).isNotEmpty()) {
      DequeSequence.fromDequeNew(stack).addFirstElement(ListSequence.fromList(allDefs).first());
      while (DequeSequence.fromDequeNew(stack).isNotEmpty()) {
        final SNode def = DequeSequence.fromDequeNew(stack).first();
        if (SetSequence.fromSet(visiting).contains(def)) {
          DequeSequence.fromDequeNew(stack).removeFirstElement();
          SetSequence.fromSet(visiting).removeElement(def);
          SetSequence.fromSet(visited).addElement(def);
          continue;
        }
        SetSequence.fromSet(visiting).addElement(def);
        Iterable<SNode> implicitDeps = SNodeOperations.getNodeDescendants(def, CONCEPTS.TypedDefReference$yw, false, new SAbstractConcept[]{});
        Sequence.fromIterable(implicitDeps).where((it) -> SNodeOperations.isInstanceOf(SLinkOperations.getTarget(it, LINKS.declaration$xU6a), CONCEPTS.DefForRule$_k)).select((it) -> SLinkOperations.getTarget(it, LINKS.declaration$xU6a)).visitAll((it) -> {
          SNode depDef = SNodeOperations.cast(it, CONCEPTS.DefForRule$_k);
          if (SetSequence.fromSet(visiting).contains(depDef)) {
            {
              final MessageTarget errorTarget = new NodeMessageTarget();
              IErrorReporter _reporter_2309309498 = typeCheckingContext.reportTypeError(def, "Remove cycle in the declared defs (" + SNodeOperations.present(it) + " is involved)", "r:61c80a02-cc27-4085-b38d-beaf0fede70a(jetbrains.mps.lang.constraints.rules.typesystem)", "2716118816012262155", null, errorTarget);
            }
            return;
          }
          if (!(SetSequence.fromSet(visited).contains(depDef))) {
            DequeSequence.fromDequeNew(stack).addFirstElement(depDef);
          }
        });
      }
      ListSequence.fromList(allDefs).removeWhere((it) -> SetSequence.fromSet(visited).contains(it));
    }
  }
  public SAbstractConcept getApplicableConcept() {
    return CONCEPTS.RulesConstraintsRoot$vG;
  }
  public IsApplicableStatus isApplicableAndPattern(SNode argument) {
    return new IsApplicableStatus(argument.getConcept().isSubConceptOf(getApplicableConcept()), null);
  }
  public boolean overrides() {
    return false;
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept DefForRule$_k = MetaAdapterFactory.getConcept(0x47257bf378d3470bL, 0x89d98c3261a61d15L, 0x653030359366e9d5L, "jetbrains.mps.lang.constraints.rules.structure.DefForRule");
    /*package*/ static final SConcept TypedDefReference$yw = MetaAdapterFactory.getConcept(0xea3159bff48e4720L, 0xbde286dba75f0d34L, 0x6530303593574311L, "jetbrains.mps.lang.context.defs.structure.TypedDefReference");
    /*package*/ static final SConcept RulesConstraintsRoot$vG = MetaAdapterFactory.getConcept(0x134c38d4e3af4d9eL, 0xb0691c7df0a4005dL, 0x19eb8590edaff8daL, "jetbrains.mps.lang.constraints.rules.skeleton.structure.RulesConstraintsRoot");
  }

  private static final class LINKS {
    /*package*/ static final SReferenceLink declaration$xU6a = MetaAdapterFactory.getReferenceLink(0xea3159bff48e4720L, 0xbde286dba75f0d34L, 0x6530303593574311L, 0x6530303593578e5eL, "declaration");
  }
}
