/*
 * 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.ide.util.gotoByName;

import com.intellij.ide.DataManager;
import com.intellij.ide.SearchTopHitProvider;
import com.intellij.ide.actions.ApplyIntentionAction;
import com.intellij.ide.ui.OptionsTopHitProvider;
import com.intellij.ide.ui.search.ActionFromOptionDescriptorProvider;
import com.intellij.ide.ui.search.OptionDescription;
import com.intellij.ide.ui.search.SearchableOptionsRegistrar;
import com.intellij.ide.ui.search.SearchableOptionsRegistrarImpl;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.impl.ActionManagerImpl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.util.CollectConsumer;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;

import java.util.*;

import static com.intellij.ide.util.gotoByName.GotoActionModel.*;

/**
 * @author peter
 */
public class GotoActionItemProvider implements ChooseByNameItemProvider {
  private final ActionManager myActionManager = ActionManager.getInstance();
  protected final SearchableOptionsRegistrar myIndex = SearchableOptionsRegistrar.getInstance();
  private final GotoActionModel myModel;

  public GotoActionItemProvider(GotoActionModel model) {
    myModel = model;
  }

  @NotNull
  @Override
  public List<String> filterNames(@NotNull ChooseByNameBase base, @NotNull String[] names, @NotNull String pattern) {
    return Collections.emptyList(); // no common prefix insertion in goto action
  }

  @Override
  public boolean filterElements(@NotNull final ChooseByNameBase base,
                                @NotNull final String pattern,
                                boolean everywhere,
                                @NotNull ProgressIndicator cancelled,
                                @NotNull final Processor<Object> consumer) {
    return filterElements(pattern, everywhere, new Processor<MatchedValue>() {
      @Override
      public boolean process(MatchedValue value) {
        return consumer.process(value);
      }
    });
  }

  public boolean filterElements(String pattern, boolean everywhere, Processor<MatchedValue> consumer) {
    DataContext dataContext = DataManager.getInstance().getDataContext(myModel.getContextComponent());

    if (!processIntentions(pattern, consumer, dataContext)) return false;
    if (!processActions(pattern, everywhere, consumer, dataContext)) return false;
    if (!processOptions(pattern, consumer, dataContext)) return false;
    if (processTopHits(pattern, consumer, dataContext)) return false;

    return true;
  }

  private static boolean processTopHits(String pattern, Processor<MatchedValue> consumer, DataContext dataContext) {
    Project project = CommonDataKeys.PROJECT.getData(dataContext);
    final CollectConsumer<Object> collector = new CollectConsumer<Object>();
    for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
      if (provider instanceof OptionsTopHitProvider) {
        String prefix = "#" + ((OptionsTopHitProvider)provider).getId() + " ";
        provider.consumeTopHits(prefix + pattern, collector, project);
      }
      provider.consumeTopHits(pattern, collector, project);
    }
    final Collection<Object> result = collector.getResult();
    final List<Comparable> c = new ArrayList<Comparable>();
    for (Object o : result) {
      if (o instanceof Comparable) {
        c.add((Comparable)o);
      }
    }
    if (!processItems(pattern, c, consumer)) return true;
    return false;
  }

  private boolean processOptions(String pattern, Processor<MatchedValue> consumer, DataContext dataContext) {
    List<Comparable> options = ContainerUtil.newArrayList();
    final Set<String> words = myIndex.getProcessedWords(pattern);
    Set<OptionDescription> optionDescriptions = null;
    final String actionManagerName = myActionManager.getComponentName();
    for (String word : words) {
      final Set<OptionDescription> descriptions = ((SearchableOptionsRegistrarImpl)myIndex).getAcceptableDescriptions(word);
      if (descriptions != null) {
        for (Iterator<OptionDescription> iterator = descriptions.iterator(); iterator.hasNext(); ) {
          OptionDescription description = iterator.next();
          if (actionManagerName.equals(description.getPath())) {
            iterator.remove();
          }
        }
        if (!descriptions.isEmpty()) {
          if (optionDescriptions == null) {
            optionDescriptions = descriptions;
          }
          else {
            optionDescriptions.retainAll(descriptions);
          }
        }
      } else {
        optionDescriptions = null;
        break;
      }
    }
    if (optionDescriptions != null && !optionDescriptions.isEmpty()) {
      Set<String> currentHits = new HashSet<String>();
      for (Iterator<OptionDescription> iterator = optionDescriptions.iterator(); iterator.hasNext(); ) {
        OptionDescription description = iterator.next();
        final String hit = description.getHit();
        if (hit == null || !currentHits.add(hit.trim())) {
          iterator.remove();
        }
      }
      for (OptionDescription description : optionDescriptions) {
        for (ActionFromOptionDescriptorProvider converter : ActionFromOptionDescriptorProvider.EP.getExtensions()) {
          AnAction action = converter.provide(description);
          if (action != null) options.add(new ActionWrapper(action, null, MatchMode.NAME, dataContext));
          options.add(description);
        }
      }
    }
    return processItems(pattern, options, consumer);
  }

  private boolean processActions(String pattern, boolean everywhere, Processor<MatchedValue> consumer, DataContext dataContext) {
    List<AnAction> actions = ContainerUtil.newArrayList();
    if (everywhere) {
      for (String id : ((ActionManagerImpl)myActionManager).getActionIds()) {
        ProgressManager.checkCanceled();
        ContainerUtil.addIfNotNull(actions, myActionManager.getAction(id));
      }
    } else {
      actions.addAll(myModel.myActionGroups.keySet());
    }

    List<ActionWrapper> actionWrappers = ContainerUtil.newArrayList();
    for (AnAction action : actions) {
      ProgressManager.checkCanceled();
      MatchMode mode = myModel.actionMatches(pattern, action);
      if (mode != MatchMode.NONE) {
        actionWrappers.add(new ActionWrapper(action, myModel.myActionGroups.get(action), mode, dataContext));
      }
    }
    return processItems(pattern, actionWrappers, consumer);
  }

  private boolean processIntentions(String pattern, Processor<MatchedValue> consumer, DataContext dataContext) {
    List<ActionWrapper> intentions = ContainerUtil.newArrayList();
    for (String intentionText : myModel.myIntentions.keySet()) {
      final ApplyIntentionAction intentionAction = myModel.myIntentions.get(intentionText);
      if (myModel.actionMatches(pattern, intentionAction) != MatchMode.NONE) {
        intentions.add(new ActionWrapper(intentionAction, intentionText, MatchMode.INTENTION, dataContext));
      }
    }
    return processItems(pattern, intentions, consumer);
  }

  private static boolean processItems(final String pattern, Collection<? extends Comparable> items, Processor<MatchedValue> consumer) {
    List<MatchedValue> matched = ContainerUtil.map(items, new Function<Comparable, MatchedValue>() {
      @Override
      public MatchedValue fun(Comparable comparable) {
        return new MatchedValue(comparable, pattern);
      }
    });
    Collections.sort(matched);
    return ContainerUtil.process(matched, consumer);
  }

}

@@ -3859,24 +3859,163 @@
 nsions()) %7B%0A
+      if (provider instanceof OptionsTopHitProvider && !((OptionsTopHitProvider)provider).isEnabled(project)) %7B%0A        continue;%0A      %7D%0A%0A
       if (pr

check that an OptionTopHitProvider is enabled while searching in goto action