package jetbrains.mps.ide.vcs.test.merge;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vcs.FileStatusListener;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import jetbrains.mps.vcs.changesmanager.SimpleCommandQueue;
import jetbrains.mps.vcs.changesmanager.CurrentDifferenceRegistry;
import jetbrains.mps.ide.platform.watching.ReloadManager;

@GeneratedClass(node = "r:b4fd80fc-7d6c-4c99-be6d-090ae8779bdc(jetbrains.mps.ide.vcs.test.merge@tests)/5876208808348878676", model = "r:b4fd80fc-7d6c-4c99-be6d-090ae8779bdc(jetbrains.mps.ide.vcs.test.merge@tests)")
/*package*/ class ChangesManagerTestWaitHelper implements Disposable {
  private boolean myWaitCompleted = true;
  private final Object myWaitLock = new Object();

  private Project myProject;


  public ChangesManagerTestWaitHelper(Project p) {
    myProject = p;
  }

  @Override
  public void dispose() {
  }

  public void waitForFileStatusChange(@NotNull final VirtualFile file, @NotNull final FileStatus expectedFileStatus) {
    final FileStatusManager fsm = FileStatusManager.getInstance(myProject);
    // In case fs is not updated, we need to stop waiting, so set timeout to 5 seconds
    int fsUpdateTimeout = 5000;
    waitForSomething(() -> {
      FileStatusListener listener;
      final _FunctionTypes._void_P0_E0 stopIfNeeded = () -> {
        if (expectedFileStatus == fsm.getStatus(file)) {
          // TODO: check attachFileStatusListener() uses correct Disposable
          // Wait until changes manager is notified about changed file status
          try {
            Thread.sleep(100);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          waitCompleted();
        }
      };
      listener = new FileStatusListener() {
        @Override
        public void fileStatusesChanged() {
          stopIfNeeded.invoke();
        }
        @Override
        public void fileStatusChanged(@NotNull VirtualFile f) {
          stopIfNeeded.invoke();
        }
      };
      fsm.addFileStatusListener(listener, ChangesManagerTestWaitHelper.this);
      VcsDirtyScopeManager.getInstance(myProject).fileDirty(file);
      stopIfNeeded.invoke();
    }, fsUpdateTimeout);
  }

  private class WaitForChangesManagerTask implements Runnable {
    @Override
    public void run() {
      SimpleCommandQueue commandQueue = CurrentDifferenceRegistry.getInstance(myProject).getCommandQueue();
      if (commandQueue.isEmpty()) {
        waitCompleted();
      } else {
        commandQueue.addTask(this);
      }
    }
  }
  public void waitForDiffRegistry() {
    waitForSomething(() -> CurrentDifferenceRegistry.getInstance(myProject).getCommandQueue().addTask(new WaitForChangesManagerTask()), -1);
  }

  public void waitForReloadFinished() {
    ReloadManager.getInstance().flush();
  }

  /**
   * 
   * @param waitScheduling is runnable with work wait to finish, must call {@link ChangesManagerTestWaitHelper#waitCompleted()}
   * @param timeout time to wait for runnable to be finished in milliseconds, -1 for wait until myWaitCompleted
   */
  private void waitForSomething(Runnable waitScheduling, int timeout) {
    synchronized (myWaitLock) {
      assert myWaitCompleted;
      myWaitCompleted = false;
      waitScheduling.run();
      if (timeout > 0) {
        while (!(myWaitCompleted)) {
          try {
            myWaitLock.wait(timeout);
          } catch (InterruptedException e) {
            e.printStackTrace();
          } finally {
            if (!(myWaitCompleted)) {
              // Reset flag for next calls
              myWaitCompleted = true;
              System.err.print("Runnable did not call waitCompleted. Ended by timeout");
            }
          }
        }
      } else {
        while (!(myWaitCompleted)) {
          try {
            myWaitLock.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

  private void waitCompleted() {
    synchronized (myWaitLock) {
      assert !(myWaitCompleted);
      myWaitCompleted = true;
      myWaitLock.notify();
    }
  }
}
