package jetbrains.mps.baseLanguage.doubleDispatch.typesystem;

/*Generated by MPS */

import java.util.Map;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.baseLanguage.behavior.Classifier__BehaviorDescriptor;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.baseLanguage.behavior.ClassConcept__BehaviorDescriptor;
import jetbrains.mps.internal.collections.runtime.IMapping;
import org.jetbrains.mps.openapi.language.SConcept;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SReferenceLink;

public class DispatchMethodCollector {
  private Map<DispatchGroupDescriptor, DispatchGroup> groups = MapSequence.fromMap(new HashMap<DispatchGroupDescriptor, DispatchGroup>());
  public DispatchMethodCollector(SNode classToCheck) {
    fill(classToCheck);
  }
  private void fill(SNode classToCheck) {

    // Instance methods

    SNode clas = classToCheck;

    while ((clas != null)) {
      startNewClass(clas);
      for (SNode m : Sequence.fromIterable(Classifier__BehaviorDescriptor.methods_id4_LVZ3pBKCn.invoke(clas)).where((it) -> DispatchUtil.isReadyMethod(it))) {
        addMethod(m);
      }
      clas = SLinkOperations.getTarget(SLinkOperations.getTarget(SNodeOperations.cast(clas, CONCEPTS.ClassConcept$bK), LINKS.superclass$Mp9$), LINKS.classifier$cxMr);
    }

    for (DispatchGroup g : Sequence.fromIterable(MapSequence.fromMap(groups).values())) {
      g.finalize();
    }

    // Now static methods. They're simpler. No need to take superclasses into account
    for (SNode m : Sequence.fromIterable(ClassConcept__BehaviorDescriptor.staticMethods_id4_LVZ3pCeXr.invoke(classToCheck)).where((it) -> DispatchUtil.isReadyMethod(it))) {
      addMethod(m);
    }
  }
  private void startNewClass(SNode cls) {
    for (IMapping<DispatchGroupDescriptor, DispatchGroup> g : MapSequence.fromMap(groups)) {
      g.value().startNewClass(cls);
    }
  }
  private void addMethod(SNode method) {

    DispatchGroupDescriptor desc = new DispatchGroupDescriptor(method);
    DispatchGroup group = MapSequence.fromMap(groups).get(desc);
    if (group == null) {
      group = new DispatchGroup(desc, SNodeOperations.getNodeAncestor(method, CONCEPTS.ClassConcept$bK, false, false));
      MapSequence.fromMap(groups).put(desc, group);
    }

    group.addMethod(method);
  }
  public Iterable<DispatchGroup> getGroups() {
    return MapSequence.fromMap(groups).values();
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept ClassConcept$bK = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, "jetbrains.mps.baseLanguage.structure.ClassConcept");
  }

  private static final class LINKS {
    /*package*/ static final SContainmentLink superclass$Mp9$ = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, 0x10f6353296dL, "superclass");
    /*package*/ static final SReferenceLink classifier$cxMr = MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, 0x101de490babL, "classifier");
  }
}
