// 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.impl.cache.impl;

import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.cache.TodoCacheManager;
import com.intellij.psi.impl.cache.impl.todo.TodoIndex;
import com.intellij.psi.impl.cache.impl.todo.TodoIndexEntry;
import com.intellij.psi.impl.cache.impl.todo.TodoIndexers;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.IndexPattern;
import com.intellij.psi.search.IndexPatternProvider;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.indexing.DumbModeAccessType;
import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Eugene Zhuravlev
 */
public class IndexTodoCacheManagerImpl implements TodoCacheManager {
  private final Project myProject;

  public IndexTodoCacheManagerImpl(@NotNull Project project) {
    myProject = project;
  }

  @Override
  public PsiFile @NotNull [] getFilesWithTodoItems() {
    if (myProject.isDefault()) {
      return PsiFile.EMPTY_ARRAY;
    }
    final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
    final Set<PsiFile> allFiles = new HashSet<>();

    fileBasedIndex.ignoreDumbMode(() -> {
      for (IndexPattern indexPattern : IndexPatternUtil.getIndexPatterns()) {
        final Collection<VirtualFile> files = fileBasedIndex.getContainingFiles(
          TodoIndex.NAME,
          new TodoIndexEntry(indexPattern.getPatternString(), indexPattern.isCaseSensitive()), GlobalSearchScope.allScope(myProject));
        PsiManager psiManager = PsiManager.getInstance(myProject);
        ApplicationManager.getApplication().runReadAction(() -> {
          for (VirtualFile file : files) {
            if (TodoIndexers.belongsToProject(myProject, file)) {
              final PsiFile psiFile = psiManager.findFile(file);
              if (psiFile != null) {
                allFiles.add(psiFile);
              }
            }
          }
        });
      }
    }, myProject, DumbModeAccessType.RELIABLE_DATA_ONLY);

    return allFiles.isEmpty() ? PsiFile.EMPTY_ARRAY : PsiUtilCore.toPsiFileArray(allFiles);
  }

  @Override
  public int getTodoCount(@NotNull final VirtualFile file, @NotNull final IndexPatternProvider patternProvider) {
    if (myProject.isDefault() || !TodoIndexers.belongsToProject(myProject, file)) {
      return 0;
    }
    if (file instanceof VirtualFileWindow) return -1;
    return Arrays.stream(patternProvider.getIndexPatterns()).mapToInt(indexPattern -> fetchCount(file, indexPattern)).sum();
  }

  @Override
  public int getTodoCount(@NotNull final VirtualFile file, @NotNull final IndexPattern pattern) {
    if (myProject.isDefault() || !TodoIndexers.belongsToProject(myProject, file)) {
      return 0;
    }
    if (file instanceof VirtualFileWindow) return -1;
    return fetchCount(file, pattern);
  }

  private int fetchCount(@NotNull VirtualFile file, @NotNull IndexPattern indexPattern) {
    final int[] count = {0};
    FileBasedIndex.getInstance().ignoreDumbMode(() -> {
      FileBasedIndex.getInstance().processValues(
        TodoIndex.NAME, new TodoIndexEntry(indexPattern.getPatternString(), indexPattern.isCaseSensitive()), file,
        (file1, value) -> {
          count[0] += value.intValue();
          return true;
        }, GlobalSearchScope.fileScope(myProject, file));
    }, myProject, DumbModeAccessType.RELIABLE_DATA_ONLY);
    return count[0];
  }
}

@@ -1039,91 +1039,9 @@
 til.
-Arrays;%0Aimport java.util.Collection;%0Aimport java.util.HashSet;%0Aimport java.util.Set
+*
 ;%0A%0A/
@@ -2801,22 +2801,25 @@
 urn 
-Arrays.stream(
+fetchCount(file, 
 patt
@@ -2853,71 +2853,8 @@
 s())
-.mapToInt(indexPattern -%3E fetchCount(file, indexPattern)).sum()
 ;%0A  
@@ -3220,25 +3220,16 @@
 le file,
- @NotNull
  IndexPa
@@ -3225,32 +3225,45 @@
 le, IndexPattern
+ @NotNull ...
  indexPattern) %7B
@@ -3255,24 +3255,25 @@
 indexPattern
+s
 ) %7B%0A    fina
@@ -3359,315 +3359,309 @@
     
-FileBasedIndex.getInstance().processValues(%0A        TodoIndex.NAME, new TodoIndexEntry(indexPattern.getPatternString(), indexPattern.isCaseSensitive()), file,%0A        (file1, value) -%3E %7B%0A          count%5B0%5D += value.intValue();%0A          return true;%0A        %7D, GlobalSearchScope.fileScope(myProject, file));
+Map%3CTodoIndexEntry, Integer%3E data = FileBasedIndex.getInstance().getFileData(TodoIndex.NAME, file, myProject);%0A      for (IndexPattern indexPattern : indexPatterns) %7B%0A        count%5B0%5D += data.getOrDefault(new TodoIndexEntry(indexPattern.getPatternString(), indexPattern.isCaseSensitive()), 0);%0A      %7D
 %0A   

use forward index to get todo count