/*
 * Copyright (c) 2005 Your Corporation. All Rights Reserved.
 */
package com.intellij.psi.impl.search;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadActionProcessor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.search.*;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.util.Processor;
import com.intellij.util.QueryExecutor;

/**
 * @author max
 */
public class MethodUsagesSearcher implements QueryExecutor<PsiReference, MethodReferencesSearch.SearchParameters> {
  public boolean execute(final MethodReferencesSearch.SearchParameters p, final Processor<PsiReference> consumer) {
    final PsiMethod method = p.getMethod();
    SearchScope searchScope = p.getScope();
    PsiManager psiManager = PsiManager.getInstance(method.getProject());
    final boolean isStrictSignatureSearch = p.isStrictSignatureSearch();

    final PsiClass aClass = method.getContainingClass();
    if (aClass == null) return true;

    if (method.isConstructor()) {
      final ConstructorReferencesSearchHelper helper = new ConstructorReferencesSearchHelper(psiManager);
      if (!helper.processConstructorReferences(consumer, method, searchScope, !isStrictSignatureSearch, isStrictSignatureSearch)) {
        return false;
      }
    }

    PsiClass parentClass = method.getContainingClass();
    if (isStrictSignatureSearch && (parentClass == null
                                    || parentClass instanceof PsiAnonymousClass
                                    || parentClass.hasModifierProperty(PsiModifier.FINAL)
                                    || method.hasModifierProperty(PsiModifier.STATIC)
                                    || method.hasModifierProperty(PsiModifier.FINAL)
                                    || method.hasModifierProperty(PsiModifier.PRIVATE))
      ) {
      return ReferencesSearch.search(method, searchScope, false).forEach(new ReadActionProcessor<PsiReference>() {
        public boolean processInReadAction(final PsiReference psiReference) {
          return consumer.process(psiReference);
        }
      });
    }

    final String text = method.getName();
    final PsiMethod[] methods = isStrictSignatureSearch ? new PsiMethod[]{method} : getOverloads(method);

    SearchScope accessScope = ApplicationManager.getApplication().runReadAction(new Computable<SearchScope>() {
      public SearchScope compute() {
        SearchScope accessScope = methods[0].getUseScope();
        for (int i = 1; i < methods.length; i++) {
          PsiMethod method1 = methods[i];
          SearchScope someScope = PsiSearchScopeUtil.scopesUnion(accessScope, method1.getUseScope());
          accessScope = someScope == null ? accessScope : someScope;
        }
        return accessScope;
      }
    });

    final TextOccurenceProcessor processor1 = new MethodTextOccurenceProcessor(consumer, aClass, isStrictSignatureSearch, methods);

    searchScope = searchScope.intersectWith(accessScope);

    short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES;
    boolean toContinue = psiManager.getSearchHelper().processElementsWithWord(processor1,
                                                                              searchScope,
                                                                              text,
                                                                              searchContext, true);
    if (!toContinue) return false;

    final String propertyName = ApplicationManager.getApplication().runReadAction(new Computable<String>(){
      public String compute() {
        return PropertyUtil.getPropertyName(method);
      }
    });
    if (propertyName != null) {
      if (searchScope instanceof GlobalSearchScope) {
        GlobalSearchScope restrictedSeachScope = GlobalSearchScope.getScopeRestrictedByFileTypes(
          (GlobalSearchScope)searchScope,
          StdFileTypes.JSP,
          StdFileTypes.JSPX,
          StdFileTypes.XML,
          StdFileTypes.XHTML
        );
        toContinue = psiManager.getSearchHelper().processElementsWithWord(processor1,
                                                                        restrictedSeachScope,
                                                                        propertyName,
                                                                        UsageSearchContext.IN_FOREIGN_LANGUAGES, true);
      }
      else {
        toContinue = psiManager.getSearchHelper().processElementsWithWord(processor1,
                                                                        searchScope,
                                                                        propertyName,
                                                                        UsageSearchContext.IN_FOREIGN_LANGUAGES, true);
      }
      if (!toContinue) return false;

      for (CustomPropertyScopeProvider provider : Extensions.getExtensions(CustomPropertyScopeProvider.EP_NAME)) {
        final SearchScope scope = searchScope.intersectWith(provider.getScope(psiManager.getProject()));
        toContinue = psiManager.getSearchHelper().processElementsWithWord(processor1,
                                                                          scope,
                                                                          propertyName,
                                                                          UsageSearchContext.IN_FOREIGN_LANGUAGES, true);
        if (!toContinue) return false;
      }
    }

    return true;
  }

  private static PsiMethod[] getOverloads(final PsiMethod method) {
    return ApplicationManager.getApplication().runReadAction(new Computable<PsiMethod[]>() {
      public PsiMethod[] compute() {
        PsiClass aClass = method.getContainingClass();
        if (aClass == null) return new PsiMethod[]{method};
        return aClass.findMethodsByName(method.getName(), false);
      }
    });
  }

}

@@ -1566,24 +1566,30 @@
 %0A    %7D%0A%0A    
+final 
 PsiClass par
@@ -1632,20 +1632,178 @@
 ();%0A    
-if (
+boolean needStrictSignatureSearch = ApplicationManager.getApplication().runReadAction(new Computable%3CBoolean%3E() %7B%0A      public Boolean compute() %7B%0A        return 
 isStrict
@@ -1870,32 +1870,36 @@
                 
+   
+ 
 %7C%7C parentClass i
@@ -1926,16 +1926,20 @@
 usClass%0A
+    
         
@@ -2048,32 +2048,36 @@
                 
+   
+ 
 %7C%7C method.hasMod
@@ -2138,32 +2138,36 @@
                 
+    
 %7C%7C method.hasMod
@@ -2227,32 +2227,36 @@
                 
+  
+  
 %7C%7C method.hasMod
@@ -2290,23 +2290,82 @@
 RIVATE))
+;
 %0A      
+%7D%0A    %7D).booleanValue();%0A    if (needStrictSignatureSearch
 ) %7B%0A    

fix read access assertion in Scala (IDEADEV-34253)