package com.intellij.openapi.util.diff.util;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.util.containers.Convertor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/*
 * Executor to perform <possibly> long operations on pooled thread
 * Is is used to reduce blinking, in case of fast end of background task.
 */
public class WaitingBackgroundableTaskExecutor {
  private static final Runnable TOO_SLOW_OPERATION = new EmptyRunnable();

  private int myModificationStamp = 0;
  @Nullable private ProgressIndicator myProgressIndicator;

  @CalledInAwt
  public int getModificationStamp() {
    return myModificationStamp;
  }

  @CalledInAwt
  public void abort() {
    if (myProgressIndicator != null) {
      myProgressIndicator.cancel();
      myProgressIndicator = null;
      myModificationStamp++;
    }
  }

  @CalledInAwt
  public void execute(@NotNull final Convertor<ProgressIndicator, Runnable> backgroundTask,
                      @Nullable final Runnable onSlowAction,
                      final int waitMillis) {
    execute(backgroundTask, onSlowAction, waitMillis, false);
  }

  @CalledInAwt
  public void execute(@NotNull final Convertor<ProgressIndicator, Runnable> backgroundTask,
                      @Nullable final Runnable onSlowAction,
                      final int waitMillis,
                      final boolean forceEDT) {
    abort();

    myModificationStamp++;
    final int modificationStamp = myModificationStamp;

    myProgressIndicator = new EmptyProgressIndicator();
    final ProgressIndicator indicator = myProgressIndicator;

    final Semaphore semaphore = new Semaphore(0);
    final AtomicReference<Runnable> resultRef = new AtomicReference<Runnable>();

    if (forceEDT) {
      Runnable result = backgroundTask.convert(indicator);
      finish(result, modificationStamp, indicator);
    }
    else {
      ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
        @Override
        public void run() {
          final Runnable result = backgroundTask.convert(indicator);

          if (indicator.isCanceled()) {
            semaphore.release();
            return;
          }

          if (!resultRef.compareAndSet(null, result)) {
            ApplicationManager.getApplication().invokeLater(new Runnable() {
              @Override
              public void run() {
                finish(result, modificationStamp, indicator);
              }
            }, ModalityState.any());
          }
          semaphore.release();
        }
      });

      try {
        semaphore.tryAcquire(waitMillis, TimeUnit.MILLISECONDS);
      }
      catch (InterruptedException ignore) {
      }
      if (!resultRef.compareAndSet(null, TOO_SLOW_OPERATION)) {
        // update presentation in the same thread to reduce blinking, caused by 'invokeLater' and fast background operation
        finish(resultRef.get(), modificationStamp, indicator);
      }
      else {
        if (onSlowAction != null) onSlowAction.run();
      }
    }
  }

  @CalledInAwt
  private void finish(@NotNull Runnable result, int modificationStamp, @NotNull ProgressIndicator indicator) {
    if (indicator.isCanceled()) return;
    if (myModificationStamp != modificationStamp) return;

    result.run();
  }
}

@@ -263,32 +263,86 @@
 gressIndicator;%0A
+import com.intellij.openapi.progress.ProgressManager;%0A
 import com.intel
@@ -1931,58 +1931,238 @@
     
-myProgressIndicator = new EmptyProgressIndicator()
+final ModalityState modality = ModalityState.current();%0A    myProgressIndicator = new EmptyProgressIndicator() %7B%0A      @NotNull%0A      @Override%0A      public ModalityState getModalityState() %7B%0A        return modality;%0A      %7D%0A    %7D
 ;%0A  
@@ -2620,32 +2620,175 @@
 ic void run() %7B%0A
+          ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A    
           final 
@@ -2847,24 +2847,28 @@
 %0A%0A          
+    
 if (indicato
@@ -2889,32 +2889,36 @@
 ) %7B%0A            
+ 
+   
 semaphore.releas
@@ -2914,32 +2914,36 @@
 hore.release();%0A
+    
             retu
@@ -2956,19 +2956,27 @@
         
-%7D%0A%0A
+    %7D%0A%0A    
         
@@ -3019,24 +3019,28 @@
  result)) %7B%0A
+    
             
@@ -3096,32 +3096,36 @@
 ew Runnable() %7B%0A
+    
               @O
@@ -3138,32 +3138,36 @@
 e%0A              
+    
 public void run(
@@ -3162,32 +3162,36 @@
 ic void run() %7B%0A
+    
                 
@@ -3242,34 +3242,41 @@
 ;%0A              
-%7D%0A
+    %7D%0A   
             %7D, M
@@ -3275,30 +3275,20 @@
     
+ 
 %7D, 
-M
+m
 odality
-State.any()
 );%0A 
@@ -3288,34 +3288,42 @@
 ity);%0A          
+  
-%7D%0A
+  %7D%0A    
           semaph
@@ -3329,32 +3329,71 @@
 hore.release();%0A
+            %7D%0A          %7D, indicator);%0A
         %7D%0A      

fix infinite loading of DiffRequest in some cases (on creating of temporal file with active modal dialog)