package com.intellij.localvcsintegr;


import com.intellij.localvcs.core.Paths;
import com.intellij.localvcs.utils.RunnableAdapter;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.*;

import java.io.File;
import java.io.IOException;

public class ExternalChangesAndRefreshingTest extends IntegrationTestCase {
  public void testRefreshingSynchronously() throws Exception {
    doTestRefreshing(false);
  }

  public void testRefreshingAsynchronously() throws Exception {
    doTestRefreshing(true);
  }

  private void doTestRefreshing(boolean async) throws Exception {
    final String path1 = createFileExternally("f1.java");
    final String path2 = createFileExternally("f2.java");

    assertFalse(hasVcsEntry(path1));
    assertFalse(hasVcsEntry(path2));

    refreshVFS(async, new Runnable() {
      public void run() {
        assertTrue(hasVcsEntry(path1));
        assertTrue(hasVcsEntry(path2));

        assertEquals(2, getVcsRevisionsFor(root).size());
      }
    });
  }

  public void testRefreshDuringCommand() {
    // shouldn't throw
    CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
      public void run() {
        refreshVFS();
      }
    }, "", null);
  }

  public void testCommandDuringRefresh() throws Exception {
    createFileExternally("f.java");

    VirtualFileListener l = new VirtualFileAdapter() {
      @Override
      public void fileCreated(VirtualFileEvent e) {
        executeSomeCommand();
      }
    };

    // shouldn't throw
    addFileListenerDuring(l, new Runnable() {
      public void run() {
        refreshVFS();
      }
    });
  }

  private void executeSomeCommand() {
    CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
      public void run() {
      }
    }, "", null);
  }

  public void testContentOfFileChangedDuringRefresh() throws Exception {
    final VirtualFile f = root.createChildData(null, "file.java");
    f.setBinaryContent("before".getBytes());

    performAllPendingJobs();

    ContentChangesListener l = new ContentChangesListener(f);
    addFileListenerDuring(l, new RunnableAdapter() {
      @Override
      public void doRun() throws IOException {
        changeFileExternally(f.getPath(), "after");
        refreshVFS();
      }
    });

    // todo unrelable test because content recorded before LvcsFileListener does its job
    assertEquals("before", l.getContentBefore());
    assertEquals("after", l.getContentAfter());
  }

  private void performAllPendingJobs() {
    refreshVFS();
  }

  public void testFileCreationDuringRefresh() throws Exception {
    final String path = createFileExternally("f.java");
    changeFileExternally(path, "content");

    final String[] content = new String[1];
    VirtualFileListener l = new VirtualFileAdapter() {
      @Override
      public void fileCreated(VirtualFileEvent e) {
        try {
          if (!e.getFile().getPath().equals(path)) return;
          content[0] = new String(e.getFile().contentsToByteArray());
        }
        catch (IOException ex) {
          throw new RuntimeException(ex);
        }
      }
    };

    addFileListenerDuring(l, new Runnable() {
      public void run() {
        refreshVFS();
      }
    });
    assertEquals("content", content[0]);
  }

  public void ignoreTestCreationOfExcludedDirectoryDuringRefresh() throws Exception {
    // todo does not work due to FileListener order. FileIndex gets event later than Lvcs.

    VirtualFile dir = root.createChildDirectory(null, "EXCLUDED");
    String p = dir.getPath();

    assertTrue(hasVcsEntry(p));

    ModifiableRootModel m = ModuleRootManager.getInstance(myModule).getModifiableModel();
    m.getContentEntries()[0].addExcludeFolder(dir);
    m.commit();

    assertFalse(hasVcsEntry(p));

    dir.delete(null);

    createDirectoryExternally("EXCLUDED");
    refreshVFS();

    assertFalse(hasVcsEntry(p));
  }

  public void testDeletionOfFilteredDirectoryExternallyDoesNotThrowExceptionDuringRefresh() throws Exception {
    VirtualFile f = root.createChildDirectory(null, EXCLUDED_DIR_NAME);
    String path = Paths.appended(root.getPath(), EXCLUDED_DIR_NAME);

    assertFalse(hasVcsEntry(path));

    new File(path).delete();
    refreshVFS();

    assertFalse(hasVcsEntry(path));
  }

  private void refreshVFS() {
    refreshVFS(false, null);
  }

  private void refreshVFS(boolean async, Runnable after) {
    VirtualFileManager.getInstance().refresh(async, after);
  }
}
@@ -371,16 +371,55 @@
 ception;
+%0Aimport java.util.concurrent.Semaphore;
 %0A%0Apublic
@@ -674,32 +674,417 @@
 ing(true);%0A  %7D%0A%0A
+  @Override%0A  protected void runBareRunnable(Runnable r) throws Throwable %7B%0A    if (getName().equals(%22testRefreshingAsynchronously%22)) %7B%0A      // this methods waits for another thread to finish, that leds%0A      // to deadlock in swing-thread. Therefore we have to run this test%0A      // outside of swing-thread%0A      r.run();%0A    %7D%0A    else %7B%0A      super.runBareRunnable(r);%0A    %7D%0A  %7D%0A%0A
   private void d
@@ -1129,38 +1129,32 @@
 Exception %7B%0A    
-final 
 String path1 = c
@@ -1185,30 +1185,24 @@
 java%22);%0A    
-final 
 String path2
@@ -1309,24 +1309,83 @@
 y(path2));%0A%0A
+    final Semaphore s = new Semaphore(1);%0A    s.acquire();%0A
     refreshV
@@ -1437,24 +1437,75 @@
 run() %7B%0A    
+    s.release();%0A      %7D%0A    %7D);%0A    s.acquire();%0A%0A
     assertTr
@@ -1524,28 +1524,24 @@
 ry(path1));%0A
-    
     assertTr
@@ -1569,20 +1569,16 @@
 );%0A%0A    
-  
-  
 assertEq
@@ -1619,32 +1619,16 @@
 ize());%0A
-      %7D%0A    %7D);%0A
   %7D%0A%0A  p

test fix