package compat;

import com.intellij.find.findUsages.CustomUsageSearcher;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesHandlerFactory;
import com.intellij.find.findUsages.FindUsagesOptions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.*;
import com.intellij.psi.xml.XmlTag;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageInfoToUsageConverter;
import com.intellij.usages.UsageSearcher;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import utils.ResourceUsageCountUtils;

import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Utils for findUsage
 * Copy from FinsUsageAction & findUsageManager & SearchForUsagesRunnable
 */
class FindUsagesImpl171 {

    private static volatile FindUsagesImpl171 mInstance;

    static FindUsagesImpl171 getInstance() {
        if (mInstance == null) {
            synchronized (FindUsagesImpl171.class) {
                if (mInstance == null) {
                    mInstance = new FindUsagesImpl171();
                }
            }
        }
        return mInstance;
    }

    int findUsage(XmlTag element) {
        final FindUsagesHandler handler = FindUsagesImpl171.getFindUsagesHandler(element, element.getProject());
        if (handler != null) {
            final FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions();
            final PsiElement[] primaryElements = handler.getPrimaryElements();
            final PsiElement[] secondaryElements = handler.getSecondaryElements();
            Factory factory = new Factory() {
                public UsageSearcher create() {
                    return FindUsagesImpl171.createUsageSearcher(primaryElements, secondaryElements, handler, findUsagesOptions, (PsiFile) null);
                }
            };
            UsageSearcher usageSearcher = (UsageSearcher)factory.create();
            final AtomicInteger mCount = new AtomicInteger(0);
            usageSearcher.generate(new Processor<Usage>() {
                @Override
                public boolean process(Usage usage) {
                    if (ResourceUsageCountUtils.isUsefulUsageToCount(usage)) {
                        mCount.incrementAndGet();
                    }
                    return true;
                }
            });
            return mCount.get();
        }
        return 0;
    }

    private static FindUsagesHandler getFindUsagesHandler(PsiElement element, Project project) {
        FindUsagesHandlerFactory[] arrs = (FindUsagesHandlerFactory[]) Extensions.getExtensions(FindUsagesHandlerFactory.EP_NAME, project);
        FindUsagesHandler handler = null;
        int length = arrs.length;
        for (int i = 0; i < length; i++) {
            FindUsagesHandlerFactory arr = arrs[i];
            if (arr.canFindUsages(element)) {
                handler = arr.createFindUsagesHandler(element, false);
                if(handler == FindUsagesHandler.NULL_HANDLER) {
                    return null;
                }

                if(handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

    private static void dropResolveCacheRegularly(ProgressIndicator indicator, final Project project) {
        if (indicator instanceof ProgressIndicatorEx) {
            ((ProgressIndicatorEx)indicator).addStateDelegate(new ProgressIndicatorBase() {
                volatile long lastCleared = System.currentTimeMillis();

                public void setFraction(double fraction) {
                    super.setFraction(fraction);
                    long current = System.currentTimeMillis();
                    if (current - this.lastCleared >= 500L) {
                        this.lastCleared = current;
                        PsiManager.getInstance(project).dropResolveCaches();
                    }

                }
            });
        }

    }



    @NotNull
    private static UsageSearcher createUsageSearcher(@NotNull final PsiElement[] primaryElements, @NotNull final PsiElement[] secondaryElements, @NotNull final FindUsagesHandler handler, @NotNull FindUsagesOptions options, final PsiFile scopeFile) {
        final FindUsagesOptions optionsClone = options.clone();
        return new UsageSearcher() {
            public void generate(@NotNull final Processor<Usage> processor) {
                Project project = (Project) ApplicationManager.getApplication().runReadAction(new Computable() {
                    public Project compute() {
                        return scopeFile != null?scopeFile.getProject():primaryElements[0].getProject();
                    }
                });
                dropResolveCacheRegularly(ProgressManager.getInstance().getProgressIndicator(), project);
                if(scopeFile != null) {
                    optionsClone.searchScope = new LocalSearchScope(scopeFile);
                }

                final CommonProcessors.UniqueProcessor usageInfoProcessor = new CommonProcessors.UniqueProcessor(new Processor<UsageInfo>() {
                    @Override
                    public boolean process(final UsageInfo usageInfo) {
                        Usage usage = (Usage)ApplicationManager.getApplication().runReadAction(new Computable() {
                            public Usage compute() {
                                return UsageInfoToUsageConverter.convert(primaryElements, usageInfo);
                            }
                        });
                        return processor.process(usage);
                    }
                });
                Iterable elements = ContainerUtil.concat(new PsiElement[][]{primaryElements, secondaryElements});
                optionsClone.fastTrack = new SearchRequestCollector(new SearchSession());
                if(optionsClone.searchScope instanceof GlobalSearchScope) {
                    optionsClone.searchScope = optionsClone.searchScope.union(GlobalSearchScope.projectScope(project));
                }

                try {
                    Iterator i$ = elements.iterator();

                    while(i$.hasNext()) {
                        final PsiElement element = (PsiElement)i$.next();
                        ApplicationManager.getApplication().runReadAction(new Runnable() {
                            public void run() {

                            }
                        });
                        handler.processElementUsages(element, usageInfoProcessor, optionsClone);
                        CustomUsageSearcher[] arr$ = (CustomUsageSearcher[])Extensions.getExtensions(CustomUsageSearcher.EP_NAME);
                        int len$ = arr$.length;

                        for(int i$1 = 0; i$1 < len$; ++i$1) {
                            CustomUsageSearcher searcher = arr$[i$1];

                            try {
                                searcher.processElementUsages(element, processor, optionsClone);
                            } catch (IndexNotReadyException var17) {
                                DumbService.getInstance(element.getProject()).showDumbModeNotification("Find usages is not available during indexing");
                            } catch (ProcessCanceledException var18) {
                                throw var18;
                            } catch (Exception var19) {

                            }
                        }
                    }

                    PsiSearchHelper.SERVICE.getInstance(project).processRequests(optionsClone.fastTrack, new Processor<PsiReference>() {
                        public boolean process(final PsiReference ref) {
                            UsageInfo info = (UsageInfo)ApplicationManager.getApplication().runReadAction(new Computable() {
                                public UsageInfo compute() {
                                    return !ref.getElement().isValid()?null:new UsageInfo(ref);
                                }
                            });
                            return info == null || usageInfoProcessor.process(info);
                        }
                    });
                } finally {
                    optionsClone.fastTrack = null;
                }

            }
        };
    }
}