
package de.ivu.elect.business.system.boundary;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import de.ivu.elect.business.system.entity.ThreadDumpFilterParameters;
import de.ivu.elect.business.system.entity.ThreadInfoByNameComparator;

// keine Arbeit mit persistenten Objekten, daher kein @Stateless nötig
public class ThreadDumpCreate {

  public List<ThreadInfo> createThreadDump(ThreadDumpFilterParameters filterParameters) {
    return sortById(filterByParameters(filterParameters, dumpAllThreads()));
  }


  protected List<ThreadInfo> dumpAllThreads() {
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    boolean objectMonitorUsageSupported = threadMXBean.isObjectMonitorUsageSupported();
    boolean synchronizerUsageSupported = threadMXBean.isSynchronizerUsageSupported();

    return Arrays.asList(threadMXBean.dumpAllThreads(objectMonitorUsageSupported, synchronizerUsageSupported));
  }

  private List<ThreadInfo> filterByParameters(ThreadDumpFilterParameters filterParameters,
                                              List<ThreadInfo> threadInfos) {

    return threadInfos.stream()
        .filter(threadInfo -> isThreadInfoApplicable(filterParameters, threadInfo))
        .collect(Collectors.toCollection(() -> new ArrayList<>(threadInfos.size())));
  }

  private boolean isThreadInfoApplicable(ThreadDumpFilterParameters filterParameters, ThreadInfo threadInfo) {

    if (filterParameters.isThreadNameSet()) {
      if (!filterParameters.getThreadNameRegexPattern().matcher(threadInfo.getThreadName()).find()) {
        return false;
      }
    }

    if (filterParameters.isStackTraceContentSet()) {
      return Arrays.stream(threadInfo.getStackTrace())
          .anyMatch(stackTraceElement -> filterParameters.getStackTraceRegexPattern().matcher(stackTraceElement.toString()).find());
    }

    return true;
  }

  private List<ThreadInfo> sortById(List<ThreadInfo> threadInfos) {
    List<ThreadInfo> result = new ArrayList<>(threadInfos);
    result.sort(Comparator.comparingLong(ThreadInfo::getThreadId));
    return result;
  }

  private List<ThreadInfo> sortByName(List<ThreadInfo> threadInfos) {
    List<ThreadInfo> result = new ArrayList<>(threadInfos);
    result.sort(new ThreadInfoByNameComparator());
    return result;
  }

}
