package games.strategy.debug;

import java.awt.Frame;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import games.strategy.engine.ClientContext;
import games.strategy.engine.framework.GameRunner;
import games.strategy.engine.framework.system.Memory;

/**
 * Moved out of Console class, so that we don't need swing.
 */
public class DebugUtils {
  private static final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();

  public static String getThreadDumps() {
    final StringBuilder result = new StringBuilder();
    result.append("THREAD DUMP\n");
    final ThreadInfo[] threadInfo = threadMxBean.getThreadInfo(threadMxBean.getAllThreadIds(), Integer.MAX_VALUE);
    for (final ThreadInfo info : threadInfo) {
      if (info != null) {
        result.append("thread<").append(info.getThreadId()).append(",").append(info.getThreadName()).append(">\n")
            .append("state:")
            .append(info.getThreadState()).append("\n");
        if (info.getLockName() != null) {
          result.append("locked on:").append(info.getLockName()).append(" locked owned by:<")
              .append(info.getLockOwnerId())
              .append(",").append(info.getLockOwnerName()).append(">\n");
        }
        final StackTraceElement[] stackTrace = info.getStackTrace();
        for (final StackTraceElement element : stackTrace) {
          result.append("  ");
          result.append(element);
          result.append("\n");
        }
        result.append("\n");
      }
    }
    long[] deadlocks = threadMxBean.findDeadlockedThreads();

    if (deadlocks != null) {
      result.append("DEADLOCKS!!");
      for (final long l : deadlocks) {
        result.append(l).append("\n");
      }
    }
    return result.toString();
  }

  public static String getMemory() {
    System.gc();
    System.runFinalization();
    System.gc();
    final int mb = 1024 * 1024;
    final StringBuilder buf = new StringBuilder("Heap utilization statistics [MB]\r\n");
    final Runtime runtime = Runtime.getRuntime();
    buf.append("Used Memory: ").append((runtime.totalMemory() - runtime.freeMemory()) / mb).append("\r\n");
    buf.append("Free memory: ").append(runtime.freeMemory() / mb).append("\r\n");
    buf.append("Total memory: ").append(runtime.totalMemory() / mb).append("\r\n");
    buf.append("Max memory: ").append(runtime.maxMemory() / mb).append("\r\n");
    final int currentMaxSetting = Memory.getMaxMemoryFromSystemIniFileInMB(GameRunner.getSystemIni());
    if (currentMaxSetting > 0) {
      buf.append("Max Memory user setting within 22% of: ").append(currentMaxSetting).append("\r\n");
    }
    return buf.toString();
  }

  public static String getProperties() {
    final StringBuilder buf = new StringBuilder("SYSTEM PROPERTIES\n");
    final Properties props = System.getProperties();
    final List<String> keys = new ArrayList<>(props.stringPropertyNames());
    Collections.sort(keys);
    for (final String property : keys) {
      final String value = props.getProperty(property);
      buf.append(property).append(" ").append(value).append("\n");
    }
    return buf.toString();
  }

  public static String getDebugReportHeadless() {
    final StringBuilder result = new StringBuilder(500);
    result.append(getThreadDumps());
    result.append(getProperties());
    result.append(getMemory());
    result.append("ENGINE VERSION: ").append(ClientContext.engineVersion()).append("\n");
    return result.toString();
  }

  public static String getDebugReportWithFramesAndWindows() {
    final StringBuilder result = new StringBuilder(500);
    result.append("CONSOLE_OUTPUT:\n");
    result.append(ErrorConsole.getConsole().getText());
    result.append("\n");
    result.append(getThreadDumps());
    result.append(getProperties());
    result.append(getMemory());
    result.append(getOpenAppWindows());
    result.append("ENGINE VERSION: ").append(ClientContext.engineVersion().getVersion()).append("\n");
    return result.toString();
  }

  private static String getOpenAppWindows() {
    final StringBuilder builder = new StringBuilder("WINDOWS\n");
    for (final Frame f : Frame.getFrames()) {
      if (f.isVisible()) {
        builder.append("window:").append("class ").append(f.getClass()).append(" size ").append(f.getSize())
            .append(" title ")
            .append(f.getTitle()).append("\n");
      }
    }
    return builder.toString();
  }
}
