package jetbrains.mps.ide.hierarchy;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import com.intellij.openapi.project.Project;
import com.intellij.icons.AllIcons;
import jetbrains.mps.ide.ui.tree.TreeHighlighterExtension;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.behaviour.BHReflection;
import jetbrains.mps.core.aspects.behaviour.SMethodIdV2;
import java.awt.font.TextAttribute;
import java.util.Set;
import java.util.HashSet;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import org.jetbrains.mps.openapi.module.FindUsagesFacade;
import java.util.Collections;
import org.jetbrains.mps.openapi.util.Consumer;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.progress.EmptyProgressMonitor;
import org.jetbrains.mps.openapi.module.SearchScope;
import jetbrains.mps.ide.findusages.model.scopes.GlobalScope;
import org.jetbrains.mps.util.Condition;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.extapi.module.TransientSModule;
import jetbrains.mps.smodel.tempmodel.TempModule;
import jetbrains.mps.scope.ConditionalScope;
import org.jetbrains.mps.openapi.language.SInterfaceConcept;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SReferenceLink;

@GeneratedClass(node = "r:8a82b7a4-1180-4262-8f18-8c96a5d3ac16(jetbrains.mps.ide.hierarchy)/5797068448049411442", model = "r:8a82b7a4-1180-4262-8f18-8c96a5d3ac16(jetbrains.mps.ide.hierarchy)")
public class BaseLanguageHierarchyViewTool extends AbstractHierarchyView {
  public BaseLanguageHierarchyViewTool(Project project) {
    super(project, "Class Hierarchy", -1, AllIcons.Toolwindows.ToolWindowHierarchy);
  }
  @Override
  protected AbstractHierarchyTree createHierarchyTree(boolean isParentHierarchy) {
    BaseLanguageHierarchyTree tree = new BaseLanguageHierarchyTree(getMPSProject().getRepository(), isParentHierarchy);
    tree.setHierarchyView(this);
    TreeHighlighterExtension.attachHighlighters(tree, getProject());
    return tree;
  }
  private static class BaseLanguageHierarchyTree extends AbstractHierarchyTree {
    private BaseLanguageHierarchyTree(SRepository repo, boolean isParentHierarchy) {
      super(repo);
      setParentHierarchy(isParentHierarchy);
    }
    @Override
    public void setNodePresentation(HierarchyTreeNode treeNode, SNode n) {
      super.setNodePresentation(treeNode, n);
      if (SNodeOperations.isInstanceOf(n, CONCEPTS.IBLDeprecatable$ah) && ((boolean) (Boolean) BHReflection.invoke0(SNodeOperations.as(n, CONCEPTS.IBLDeprecatable$ah), CONCEPTS.IDeprecatable$2W, SMethodIdV2.create("isDeprecated", 1224609060727L, 0x553941aeb020c32eL)))) {
        treeNode.addFontAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
      }
    }
    @Override
    protected Set<SNode> getParents(SNode node, Set<SNode> visited) {
      HashSet<SNode> result = new HashSet<SNode>();
      if (SNodeOperations.isInstanceOf(node, CONCEPTS.ClassConcept$bK)) {
        SNode classConcept = SNodeOperations.cast(node, CONCEPTS.ClassConcept$bK);
        SNode classifierType = SLinkOperations.getTarget(classConcept, LINKS.superclass$Mp9$);
        if (classifierType != null) {
          SNode classifier = SLinkOperations.getTarget(classifierType, LINKS.classifier$cxMr);
          if (SNodeOperations.isInstanceOf(classifier, CONCEPTS.ClassConcept$bK)) {
            result.add(classifier);
          }
        }
        for (SNode interfaceType : SLinkOperations.getChildren(classConcept, LINKS.implementedInterface$rujG)) {
          SNode interfaceClassifier = SLinkOperations.getTarget(interfaceType, LINKS.classifier$cxMr);
          if (SNodeOperations.isInstanceOf(interfaceClassifier, CONCEPTS.Interface$db)) {
            result.add(interfaceClassifier);
          }
        }
      } else
      if (SNodeOperations.isInstanceOf(node, CONCEPTS.Interface$db)) {
        SNode anInterface = SNodeOperations.cast(node, CONCEPTS.Interface$db);
        for (SNode interfaceType : SLinkOperations.getChildren(anInterface, LINKS.extendedInterface$PDVO)) {
          SNode interfaceClassifier = SLinkOperations.getTarget(interfaceType, LINKS.classifier$cxMr);
          if (SNodeOperations.isInstanceOf(interfaceClassifier, CONCEPTS.Interface$db)) {
            result.add(interfaceClassifier);
          }
        }
      }
      return result;
    }
    @Override
    protected String noNodeString() {
      return "(no classifier)";
    }
    @Override
    protected SNode getParent(SNode node) {
      if (SNodeOperations.isInstanceOf(node, CONCEPTS.ClassConcept$bK)) {
        SNode classConcept = SNodeOperations.cast(node, CONCEPTS.ClassConcept$bK);
        SNode type = SLinkOperations.getTarget(classConcept, LINKS.superclass$Mp9$);
        if (type == null) {
          return null;
        }
        SNode classifier = SLinkOperations.getTarget(type, LINKS.classifier$cxMr);
        if (SNodeOperations.isInstanceOf(classifier, CONCEPTS.ClassConcept$bK)) {
          return classifier;
        }
      } else
      if (SNodeOperations.isInstanceOf(node, CONCEPTS.Interface$db)) {
        return null;
      }
      return null;
    }
    @Override
    protected Set<SNode> getDescendants(SNode node, Set<SNode> visited) {
      FindUsagesFacade fuFacade = FindUsagesFacade.getInstance();
      // FIXME there's ClassifierSuccessor index we can use here, although shall not use them directly
      // but rather through FindUsagesFacade, with dedicated 'successor' query, so that there'd be single HierarchyViewTool, and finders plug through
      // FindUsages to get accessed in a generic way.
      final Set<SNode> result = new HashSet<SNode>();
      fuFacade.findUsages(decideSearchScope(), Collections.<SNode>singleton(node), new Consumer<SReference>() {
        @Override
        public void consume(SReference usage) {
          SNode sourceNode = usage.getSourceNode();
          if (LINKS.classifier$cxMr.equals(usage.getLink())) {
            if (SNodeOperations.isInstanceOf(sourceNode, CONCEPTS.ClassifierType$bL)) {
              SNode classifierType = SNodeOperations.cast(sourceNode, CONCEPTS.ClassifierType$bL);
              if (SNodeOperations.isInstanceOf(SNodeOperations.getParent(classifierType), CONCEPTS.ClassConcept$bK) && LINKS.superclass$Mp9$.equals(classifierType.getContainmentLink())) {
                result.add(SNodeOperations.getParent(classifierType));
              }
              if (SNodeOperations.isInstanceOf(SNodeOperations.getParent(classifierType), CONCEPTS.ClassConcept$bK) && LINKS.implementedInterface$rujG.equals(classifierType.getContainmentLink())) {
                result.add(SNodeOperations.getParent(classifierType));
              }
              if (SNodeOperations.isInstanceOf(SNodeOperations.getParent(classifierType), CONCEPTS.Interface$db) && LINKS.extendedInterface$PDVO.equals(classifierType.getContainmentLink())) {
                result.add(SNodeOperations.getParent(classifierType));
              }
            }
          }
          if (LINKS.classifier$q_Y$.equals(usage.getLink())) {
            if (SNodeOperations.isInstanceOf(sourceNode, CONCEPTS.AnonymousClass$Bt)) {
              SNode anonymousClass = SNodeOperations.cast(sourceNode, CONCEPTS.AnonymousClass$Bt);
              result.add(anonymousClass);
            }
          }
        }
      }, new EmptyProgressMonitor());
      return result;
    }
    /**
     * TODO let user control whether he needs to include transient/temporary models into the scope
     *      FWIW, there's some rudimentary control in the superclass, shall unify.
     */
    private SearchScope decideSearchScope() {
      // I think it's reasonable to use base scope that respects VisibleModulesRegistry, although
      // could imagine an option to use a scope without any restriction as a base.
      GlobalScope base = new GlobalScope(getHierarchyView().getMPSProject());
      // FIXME this is just direct replacement of AllUserModelsScope logic
      Condition<SModule> c = new Condition<SModule>() {
        @Override
        public boolean met(SModule m) {
          return !(m instanceof TransientSModule) && !(m instanceof TempModule);
        }
      };
      return new ConditionalScope(base, c, null);
    }
  }

