package com.cm.central.control.client.socket.service.socket.clientinfo;

import com.alibaba.fastjson.JSONObject;
import com.cm.central.control.client.socket.config.properties.SocketClientProperties;
import com.cm.central.control.client.socket.service.socket.BaseSocketService;
import com.cm.common.utils.DateUtil;
import com.cm.socket.enums.SocketTypeMessageEnum;
import com.cm.socket.pojo.Message;
import com.sun.management.OperatingSystemMXBean;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.ScheduledFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.util.FormatUtil;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: ClientInfoServiceImpl
 * @Description: 客户端信息
 * @Author: WangGeng
 * @Date: 2020/7/29 2:42 下午
 * @Version: 1.0
 **/
@Service("clientInfoServiceImpl")
public class ClientInfoServiceImpl extends BaseSocketService {

    @Autowired
    private SocketClientProperties socketClientProperties;

    @Override
    protected void readMessageService(ChannelHandlerContext channelHandlerContext, Message readMessage) {

    }

    @Override
    public void autoReply(ChannelHandlerContext channelHandlerContext) throws Exception {
        Channel channel = channelHandlerContext.channel();
        if (!socketClientProperties.getSendClientInfoActive()) {
            LOG.debug("发送客户端信息未激活");
            return;
        }
        ScheduledFuture<?> scheduledFuture = channel.eventLoop().schedule(() -> {
            if (channel.isActive()) {
                try {
                    Map<String, Object> clientInfo = getClientInfo();
                    writeMessage(channelHandlerContext, SocketTypeMessageEnum.MESSAGE_TYPE_CLIENT_INFO, getEncodeContent(JSONObject.toJSONString(clientInfo)));
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            } else {
                channelHandlerContext.close();
                throw new RuntimeException();
            }
        }, socketClientProperties.getSendClientInfoSecond(), TimeUnit.SECONDS);
        scheduledFuture.addListener(future -> {
            if (future.isSuccess()) {
                autoReply(channelHandlerContext);
            }
        });
    }

    private Map<String, Object> getClientInfo() throws InterruptedException {
        SystemInfo systemInfo = new SystemInfo();
        OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        String currentTime = DateUtil.getTime();
        Map<String, Object> clientInfoMap = getHashMap(10);
        clientInfoMap.put("system", system());
        clientInfoMap.put("disk", listDisk());
        clientInfoMap.put("memory", getMemory(operatingSystemMXBean, currentTime));
        clientInfoMap.put("jvmMemory", getJVMMemory(currentTime));
        clientInfoMap.put("cpu", getCPU(systemInfo, currentTime));
        return clientInfoMap;
    }

    /**
     * 系统
     *
     * @return
     */
    private Map<String, Object> system() {
        Map<String, Object> systemMap = getHashMap(10);
        systemMap.put("osName", System.getProperty("os.name"));
        systemMap.put("javaHome", System.getProperty("java.home"));
        systemMap.put("javaVersion", System.getProperty("java.version"));
        systemMap.put("userHome", System.getProperty("user.home"));
        systemMap.put("userName", System.getProperty("user.name"));
        return systemMap;
    }

    /**
     * 磁盘列表
     *
     * @return
     */
    private List<Map<String, Object>> listDisk() {
        List<Map<String, Object>> diskList = new ArrayList<>();
        File[] files = File.listRoots();
        for (File file : files) {
            Map<String, Object> diskMap = getHashMap(6);
            diskMap.put("path", file.getPath());
            diskMap.put("total", Double.parseDouble(new DecimalFormat("#.#").format(file.getTotalSpace() * 1.0 / 1024 / 1024 / 1024)));
            diskMap.put("free", Double.parseDouble(new DecimalFormat("#.#").format(file.getFreeSpace() * 1.0 / 1024 / 1024 / 1024)));
            diskMap.put("usable", Double.parseDouble(new DecimalFormat("#.#").format(file.getUsableSpace() * 1.0 / 1024 / 1024 / 1024)));
            diskList.add(diskMap);
        }
        return diskList;
    }

    /**
     * 物理内存
     *
     * @param operatingSystemMXBean
     * @return
     */
    private Map<String, Object> getMemory(OperatingSystemMXBean operatingSystemMXBean, String currentTime) {
        Map<String, Object> memoryMap = getHashMap(6);
        // 总内存，单位G
        memoryMap.put("totalMemory", Double.parseDouble(new DecimalFormat("#.##").format(operatingSystemMXBean.getTotalPhysicalMemorySize() / 1024.0 / 1024 / 1024)));
        // 剩余的物理内存，单位G
        memoryMap.put("freePhysicalMemory", Double.parseDouble(new DecimalFormat("#.##").format(operatingSystemMXBean.getFreePhysicalMemorySize() / 1024.0 / 1024 / 1024)));
        // 已使用的物理内存，单位G
        memoryMap.put("usedMemory", Double.parseDouble(new DecimalFormat("#.##").format(((operatingSystemMXBean.getTotalPhysicalMemorySize() - operatingSystemMXBean.getFreePhysicalMemorySize()) / 1024.0 / 1024 / 1024))));
        memoryMap.put("time", currentTime);
        return memoryMap;
    }

    /**
     * JVM内存
     *
     * @return
     */
    private Map<String, Object> getJVMMemory(String currentTime) {
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage();
        long initTotalMemorySize = memoryUsage.getInit();
        // 最大可用内存
        long maxMemorySize = memoryUsage.getMax();
        // 已使用的内存
        long usedMemorySize = memoryUsage.getUsed();
        Map<String, Object> jvmMap = getHashMap(6);
        // 初始的总内存(JVM)，单位M
        jvmMap.put("initTotalMemorySize", Double.parseDouble(new DecimalFormat("#.#").format(initTotalMemorySize * 1.0 / 1024 / 1024)));
        // 最大可用内存(JVM)，单位M
        jvmMap.put("maxMemorySize", Double.parseDouble(new DecimalFormat("#.#").format(maxMemorySize * 1.0 / 1024 / 1024)));
        // 已使用的内存(JVM)，单位M
        jvmMap.put("usedMemorySize", Double.parseDouble(new DecimalFormat("#.#").format(usedMemorySize * 1.0 / 1024 / 1024)));
        jvmMap.put("time", currentTime);
        return jvmMap;
    }

    /**
     * CPU
     *
     * @param systemInfo
     * @return
     * @throws InterruptedException
     */
    private Map<String, Object> getCPU(SystemInfo systemInfo, String currentTime) throws InterruptedException {
        CentralProcessor processor = systemInfo.getHardware().getProcessor();
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        TimeUnit.SECONDS.sleep(1);
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()]
                - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()]
                - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()]
                - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()]
                - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()]
                - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user = ticks[CentralProcessor.TickType.USER.getIndex()]
                - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()]
                - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()]
                - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;

        Map<String, Object> cpuMap = getHashMap(10);
        // cpu 核数
        cpuMap.put("coreCount", Runtime.getRuntime().availableProcessors());
        // 系统使用率
        cpuMap.put("systemUseRate", Double.parseDouble(new DecimalFormat("#.##").format(cSys * 100.0 / totalCpu)));
        // 用户使用率
        cpuMap.put("userUseRate", Double.parseDouble(new DecimalFormat("#.##").format(user * 100.0 / totalCpu)));
        // 等待率
        cpuMap.put("waitRate", Double.parseDouble(new DecimalFormat("#.##").format(iowait * 100.0 / totalCpu)));
        // 空闲率
        cpuMap.put("freeRate", Double.parseDouble(new DecimalFormat("#.##").format(idle * 100.0 / totalCpu)));
        cpuMap.put("time", currentTime);
        return cpuMap;
    }

    /**
     * 网络IO
     *
     * @param systemInfo
     * @return
     */
    private List<Map<String, Object>> listNetIO(SystemInfo systemInfo) {
        HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
        List<Map<String, Object>> netIOList = new ArrayList<>();
        for (NetworkIF net : hardwareAbstractionLayer.getNetworkIFs()) {
            if(net.getIPv4addr() == null || net.getIPv4addr().length == 0) {
                continue;
            }
            Map<String, Object> netIOMap = getHashMap(10);
            netIOMap.put("name", net.getName());
            netIOMap.put("displayName", net.getDisplayName());
            netIOMap.put("mac", net.getMacaddr());
            netIOMap.put("ipv4", Arrays.toString(net.getIPv4addr()));
            boolean hasData = net.getBytesRecv() > 0 || net.getBytesSent() > 0 || net.getPacketsRecv() > 0
                    || net.getPacketsSent() > 0;
            netIOMap.put("receivedPackets", hasData ? net.getPacketsRecv() : 0);
            netIOMap.put("receivedBytes", hasData ? FormatUtil.formatBytes(net.getBytesRecv()) : 0);
            netIOMap.put("receivedErrors", hasData ? net.getInErrors() : 0);
            netIOMap.put("sentPackets", hasData ? net.getPacketsSent() : 0);
            netIOMap.put("sentBytes", hasData ? FormatUtil.formatBytes(net.getBytesSent()) : 0);
            netIOMap.put("sentErrors", hasData ? net.getOutErrors() : 0);
            netIOList.add(netIOMap);
        }
        return netIOList;
    }

}
