package cn.reghao.bnt.common.machine;

import cn.reghao.bnt.common.machine.model.DiskInfo;
import cn.reghao.bnt.common.machine.model.DiskPartition;
import cn.reghao.jutil.jdk.converter.ByteConverter;
import cn.reghao.jutil.jdk.machine.data.MachineData;
import cn.reghao.jutil.jdk.machine.data.detail.DiskDetail;
import cn.reghao.jutil.jdk.machine.data.stat.DiskStat;
import cn.reghao.jutil.jdk.math.Calculator;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2019-10-25 13:21:25
 */
public class Disk implements MachineData<List<DiskDetail>, List<DiskStat>> {
    private final HardwareAbstractionLayer hal;
    private final OperatingSystem os;
    private final ByteConverter byteConverter;

    public Disk(SystemInfo si, ByteConverter byteConverter) {
        this.hal = si.getHardware();
        this.os = si.getOperatingSystem();
        this.byteConverter = byteConverter;
    }

    @Override
    public List<DiskDetail> detail() {
        return os.getFileSystem().getFileStores().stream()
                .filter(osFileStore -> osFileStore.getVolume().startsWith("/dev/"))
                .map(osFileStore -> {
                    String vol = osFileStore.getVolume();
                    String mount = osFileStore.getMount();
                    String fsType = osFileStore.getType();

                    long total = osFileStore.getTotalSpace();
                    // non-root 用户可用的磁盘空间
                    long userAvail = osFileStore.getUsableSpace();
                    // root 用户可用的磁盘空间
                    long rootAvail = osFileStore.getFreeSpace();

                    long inodeTotal = osFileStore.getTotalInodes();
                    long inodeFree = osFileStore.getFreeInodes();

                    return new DiskDetail(vol, mount, fsType, total, userAvail, total-userAvail, inodeTotal, inodeFree);
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<DiskStat> stat() {
        return null;
    }

    /**
     * 根据一个路径确定它所在的分区
     *
     * @param
     * @return
     * @date 2021-02-06 下午11:25
     */
    public DiskDetail diskDetail(String path) {
        List<DiskDetail> diskInfos = detail();
        // 根据挂载路径的长度降序
        diskInfos.sort((o1, o2) -> o2.getMountedOn().length() - o1.getMountedOn().length());

        List<DiskDetail> candidates = new ArrayList<>();
        for (DiskDetail diskInfo : diskInfos) {
            String mountedOn = diskInfo.getMountedOn();
            String[] array = path.split(mountedOn);
            if (array.length == 0) {
                return diskInfo;
            } else if (array.length == 2) {
                if (array[1].startsWith("/") || "/".equals(mountedOn)) {
                    candidates.add(diskInfo);
                }
            } else if (array.length > 2) {
                candidates.add(diskInfo);
            }
        }

        if (!candidates.isEmpty()) {
            // TODO 如果 candidates 大于 0 则判断准确的那个
            return candidates.get(0);
        } else {
            return null;
        }
    }

    public List<DiskInfo> getDisks() {
        List<DiskInfo> diskInfoList = new ArrayList<>();
        hal.getDiskStores().forEach(hwDiskStore -> {
            String model = hwDiskStore.getModel();
            String name = hwDiskStore.getName();
            long size = hwDiskStore.getSize();
            String sizeStr = byteConverter.convert(size);
            DiskInfo diskInfo = new DiskInfo(model, name, size, sizeStr);
            hwDiskStore.getPartitions().forEach(hwPartition -> {
                String uuid = hwPartition.getUuid();
                String id = hwPartition.getIdentification();
                String mountedOn = hwPartition.getMountPoint();
                String fsType = hwPartition.getType();
                long pSize = hwPartition.getSize();

                DiskPartition diskPartition = new DiskPartition(name, pSize, uuid, id, mountedOn, fsType);
                diskPartition.setTotalSpace(pSize);
                diskInfo.getDiskPartitionList().add(diskPartition);
            });

            diskInfoList.add(diskInfo);
        });

        Map<String, DiskPartition> map = diskInfoList.stream()
                .map(DiskInfo::getDiskPartitionList)
                .flatMap(Collection::stream)
                .collect(Collectors.groupingBy(DiskPartition::getUuid,
                        Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));
        os.getFileSystem().getFileStores().forEach(osFileStore -> {
            String uuid = osFileStore.getUUID();
            String vol = osFileStore.getVolume();
            long totalSpace = osFileStore.getTotalSpace();
            String totalSpaceStr = byteConverter.convert(totalSpace);
            long freeSpace = osFileStore.getFreeSpace();
            String freeSpaceStr = byteConverter.convert(freeSpace);
            long usableSpace = osFileStore.getUsableSpace();
            String usableSpaceStr = byteConverter.convert(usableSpace);
            long totalInode = osFileStore.getTotalInodes();
            long freeInode = osFileStore.getFreeInodes();

            DiskPartition diskPartition = map.get(uuid);
            if (diskPartition != null) {
                diskPartition.setTotalSpace(totalSpace);
                diskPartition.setTotalSpaceStr(totalSpaceStr);
                diskPartition.setFreeSpace(freeSpace);
                diskPartition.setFreeSpaceStr(freeSpaceStr);
                diskPartition.setUsableSpace(usableSpace);
                diskPartition.setUsableSpaceStr(usableSpaceStr);
                double freeSpacePercent = Calculator.divide(freeSpace, totalSpace)*100;
                diskPartition.setFreeSpacePercent(freeSpacePercent);

                diskPartition.setTotalInode(totalInode);
                diskPartition.setFreeInode(freeInode);
                double freeInodePercent = Calculator.divide(freeInode, totalInode)*100;
                diskPartition.setFreeInodePercent(freeInodePercent);
            }
        });

        return diskInfoList;
    }
}
