package jetbrains.mps.baseLanguage.pluginSolution.plugin;

/*Generated by MPS */

import jetbrains.mps.checkers.IChecker;
import jetbrains.mps.errors.item.IssueKindReportItem;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.util.Consumer;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import java.util.HashMap;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.ArrayList;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import java.util.Collection;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.baseLanguage.behavior.Classifier__BehaviorDescriptor;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.SNodePointer;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.core.behavior.BaseConcept__BehaviorDescriptor;
import jetbrains.mps.errors.item.NodeReportItemBase;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SConcept;

public class ExtendedClassImportCheck extends IChecker.AbstractModelChecker<IssueKindReportItem> {

  @Override
  public IssueKindReportItem.CheckerCategory getCategory() {
    // that's what NonTypesystemChecker (used to run CheckExtendedClassIsImported checking rule) use
    return IssueKindReportItem.TYPESYSTEM;
  }

  @Override
  public void check(SModel toCheck, SRepository repository, Consumer<? super IssueKindReportItem> errorCollector, ProgressMonitor monitor) {
    monitor.start("classes available for compile", 3);

    // XXX Could be reasonable to check model uses BL first, but given BL could come through a devkit
    //    the check might be expensive compared to FastNodeFinder access

    monitor.step("classifiers look up");
    final HashMap<SNode, ArrayList<SNode>> cls2use = new HashMap<SNode, ArrayList<SNode>>();
    for (SNode ct : SModelOperations.nodes(toCheck, CONCEPTS.ClassifierType$bL)) {
      SNode cls = SLinkOperations.getTarget(ct, LINKS.classifier$cxMr);
      if ((cls == null)) {
        continue;
      }
      ArrayList<SNode> uses = cls2use.get(cls);
      if (uses == null) {
        uses = new ArrayList<SNode>();
        cls2use.put(cls, uses);
      }
      uses.add(ct);
    }
    if (monitor.isCanceled()) {
      return;
    }
    if (cls2use.isEmpty()) {
      monitor.done();
      return;
    }
    monitor.advance(1);

    monitor.step("module dependencies");
    Collection<SModule> deps = new GlobalModuleDependenciesManager(toCheck.getModule()).getModules(GlobalModuleDependenciesManager.Deptype.COMPILE);
    if (monitor.isCanceled()) {
      return;
    }
    monitor.advance(1);

    monitor.step("check");
    IssueKindReportItem.ItemKind itemKind = getCategory().deriveItemKind();

    for (SNode classifier : SetSequence.fromSet(cls2use.keySet())) {
      if (monitor.isCanceled()) {
        return;
      }
      for (SNode extendedClassifierType : Classifier__BehaviorDescriptor.getExtendedClassifierTypes_id1UeCwxlWKny.invoke(classifier)) {
        SNode extendedClassifier = SLinkOperations.getTarget(extendedClassifierType, LINKS.classifier$cxMr);
        if (extendedClassifier == null) {
          continue;
        }
        if (SNodeOperations.is(extendedClassifier, new SNodePointer("6354ebe7-c22a-4a0f-ac54-50b52ab9b065/java:java.lang(JDK/)", "~Object"))) {
          continue;
        }
        SModel model = extendedClassifier.getModel();
        if (model == null) {
          continue;
        }
        SModule classifierModule = model.getModule();
        assert classifierModule != null;
        if (deps.contains(classifierModule)) {
          continue;
        }
        for (SNode ct : ListSequence.fromList(cls2use.get(classifier))) {
          String m = String.format("The definition of %s ancestor is not visible from this module", BaseConcept__BehaviorDescriptor.getPresentation_idhEwIMiw.invoke(extendedClassifier));
          IssueKindReportItem error = NodeReportItemBase.error(m, SNodeOperations.getPointer(ct), itemKind);
          errorCollector.accept(error);
        }
      }
    }

    monitor.done();
  }

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

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