package com.intellij.refactoring.move.moveClassesOrPackages;

import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.util.RefactoringConflictsUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Function;
import com.intellij.util.containers.MultiMap;
import com.intellij.psi.util.FileTypeUtils;

import java.util.*;

public class JavaMoveDirectoryWithClassesHelper extends MoveDirectoryWithClassesHelper {

  @Override
  public void findUsages(Collection<PsiFile> filesToMove,
                         PsiDirectory[] directoriesToMove,
                         Collection<UsageInfo> usages,
                         boolean searchInComments,
                         boolean searchInNonJavaFiles,
                         Project project) {
    final Set<String> packageNames = new HashSet<String>();
    for (PsiFile psiFile : filesToMove) {
      if (psiFile instanceof PsiClassOwner) {
        final PsiClass[] classes = ((PsiClassOwner)psiFile).getClasses();
        for (PsiClass aClass : classes) {
          Collections
            .addAll(usages, MoveClassesOrPackagesUtil.findUsages(aClass, searchInComments, searchInNonJavaFiles, aClass.getName()));
        }
        packageNames.add(((PsiClassOwner)psiFile).getPackageName());
      }
    }

    final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
    for (String packageName : packageNames) {
      final PsiPackage aPackage = psiFacade.findPackage(packageName);
      if (aPackage != null) {
        boolean remainsNothing = true;
        for (PsiDirectory packageDirectory : aPackage.getDirectories()) {
          if (!isUnderRefactoring(packageDirectory, directoriesToMove)) {
            remainsNothing = false;
            break;
          }
        }
        if (remainsNothing) {
          for (PsiReference reference : ReferencesSearch.search(aPackage, GlobalSearchScope.projectScope(project))) {
            final PsiElement element = reference.getElement();
            final PsiImportStatementBase statementBase = PsiTreeUtil.getParentOfType(element, PsiImportStatementBase.class);
            if (statementBase != null && statementBase.isOnDemand()) {
              usages.add(new RemoveOnDemandImportStatementsUsageInfo(statementBase));
            }
          }
        }
      }
    }
  }

  private static boolean isUnderRefactoring(PsiDirectory packageDirectory, PsiDirectory[] directoriesToMove) {
    for (PsiDirectory directory : directoriesToMove) {
      if (PsiTreeUtil.isAncestor(directory, packageDirectory, true)) {
        return true;
      }
    }
    return false;
  }

  @Override
  public boolean move(PsiFile file,
                      PsiDirectory moveDestination,
                      Map<PsiElement, PsiElement> oldToNewElementsMapping,
                      List<PsiFile> movedFiles,
                      RefactoringElementListener listener) {
    if (!(file instanceof PsiClassOwner)) {
      return false;
    }

    if (FileTypeUtils.isInServerPageFile(file)) {
      return false;
    }

    for (PsiClass psiClass : ((PsiClassOwner)file).getClasses()) {
      final PsiClass newClass = MoveClassesOrPackagesUtil.doMoveClass(psiClass, moveDestination);
      oldToNewElementsMapping.put(psiClass, newClass);
      listener.elementMoved(newClass);
    }
    return true;
  }

  @Override
  public void postProcessUsages(UsageInfo[] usages, Function<PsiDirectory, PsiDirectory> newDirMapper) {
    for (UsageInfo usage : usages) {
      if (usage instanceof RemoveOnDemandImportStatementsUsageInfo) {
        final PsiElement element = usage.getElement();
        if (element != null) {
          element.delete();
        }
      }
    }
  }

  @Override
  public void preprocessUsages(Project project,
                               Set<PsiFile> files,
                               UsageInfo[] infos,
                               PsiDirectory directory,
                               MultiMap<PsiElement, String> conflicts) {
    RefactoringConflictsUtil.analyzeModuleConflicts(project, files, infos, directory, conflicts);
  }

  @Override
  public void beforeMove(PsiFile psiFile) {
    ChangeContextUtil.encodeContextInfo(psiFile, true);
  }

  @Override
  public void afterMove(PsiElement newElement) {
    ChangeContextUtil.decodeContextInfo(newElement, null, null);
  }

  private static class RemoveOnDemandImportStatementsUsageInfo extends UsageInfo {
    public RemoveOnDemandImportStatementsUsageInfo(PsiImportStatementBase statementBase) {
      super(statementBase);
    }
  }
}

@@ -3262,24 +3262,149 @@
 lse;%0A    %7D%0A%0A
+    final PsiClass%5B%5D classes = ((PsiClassOwner)file).getClasses();%0A    if (classes.length == 0) %7B%0A      return false;%0A    %7D%0A%0A
     if (File
@@ -3501,42 +3501,15 @@
 s : 
-((PsiClassOwner)file).getClasses()
+classes
 ) %7B%0A

move directory with classes: don't move as java if no classes is provided for java file (IDEA-143778)