package com.intellij.structuralsearch.duplicates;

import com.intellij.dupLocator.DuplocatorSettings;
import com.intellij.dupLocator.NodeSpecificHasher;
import com.intellij.dupLocator.treeHash.DuplocatorHashCallback;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.LeafElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.structuralsearch.equivalence.EquivalenceDescriptorProvider;
import com.intellij.structuralsearch.impl.matcher.GlobalMatchingVisitor;
import com.intellij.structuralsearch.impl.matcher.iterators.FilteringNodeIterator;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Eugene.Kudelevsky
 */
public class SSRNodeSpecificHasher extends NodeSpecificHasher {
  private final SSRTreeHasher myTreeHasher;
  private final DuplocatorSettings mySettings;

  public SSRNodeSpecificHasher(@NotNull final DuplocatorSettings settings,
                               @NotNull DuplocatorHashCallback callback) {
    myTreeHasher = new SSRTreeHasher(callback, settings);
    mySettings = settings;
  }

  @Override
  public int getNodeHash(PsiElement node) {
    if (node == null) {
      return 0;
    }
    if (node instanceof PsiWhiteSpace || node instanceof PsiErrorElement) {
      return 0;
    }
    else if (node instanceof LeafElement) {
      if (!mySettings.DISTINGUISH_LITERALS) {
        final EquivalenceDescriptorProvider descriptorProvider = EquivalenceDescriptorProvider.getInstance(node);
        if (descriptorProvider != null) {
          final IElementType elementType = ((LeafElement)node).getElementType();
          if (descriptorProvider.getLiterals().contains(elementType)) {
            return 0;
          }
        }
      }
      return node.getText().hashCode();
    }
    return node.getClass().getName().hashCode();
  }

  @Override
  public int getNodeCost(PsiElement node) {
    if (node == null) {
      return 0;
    }

    EquivalenceDescriptorProvider descriptorProvider = EquivalenceDescriptorProvider.getInstance(node);
    if (descriptorProvider == null) {
      return 0;
    }

    return descriptorProvider.getNodeCost(node);
  }

  @Override
  public List<PsiElement> getNodeChildren(PsiElement node) {
    final List<PsiElement> result = new ArrayList<PsiElement>();

    final FilteringNodeIterator it = new FilteringNodeIterator(node.getFirstChild());
    while (it.hasNext()) {
      result.add(it.current());
      it.advance();
    }

    return result;
  }

  @Override
  public boolean areNodesEqual(@NotNull PsiElement node1, @NotNull PsiElement node2) {
    return false;
  }

  @Override
  public boolean areTreesEqual(@NotNull PsiElement root1, @NotNull PsiElement root2, int discardCost) {
    // todo: support discard cost
    return new GlobalMatchingVisitor().match(root1, root2);
  }

  @Override
  public boolean checkDeep(PsiElement node1, PsiElement node2) {
    // todo: try to optimize this
    return true;
  }

  @Override
  public void visitNode(@NotNull PsiElement node) {
    myTreeHasher.hash(node, this);
  }

  @Override
  public void hashingFinished() {
  }
}

@@ -210,16 +210,76 @@
 llback;%0A
+import com.intellij.openapi.application.ApplicationManager;%0A
 import c
@@ -3185,24 +3185,176 @@
 ent node) %7B%0A
+    if (ApplicationManager.getApplication().isUnitTestMode() %7C%7C%0A        mySettings.SELECTED_PROFILES.contains(node.getLanguage().getDisplayName())) %7B%0A  
     myTreeHa
@@ -3376,16 +3376,22 @@
  this);%0A
+    %7D%0A
   %7D%0A%0A  @

make search only for selected languages