  private static final class CONCEPTS {
    /*package*/ static final SInterfaceConcept IBLDeprecatable$ah = MetaAdapterFactory.getInterfaceConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x11d2ea8a339L, "jetbrains.mps.baseLanguage.structure.IBLDeprecatable");
    /*package*/ static final SInterfaceConcept IDeprecatable$2W = MetaAdapterFactory.getInterfaceConcept(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x11d205fe38dL, "jetbrains.mps.lang.core.structure.IDeprecatable");
    /*package*/ static final SConcept Interface$db = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101edd46144L, "jetbrains.mps.baseLanguage.structure.Interface");
    /*package*/ static final SConcept ClassConcept$bK = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, "jetbrains.mps.baseLanguage.structure.ClassConcept");
    /*package*/ static final SConcept ClassifierType$bL = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, "jetbrains.mps.baseLanguage.structure.ClassifierType");
    /*package*/ static final SConcept AnonymousClass$Bt = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1107e0cb103L, "jetbrains.mps.baseLanguage.structure.AnonymousClass");
  }

  private static final class LINKS {
    /*package*/ static final SContainmentLink extendedInterface$PDVO = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101edd46144L, 0x101eddadad7L, "extendedInterface");
    /*package*/ static final SReferenceLink classifier$cxMr = MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, 0x101de490babL, "classifier");
    /*package*/ static final SContainmentLink superclass$Mp9$ = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, 0x10f6353296dL, "superclass");
    /*package*/ static final SContainmentLink implementedInterface$rujG = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, 0xff2ac0b419L, "implementedInterface");
    /*package*/ static final SReferenceLink classifier$q_Y$ = MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1107e0cb103L, 0x1107e0fd2a0L, "classifier");
  }
}
