/*
 * Copyright 2000-2016 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.reporting;

import com.intellij.diagnostic.ThreadDumper;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;

public class FreezeLoggerImpl extends FreezeLogger {
  
  private static final Logger LOG = Logger.getInstance(FreezeLoggerImpl.class);
  private static final Alarm ALARM = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, ApplicationManager.getApplication());
  private static final int MAX_ALLOWED_TIME = 500;
  
  @Override
  public void runUnderPerformanceMonitor(@Nullable Project project, @NotNull Runnable action) {
    if (isUnderDebug() || ApplicationManager.getApplication().isUnitTestMode()) {
      action.run();
      return;
    }
    
    final ModalityState initial = ModalityState.current();
    ALARM.cancelAllRequests();
    ALARM.addRequest(() -> dumpThreads(project, initial), MAX_ALLOWED_TIME);
    
    try {
      action.run();
    }
    finally {
      ALARM.cancelAllRequests();
    }
  }
  
  private static void dumpThreads(@Nullable Project project, @NotNull ModalityState initialState) {
    if (!initialState.equals(ModalityState.current())) {
      return;
    }
    
    final ThreadInfo[] infos = ThreadDumper.getThreadInfos();
    final String edtTrace = ThreadDumper.dumpEdtStackTrace(infos);
    if (edtTrace.contains("java.lang.ClassLoader.loadClass")) {
      return;
    }

    final boolean isInDumbMode = project != null && !project.isDisposed() && DumbService.isDumb(project);
    final String dumps = ThreadDumper.dumpThreadsToString();
    final String msg = "Typing freeze report, (DumbMode=" + isInDumbMode + ") thread dumps attached. EDT stacktrace:\n"
                 + edtTrace
                 + "\n\n\n";

    if (Registry.is("typing.freeze.report.dumps")) {
      ThreadDumpInfo info = new ThreadDumpInfo(infos, isInDumbMode);
      String report = ReporterKt.createReportLine("typing-freeze-dumps", info);
      if (!StatsSender.INSTANCE.send(report, true)) {
        LOG.debug("Error while reporting thread dump");
      }
    }
    else {
      LOG.error(msg, dumps);
    }
  }
  
  private static boolean isUnderDebug() {
    return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp");
  }
  
}

class ThreadDumpInfo {
  public final ThreadInfo[] threadInfos;
  public final String version;
  public final String product;
  public final String buildNumber;
  public final boolean isEAP;
  public final boolean isInDumbMode;

  public ThreadDumpInfo(ThreadInfo[] threadInfos, boolean isInDumbMode) {
    this.threadInfos = threadInfos;
    this.product = ApplicationInfo.getInstance().getVersionName();
    this.version = ApplicationInfo.getInstance().getFullVersion();
    this.buildNumber = ApplicationInfo.getInstance().getBuild().toString();
    this.isEAP = ApplicationManager.getApplication().isEAP();
    this.isInDumbMode = isInDumbMode;
  }
}
@@ -1667,16 +1667,35 @@
     if (
+!shouldReport() %7C%7C 
 isUnderD
@@ -2062,34 +2062,133 @@
 ts();%0A    %7D%0A  %7D%0A
+%0A
   
+private static boolean shouldReport() %7B%0A    return Registry.is(%22typing.freeze.report.dumps%22);%0A  %7D%0A
 %0A  private stati
@@ -2276,90 +2276,8 @@
 ) %7B%0A
-    if (!initialState.equals(ModalityState.current())) %7B%0A      return;%0A    %7D%0A    %0A
     
@@ -2596,297 +2596,373 @@
 t);%0A
+%0A
     
-final String dumps = ThreadDumper.dumpThreadsToString();%0A    fin
+ApplicationManager.getApplication().invokeLater(() -%3E %7B%0A      if (!initi
 al
- 
 St
-ring msg = %22Typing freeze report, (DumbMode=%22 + isInDumbMode + %22) thread dumps attached. EDT stacktrace:%5Cn%22%0A                 + edtTrace%0A                 + %22%5Cn%5Cn%5Cn%22;%0A%0A    if (Registry.is(%22typing.freeze.report.dumps%22))
+ate.equals(ModalityState.current())) return;%0A      sendDumpsInBackground(infos, isInDumbMode);%0A    %7D, ModalityState.any());%0A  %7D%0A%0A  private static void sendDumpsInBackground(ThreadInfo%5B%5D infos, boolean isInDumbMode) %7B%0A    ApplicationManager.getApplication().executeOnPooledThread(() -%3E
  %7B%0A 
@@ -3236,61 +3236,15 @@
    %7D
-%0A    else %7B%0A      LOG.error(msg, dumps);%0A    %7D
+);
 %0A  %7D%0A
-  
 %0A  p

check modality state under invokeLater