package com.ruoyi.file.object;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Data
public final class RuntimeState
{
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    private Date startTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    private Date uptime;
    private long runDuration;
    private String arguments;

    private long heapUsed;
    private long heapInit;
    private long heapMax;
    private long heapCommitted;

    private long nonHeapUsed;
    private long nonHeapInit;
    private long nonHeapMax;
    private long nonHeapCommitted;

    private long threadCount;
    private long daemonThreadCount;
    private long peakThreadCount;
    private long startedThreadCount;
    private List<ThreadState> threads;

    private List<MemoryPoolState> memoryPools;

    @Data
    public static class ThreadState
    {
        private long threadId;
        private String threadName;
        private String threadState;
        private Boolean isCurrent;
    }

    @Data
    public static class MemoryPoolState
    {
        private String name;
        private String type;
        private long memoryUsed = -1;
        private long memoryInit = -1;
        private long memoryMax = -1;
        private long memoryCommitted = -1;
    }

    public static RuntimeState capture()
    {
        RuntimeState state = new RuntimeState();
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        long startTime = runtimeMXBean.getStartTime();
        long uptime = runtimeMXBean.getUptime();
        state.startTime = new Date(startTime);
        state.uptime = new Date(startTime + uptime);
        state.runDuration = uptime;
        state.arguments = String.join(" ", runtimeMXBean.getInputArguments());

        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
        state.heapUsed = heapMemoryUsage.getUsed();
        state.heapMax = heapMemoryUsage.getMax();
        state.heapInit = heapMemoryUsage.getInit();
        state.heapCommitted = heapMemoryUsage.getCommitted();

        MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
        state.nonHeapUsed = nonHeapMemoryUsage.getUsed();
        state.nonHeapMax = nonHeapMemoryUsage.getMax();
        state.nonHeapInit = nonHeapMemoryUsage.getInit();
        state.nonHeapCommitted = nonHeapMemoryUsage.getCommitted();

        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        state.threadCount = threadMXBean.getThreadCount();
        state.daemonThreadCount = threadMXBean.getDaemonThreadCount();
        state.peakThreadCount = threadMXBean.getPeakThreadCount();
        state.startedThreadCount = threadMXBean.getTotalStartedThreadCount();
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
        state.threads = new ArrayList<>();
        for(ThreadInfo threadInfo : threadInfos)
        {
            ThreadState t = new ThreadState();
            t.threadId = threadInfo.getThreadId();
            t.threadName = threadInfo.getThreadName();
            t.threadState = threadInfo.getThreadState().name();
            t.isCurrent = threadInfo.getThreadId() == Thread.currentThread().getId();
            state.threads.add(t);
        }

        state.memoryPools = new ArrayList<>();
        List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
        for(MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans)
        {
            MemoryPoolState m = new MemoryPoolState();
            m.name = memoryPoolMXBean.getName();
            m.type = memoryPoolMXBean.getType().name();
            MemoryUsage usage = memoryPoolMXBean.getUsage();
            if(null != usage)
            {
                m.memoryUsed = usage.getUsed();
                m.memoryMax = usage.getMax();
                m.memoryInit = usage.getInit();
                m.memoryCommitted = usage.getCommitted();
            }
            state.memoryPools.add(m);
        }

        return state;
    }
}
