/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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 com.intellij.diagnostic;

import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.ErrorLogger;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.util.containers.ContainerUtil;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Priority;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Mike
 */
public class DialogAppender extends AppenderSkeleton {
  private static final DefaultIdeaErrorLogger DEFAULT_LOGGER = new DefaultIdeaErrorLogger();

  private volatile Runnable myDialogRunnable = null;

  @Override
  protected synchronized void append(@NotNull final LoggingEvent event) {
    if (!event.level.isGreaterOrEqual(Priority.ERROR)) return;

    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        List<ErrorLogger> loggers = new ArrayList<ErrorLogger>();
        loggers.add(DEFAULT_LOGGER);

        Application application = ApplicationManager.getApplication();
        if (application != null) {
          if (application.isHeadlessEnvironment() || application.isDisposed()) return;
          ContainerUtil.addAll(loggers, application.getComponents(ErrorLogger.class));
        }

        appendToLoggers(event, loggers.toArray(new ErrorLogger[loggers.size()]));
      }
    });
  }

  void appendToLoggers(@NotNull LoggingEvent event, @NotNull ErrorLogger[] errorLoggers) {
    if (myDialogRunnable != null) {
      return;
    }

    final IdeaLoggingEvent ideaEvent;
    final Object message = event.getMessage();
    if (message instanceof IdeaLoggingEvent) {
      ideaEvent = (IdeaLoggingEvent)message;
    }
    else {
      ThrowableInformation throwable = event.getThrowableInformation();
      if (throwable == null) {
        return;
      }
      ideaEvent = new IdeaLoggingEvent(message == null ? "<null> " : message.toString(), throwable.getThrowable());
    }
    for (int i = errorLoggers.length - 1; i >= 0; i--) {
      final ErrorLogger logger = errorLoggers[i];
      if (!logger.canHandle(ideaEvent)) {
        continue;
      }
      myDialogRunnable = new Runnable() {
        @Override
        public void run() {
          try {
            logger.handle(ideaEvent);
          }
          finally {
            myDialogRunnable = null;
          }
        }
      };

      final Application app = ApplicationManager.getApplication();
      if (app == null) {
        new Thread(myDialogRunnable).start();
      }
      else {
        app.executeOnPooledThread(myDialogRunnable);
      }
      break;
    }
  }

  @TestOnly
  Runnable getDialogRunnable() {
    return myDialogRunnable;
  }

  @Override
  public boolean requiresLayout() {
    return false;
  }

  @Override
  public void close() {
  }
}


@@ -901,16 +901,52 @@
 erUtil;%0A
+import com.intellij.util.ui.UIUtil;%0A
 import o
@@ -1263,16 +1263,66 @@
 il.List;
+%0Aimport java.util.concurrent.atomic.AtomicInteger;
 %0A%0A/**%0A *
@@ -1491,60 +1491,192 @@
 ();%0A
-%0A  private volatile Runnable myDialogRunnable = null
+  private static final int MAX_ASYNC_LOGGING_EVENTS = 5;%0A%0A  private volatile Runnable myDialogRunnable = null;%0A  private final AtomicInteger myPendingAppendCounts = new AtomicInteger()
 ;%0A%0A 
@@ -1832,35 +1832,26 @@
     
-SwingUtilities.invokeLater(
+Runnable action = 
 new 
@@ -1897,32 +1897,48 @@
 ic void run() %7B%0A
+        try %7B%0A  
         List%3CErr
@@ -1991,24 +1991,26 @@
 ();%0A        
+  
 loggers.add(
@@ -2027,16 +2027,18 @@
 GGER);%0A%0A
+  
         
@@ -2100,32 +2100,34 @@
 tion();%0A        
+ 
+ 
 if (application 
@@ -2129,32 +2129,34 @@
 tion != null) %7B%0A
+  
           if (ap
@@ -2222,24 +2222,26 @@
 ()) return;%0A
+  
           Co
@@ -2319,27 +2319,31 @@
 ));%0A        
+  
 %7D%0A%0A
+  
         appe
@@ -2422,17 +2422,466 @@
     
+  
 %7D%0A    
-%7D);
+    finally %7B%0A          myPendingAppendCounts.decrementAndGet();%0A        %7D%0A      %7D%0A    %7D;%0A%0A    if (myPendingAppendCounts.addAndGet(1) %3E MAX_ASYNC_LOGGING_EVENTS) %7B%0A      // Stop adding requests to the queue or we can get OOME on pending logging requests (IDEA-95327)%0A      // Note, we MUST avoid SYNCHRONOUS invokeAndWait to prevent deadlocks%0A      // UIUtil.invokeAndWaitIfNeeded(action);%0A    %7D else %7B%0A      SwingUtilities.invokeLater(action);%0A    %7D
 %0A  %7D

fix for too many pending invokation events [r=Maxim.Shafirov]