// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.search;

import com.intellij.ide.favoritesTreeView.FavoritesManager;
import com.intellij.ide.projectView.impl.AbstractUrl;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packageDependencies.ChangeListsScopesProvider;
import com.intellij.psi.search.scope.packageSet.NamedScope;
import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
import com.intellij.util.TreeItem;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author gregsh
 */
public class DefaultSearchScopeProviders {
  private DefaultSearchScopeProviders() {}

  public static class Favorites implements SearchScopeProvider {
    @Override
    public String getDisplayName() {
      return "Favorites";
    }

    @NotNull
    @Override
    public List<SearchScope> getSearchScopes(@NotNull Project project) {
      FavoritesManager favoritesManager = FavoritesManager.getInstance(project);
      if (favoritesManager == null) return Collections.emptyList();
      List<SearchScope> result = new ArrayList<>();
      for (String favorite : favoritesManager.getAvailableFavoritesListNames()) {
        Collection<TreeItem<Pair<AbstractUrl, String>>> rootUrls = favoritesManager.getFavoritesListRootUrls(favorite);
        if (rootUrls.isEmpty()) continue;  // ignore unused root
        result.add(new GlobalSearchScope(project) {
          @NotNull
          @Override
          public String getDisplayName() {
            return "Favorite \'" + favorite + "\'";
          }

          @Override
          public boolean contains(@NotNull VirtualFile file) {
            return ReadAction.compute(() -> favoritesManager.contains(favorite, file));
          }

          @Override
          public boolean isSearchInModuleContent(@NotNull Module aModule) {
            return true;
          }

          @Override
          public boolean isSearchInLibraries() {
            return true;
          }
        });
      }
      return result;
    }
  }

  public static class ChangeLists implements SearchScopeProvider {
    @Override
    public String getDisplayName() {
      return "Local Changes";
    }

    @NotNull
    @Override
    public List<SearchScope> getSearchScopes(@NotNull Project project) {
      List<SearchScope> result = new ArrayList<>();
      List<NamedScope> changeLists = ChangeListsScopesProvider.getInstance(project).getFilteredScopes();
      if (!changeLists.isEmpty()) {
        for (NamedScope changeListScope : changeLists) {
          GlobalSearchScope scope = GlobalSearchScopesCore.filterScope(project, changeListScope);
          result.add(scope);
        }
      }
      return result;
    }
  }

  public static class CustomNamed implements SearchScopeProvider {
    @Override
    public String getDisplayName() {
      return "Other";
    }

    @NotNull
    @Override
    public List<SearchScope> getSearchScopes(@NotNull Project project) {
      List<SearchScope> result = new ArrayList<>();
      NamedScopesHolder[] holders = NamedScopesHolder.getAllNamedScopeHolders(project);
      for (NamedScopesHolder holder : holders) {
        NamedScope[] scopes = holder.getEditableScopes();  // predefined scopes already included
        for (NamedScope scope : scopes) {
          GlobalSearchScope searchScope = GlobalSearchScopesCore.filterScope(project, scope);
          result.add(searchScope);
        }
      }
      return result;
    }
  }
}

@@ -282,16 +282,67 @@
 actUrl;%0A
+import com.intellij.ide.util.treeView.WeighedItem;%0A
 import c
@@ -2964,63 +2964,28 @@
     
-GlobalSearchScope scope = GlobalSearchScopesCore.filter
+result.add(wrapNamed
 Scop
@@ -3015,36 +3015,8 @@
 ope)
-;%0A          result.add(scope
 );%0A 
@@ -3649,16 +3649,235 @@
         
+result.add(wrapNamedScope(project, scope));%0A        %7D%0A      %7D%0A      return result;%0A    %7D%0A  %7D%0A%0A  @NotNull%0A  private static GlobalSearchScope wrapNamedScope(@NotNull Project project, @NotNull NamedScope namedScope) %7B%0A    
 GlobalSe
@@ -3887,22 +3887,16 @@
 hScope s
-earchS
 cope = G
@@ -3934,25 +3934,30 @@
 pe(project, 
-s
+namedS
 cope);%0A     
@@ -3959,56 +3959,419 @@
     
-      result.add(searchScope);%0A    
+return namedScope instanceof WeighedItem ? new MyWeightedScope(scope, ((WeighedItem)namedScope).getWeight()) : scope;%0A  %7D%0A%0A  private static class MyWeightedScope extends DelegatingGlobalSearchScope implements WeighedItem %7B%0A    final int weight;%0A%0A    MyWeightedScope(@NotNull GlobalSearchScope scope, int weight) %7B%0A      super(scope);%0A      this.weight = weight;%0A
     %7D%0A
+%0A
     
-  %7D
+@Override%0A    public int getWeight() %7B
 %0A   
@@ -4372,36 +4372,36 @@
  %7B%0A      return 
-resul
+weigh
 t;%0A    %7D%0A  %7D%0A%7D%0A

fix scopes sorting: "All ..." scope must be the first 2