/*
 * Copyright 2013-2018 consulo.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package consulo.application.impl;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.AccessToken;
import consulo.logging.Logger;
import com.intellij.openapi.util.AsyncResult;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.TimeoutUtil;
import consulo.application.internal.ApplicationWithOwnWriteThread;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;

/**
 * @author VISTALL
 * @since 2018-01-06
 */
public class WriteThread extends Thread implements Disposable {
  private static class CallInfo {
    private ThrowableComputable myComputable;
    private AsyncResult myResult;
    private Class myCallClass;
    private Exception myCreateTrace;

    private CallInfo(ThrowableComputable computable, AsyncResult result, Class callClass) {
      myComputable = computable;
      myResult = result;
      myCallClass = callClass;
      myCreateTrace = new Exception();
    }
  }

  private static final Logger LOG = Logger.getInstance(WriteThread.class);

  private final Deque<CallInfo> myQueue = new ConcurrentLinkedDeque<>();
  private final ApplicationWithOwnWriteThread myApplication;

  private boolean myStop;

  public WriteThread(@Nullable ApplicationWithOwnWriteThread application) {
    super("Consulo Write Thread");
    myApplication = application;

    setPriority(MAX_PRIORITY);

    start();
  }

  public <T> void push(ThrowableComputable<T, Throwable> computable, AsyncResult<T> result, Class caller) {
    myQueue.addLast(new CallInfo(computable, result, caller));
  }

  @Override
  public void run() {
    while (!myStop) {
      try {
        CallInfo first = myQueue.pollFirst();
        if (first != null) {
          runImpl(first.myCallClass, first.myComputable, first.myResult, first.myCreateTrace);
        }
      }
      finally {
        TimeoutUtil.sleep(100);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private void runImpl(@Nonnull Class caller, @Nonnull ThrowableComputable computable, @Nonnull AsyncResult asyncResult, @Nonnull Exception e) {
    long start = System.currentTimeMillis();
    try {
      Object compute;
      try (AccessToken ignored = myApplication.acquireWriteActionLockInternal(caller)) {
        compute = computable.compute();
      }

      asyncResult.setDone(compute);
    }
    catch (Throwable throwable) {
      String throwableText = ExceptionUtil.getThrowableText(throwable);

      LOG.warn("Exception trace: " + throwableText);
      LOG.warn("Call trace: " + ExceptionUtil.getThrowableText(e));

      asyncResult.rejectWithThrowable(throwable);
    }
    finally {
      long l = System.currentTimeMillis() - start;

      if (l > 1_000L) {
        LOG.warn("Long write operation. Time: " + l, e);
      }
    }
  }

  @Override
  public void dispose() {
    myStop = true;
  }
}
@@ -720,39 +720,8 @@
 en;%0A
-import consulo.logging.Logger;%0A
 impo
@@ -762,16 +762,16 @@
 Result;%0A
+
 import c
@@ -860,46 +860,8 @@
 il;%0A
-import com.intellij.util.TimeoutUtil;%0A
 impo
@@ -922,16 +922,47 @@
 eThread;
+%0Aimport consulo.logging.Logger;
 %0A%0Aimport
@@ -1046,16 +1046,16 @@
 .Deque;%0A
-
 import j
@@ -1096,16 +1096,63 @@
 edDeque;
+%0Aimport java.util.concurrent.locks.LockSupport;
 %0A%0A/**%0A *
@@ -2274,16 +2274,47 @@
 aller));
+%0A%0A    LockSupport.unpark(this);
 %0A  %7D%0A%0A  
@@ -2377,36 +2377,41 @@
     
-try %7B%0A        CallInfo first
+CallInfo task;%0A      while ((task
  = m
@@ -2432,27 +2432,9 @@
 st()
-;%0A        if (first
+)
  != 
@@ -2453,18 +2453,16 @@
     
-  
 runImpl(
 firs
@@ -2457,21 +2457,20 @@
 runImpl(
-first
+task
 .myCallC
@@ -2475,21 +2475,20 @@
 lClass, 
-first
+task
 .myCompu
@@ -2494,21 +2494,20 @@
 utable, 
-first
+task
 .myResul
@@ -2513,13 +2513,12 @@
 lt, 
-first
+task
 .myC
@@ -2540,75 +2540,36 @@
     
-  %7D%0A      %7D%0A      finally %7B%0A        TimeoutUtil.sleep(100);%0A      %7D
+%7D%0A%0A      LockSupport.park();
 %0A   

replace by park