package com.intellij.openapi.vcs.changes;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vfs.VirtualFile;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author max
 */
public class VirtualFileHolder {
  private Set<VirtualFile> myFiles = new HashSet<VirtualFile>();
  private Project myProject;

  public VirtualFileHolder(Project project) {
    myProject = project;
  }

  public synchronized void cleanAll() {
    myFiles.clear();
  }

  public void cleanScope(final VcsDirtyScope scope) {
    ApplicationManager.getApplication().runReadAction(new Runnable() {
      public void run() {
        // to avoid deadlocks caused by incorrect lock ordering, need to lock on this after taking read action
        synchronized(VirtualFileHolder.this) {
          if (myProject.isDisposed()) return;
          final List<VirtualFile> currentFiles = new ArrayList<VirtualFile>(myFiles);
          for (VirtualFile file : currentFiles) {
            if (fileDropped(file) || scope.belongsTo(new FilePathImpl(file))) {
              myFiles.remove(file);
            }
          }
        }
      }
    });
  }

  private boolean fileDropped(final VirtualFile file) {
    return !file.isValid() || ProjectLevelVcsManager.getInstance(myProject).getVcsFor(file) == null;
  }

  public synchronized void addFile(VirtualFile file) {
    myFiles.add(file);
  }

  public synchronized void removeFile(VirtualFile file) {
    myFiles.remove(file);
  }

  public synchronized List<VirtualFile> getFiles() {
    return new ArrayList<VirtualFile>(myFiles);
  }

  public synchronized VirtualFileHolder copy() {
    final VirtualFileHolder copyHolder = new VirtualFileHolder(myProject);
    copyHolder.myFiles.addAll(myFiles);
    return copyHolder;
  }

  public synchronized boolean containsFile(final VirtualFile file) {
    return myFiles.contains(file);
  }

  public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    final VirtualFileHolder that = (VirtualFileHolder)o;

    if (!myFiles.equals(that.myFiles)) return false;

    return true;
  }

  public int hashCode() {
    return myFiles.hashCode();
  }
}

@@ -141,16 +141,72 @@
 roject;%0A
+import com.intellij.openapi.vcs.ProjectLevelVcsManager;%0A
 import c
@@ -275,38 +275,24 @@
 api.vcs.
-ProjectLevelVcsManager
+FilePath
 ;%0Aimport
@@ -1069,16 +1069,37 @@
 sposed()
+ %7C%7C myFiles.isEmpty()
 ) return
@@ -1100,16 +1100,16 @@
 return;%0A
-
         
@@ -1182,24 +1182,395 @@
 %3E(myFiles);%0A
+          if (scope.getRecursivelyDirtyDirectories().size() == 0) %7B%0A            final Set%3CFilePath%3E dirtyFiles = scope.getDirtyFiles();%0A            for(FilePath dirtyFile: dirtyFiles) %7B%0A              VirtualFile f = dirtyFile.getVirtualFile();%0A              if (f != null) %7B%0A                myFiles.remove(f);%0A              %7D%0A            %7D%0A          %7D%0A          else %7B%0A  
           fo
@@ -1599,32 +1599,34 @@
 currentFiles) %7B%0A
+  
             if (
@@ -1695,32 +1695,34 @@
 %7B%0A              
+  
 myFiles.remove(f
@@ -1727,16 +1727,32 @@
 (file);%0A
+              %7D%0A
         

optimize cleanScope() for cases when dirty scope only contains files (IDEADEV-23621)