package fr.adrienbrault.idea.symfony2plugin.stubs.cache;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.ID;
import fr.adrienbrault.idea.symfony2plugin.stubs.SymfonyProcessors;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * All FileBasedIndex are slow and cross project data, we need them every often
 * Cache values as long nothing globally change in our project.
 *
 * @author Daniel Espendiller <daniel@espendiller.net>
 */
public class FileIndexCaches {

    /**
     * @param dataHolderKey Main data to cache
     * @param dataHolderNames Cache extracted name Set
     */
    static public synchronized <T> Map<String, List<T>> getSetDataCache(@NotNull final Project project, @NotNull Key<CachedValue<Map<String, List<T>>>> dataHolderKey, final @NotNull Key<CachedValue<Set<String>>> dataHolderNames, @NotNull final ID<String, T> ID, @NotNull final GlobalSearchScope scope) {
        return CachedValuesManager.getManager(project).getCachedValue(
            project,
            dataHolderKey,
            () -> {
                Map<String, List<T>> items = new HashMap<>();

                final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();

                getIndexKeysCache(project, dataHolderNames, ID).forEach(service ->
                    items.put(service, fileBasedIndex.getValues(ID, service, scope))
                );

                return CachedValueProvider.Result.create(items, PsiModificationTracker.MODIFICATION_COUNT);
            },
            false
        );
    }

    /**
     * @param dataHolderKey Main data to cache
     * @param dataHolderNames Cache extracted name Set
     */
    static public synchronized Map<String, List<String>> getStringDataCache(@NotNull final Project project, @NotNull Key<CachedValue<Map<String, List<String>>>> dataHolderKey, final @NotNull Key<CachedValue<Set<String>>> dataHolderNames, @NotNull final ID<String, String> ID, @NotNull final GlobalSearchScope scope) {
        return CachedValuesManager.getManager(project).getCachedValue(
            project,
            dataHolderKey,
            () -> {
                Map<String, List<String>> strings = new HashMap<>();

                final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
                getIndexKeysCache(project, dataHolderNames, ID).forEach(parameterName -> {
                    // just for secure
                    if(parameterName == null) {
                        return;
                    }

                    strings.put(parameterName, fileBasedIndex.getValues(ID, parameterName, scope));
                });

                return CachedValueProvider.Result.create(strings, PsiModificationTracker.MODIFICATION_COUNT);
            },
            false
        );
    }

    /**
     * There several methods that just need to check for names, as they also needed for value extraction, so cache them also
     */
    static public synchronized Set<String> getIndexKeysCache(@NotNull final Project project, @NotNull Key<CachedValue<Set<String>>> dataHolderKey, @NotNull final ID<String, ?> ID) {
        return CachedValuesManager.getManager(project).getCachedValue(
            project,
            dataHolderKey,
            () -> CachedValueProvider.Result.create(SymfonyProcessors.createResult(project, ID), PsiModificationTracker.MODIFICATION_COUNT),
            false
        );
    }

}

@@ -134,16 +134,70 @@
 il.Key;%0A
+import com.intellij.openapi.util.ModificationTracker;%0A
 import c
@@ -3585,18 +3585,18 @@
 ing, ?%3E 
-ID
+id
 ) %7B%0A    
@@ -3806,16 +3806,16 @@
 ct, 
-ID), Psi
+id), get
 Modi
@@ -3829,35 +3829,39 @@
 nTracker
-.MODIFICATION_COUNT
+ForIndexId(project, id)
 ),%0A     
@@ -3891,10 +3891,247 @@
 %0A    %7D%0A%0A
+    @NotNull%0A    private static ModificationTracker getModificationTrackerForIndexId(@NotNull Project project, @NotNull final ID%3C?, ?%3E id) %7B%0A        return () -%3E FileBasedIndex.getInstance().getIndexModificationStamp(id, project);%0A    %7D%0A
 %7D%0A

use getIndexModificationStamp to cache index result based on it index change key