/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.structuralsearch.inspection.highlightTemplate;

import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInspection.*;
import com.intellij.dupLocator.iterators.CountingNodeIterator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.structuralsearch.MatchResult;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.SSRBundle;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.impl.matcher.MatcherImpl;
import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
import com.intellij.structuralsearch.plugin.replace.ReplacementInfo;
import com.intellij.structuralsearch.plugin.replace.Replacer;
import com.intellij.structuralsearch.plugin.replace.ui.ReplaceConfiguration;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.structuralsearch.plugin.ui.ConfigurationManager;
import com.intellij.structuralsearch.plugin.ui.SearchContext;
import com.intellij.util.PairProcessor;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author cdr
 */
public class SSBasedInspection extends LocalInspectionTool {
  static final String SHORT_NAME = "SSBasedInspection";
  private List<Configuration> myConfigurations = new ArrayList<Configuration>();

  public void writeSettings(@NotNull Element node) throws WriteExternalException {
    ConfigurationManager.writeConfigurations(node, myConfigurations, Collections.<Configuration>emptyList());
  }

  public void readSettings(@NotNull Element node) throws InvalidDataException {
    myConfigurations.clear();
    ConfigurationManager.readConfigurations(node, myConfigurations, new ArrayList<Configuration>());
  }

  @NotNull
  public String getGroupDisplayName() {
    return GENERAL_GROUP_NAME;
  }

  @NotNull
  public String getDisplayName() {
    return SSRBundle.message("SSRInspection.display.name");
  }

  @NotNull
  @NonNls
  public String getShortName() {
    return SHORT_NAME;
  }

  @NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
    final MatcherImpl.CompiledOptions compiledOptions =
      SSBasedInspectionCompiledPatternsCache.getCompiledOptions(holder.getProject());

    if (compiledOptions == null) return super.buildVisitor(holder, isOnTheFly);

    return new PsiElementVisitor() {
      final List<Pair<MatchContext,Configuration>> contexts = compiledOptions.getMatchContexts();
      final Matcher matcher = new Matcher(holder.getManager().getProject());
      final PairProcessor<MatchResult, Configuration> processor = new PairProcessor<MatchResult, Configuration>() {
        public boolean process(MatchResult matchResult, Configuration configuration) {
          PsiElement element = matchResult.getMatch();
          String name = configuration.getName();
          LocalQuickFix fix = createQuickFix(holder.getManager().getProject(), matchResult, configuration);
          holder.registerProblem(
            holder.getManager().createProblemDescriptor(element, name, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)
          );
          return true;
        }
      };

      @Override
      public void visitElement(PsiElement element) {
        if (LexicalNodesFilter.getInstance().accepts(element)) return;
        final SsrFilteringNodeIterator matchedNodes = new SsrFilteringNodeIterator(element);
        for (Pair<MatchContext, Configuration> pair : contexts) {
          Configuration configuration = pair.second;
          MatchContext context = pair.first;

          if (Matcher.checkIfShouldAttemptToMatch(context, matchedNodes)) {
            final int nodeCount = context.getPattern().getNodeCount();
            matcher.processMatchesInElement(context, configuration, new CountingNodeIterator(nodeCount, matchedNodes), processor);
          }
        }
      }
    };
  }

  private static LocalQuickFix createQuickFix(final Project project, final MatchResult matchResult, final Configuration configuration) {
    if (!(configuration instanceof ReplaceConfiguration)) return null;
    ReplaceConfiguration replaceConfiguration = (ReplaceConfiguration)configuration;
    final Replacer replacer = new Replacer(project, replaceConfiguration.getOptions());
    final ReplacementInfo replacementInfo = replacer.buildReplacement(matchResult);

    return new LocalQuickFix() {
      @NotNull
      public String getName() {
        return SSRBundle.message("SSRInspection.replace.with", replacementInfo.getReplacement());
      }

      public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
        PsiElement element = descriptor.getPsiElement();
        if (element != null && FileModificationService.getInstance().preparePsiElementsForWrite(element)) {
          replacer.replace(replacementInfo);
        }
      }

      @NotNull
      public String getFamilyName() {
        return SSRBundle.message("SSRInspection.family.name");
      }
    };
  }

  @Nullable
  public JComponent createOptionsPanel() {
    return new SSBasedInspectionOptions(myConfigurations){
      public void configurationsChanged(final SearchContext searchContext) {
        super.configurationsChanged(searchContext);
        SSBasedInspectionCompiledPatternsCache.precompileConfigurations(searchContext.getProject(), SSBasedInspection.this);
        InspectionProfileManager.getInstance().fireProfileChanged(null);
      }
    }.getComponent();
  }

  @TestOnly
  public void setConfigurations(final List<Configuration> configurations, final Project project) {
    myConfigurations = configurations;
    SSBasedInspectionCompiledPatternsCache.setCompiledOptions(project, configurations);
  }

  public List<Configuration> getConfigurations() {
    return myConfigurations;
  }
}

@@ -5137,16 +5137,50 @@
 essor);%0A
+            matchedNodes.reset();%0A
         

reset iterator (EA-56060 - IAE: LocalSearchScope.<init>)