package vut.msa.monitor.system;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
import org.hyperic.sigar.CpuInfo;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.DirUsage;
import org.hyperic.sigar.FileSystem;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.NetInterfaceConfig;
import org.hyperic.sigar.NetInterfaceStat;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.Uptime;

/**
 * Trieda sluzi na ziskanie informacii o serveri. Tieto informacie sluzia na sledovanie
 * stavu servera. Vyuziva nastroj SIGAR.
 */
public class ServerInformation {

    private static final Logger log = Logger.getLogger(FileSystemInformation.class);
    private static final String DEFAULT_PATH = "C:\\Documents";


    private Sigar sigar;
    private Mem memoryInformation;

    //Informacie o CPU
    private CpuPerc cpuPerc;
    private CpuInfo cpuInfo;
    private CpuInfo[] cpusInfo;

    //Informacie zo sietoveho adaptera
    private List<NetCompleteInfo> netInformation;

    //Informacie o uloznych miestach
    private List<FileSystem> fsList;

    // System information
    private double uptime;
    private long memoryRAM;
    private double memoryUsedPerc;

    private long totalDisk;
    private long usedDisk;
    private long freeDisk;

    private int documentsCount;
    private long documentsSize;

    //Singleton Pattern
    private ServerInformation() {

        try {
            sigar = new Sigar();
            Sigar.load();

            //V sekundach, postacuje na 68 rokov
            Uptime upt = new Uptime();
            upt.gather(sigar);
            uptime = upt.getUptime();

            //Naplnenie zoznamu, informaciami o sieti
            String[] netInterfaceList = sigar.getNetInterfaceList();
            NetInterfaceConfig[] temp = new NetInterfaceConfig[netInterfaceList.length + 1];
            netInformation = new ArrayList<NetCompleteInfo> ();

            for(int i = 0; i < netInterfaceList.length; i++) {
                temp[i] = sigar.getNetInterfaceConfig(netInterfaceList[i]);
                if (!temp[i].getAddress().equals("0.0.0.0")) {
                    NetInterfaceConfig config = temp[i];
                    NetInterfaceStat stat = sigar.getNetInterfaceStat(temp[i].getName());

                    netInformation.add(new NetCompleteInfo(config, stat));
                }
            }
            //fillSystemInformations();
        } catch (SigarException ex) {
            log.error(ex.getMessage());
        }
    }

    private static class ServerInformationHolder {
        public static final ServerInformation instance = new ServerInformation();
    }

    public static ServerInformation getInstance() {
        return ServerInformationHolder.instance;
    }

    public long getFreeDisk() { return freeDisk; }

    public long getTotalDisk() { return totalDisk; }

    public long getUsedDisk() { return usedDisk; }

    public long getMemoryRAM() { return memoryRAM; }

    public int getDocumentsCount() { return documentsCount; }

    public long getDocumentsSize() { return documentsSize; }

    public double getMemoryUsedPerc() { return memoryUsedPerc; }

    /**
     * Funkcia vráti čas od spustenia systému (Uptime) v sekundách.
     *
     * @return čas v sekundách
     * @throws SigarException
     */
    public double getUptime() throws SigarException {      
        return uptime;
    }

    //PROPERTY: sigar
    public Sigar getSigar() { return sigar; }

    public String getFormatedUptime() throws SigarException{
        Uptime upt = new Uptime();
        upt.gather(sigar);
        uptime = upt.getUptime();
        return getFormatedUptime((int)uptime);
    }

    /**
     * Metoda naformatuje cas behu systemu z milisekund na format "xx dní, xx hodín, xx min."
     * @param uptime
     * @return
     */
    public String getFormatedUptime(int uptime) {

        int days = uptime/86400;
        int temp = uptime % 86400;
        int hours = temp/3600;
        int minutes = (temp % 3600) /60;
        return days + " dní, " + hours + " hodín, " + minutes + "min.";
    }

    /**
     * Funkcia vrati objekt Mem, ktory poskytuje informacie o pamati.
     *
     * @return objekt Mem
     * @throws SigarException
     * @see org.hyperic.sigar.Mem
     */
    public Mem getMemoryInfo() throws SigarException {

        try {
            memoryInformation = sigar.getMem();
        } catch (SigarException se) {
            log.error(se.getMessage());
        }
       
        return memoryInformation;
    }

    /**
     * Funkcia vrati objekt CpuInfo, ktory poskytuje informacie o jednotke CPU.
     *
     * @return pole objektov CpuInfo
     * @see org.hyperic.sigar.CpuInfo
     */
    public CpuInfo[] getCPUsInfo() {
        
        try {
            cpusInfo = sigar.getCpuInfoList();
        } catch (SigarException se) {
            log.error(se.getMessage());
        }

        return cpusInfo;
    }

    /**
     * Funkcia vrati informacie o jednotke CPU.
     *
     * @return objekt CpuInfo
     * @see org.hyperic.sigar.CpuInfo
     */
    public CpuInfo getCpuInfo() {

        getCPUsInfo();
        if (cpusInfo[0] != null) {
            cpuInfo =cpusInfo[0];
        }

        return cpuInfo;
    }

    /**
     * Metoda vrati objekt CpuPerc, ktory poskytuje informacie o vytazeni
     * procesora.
     *
     * @return objekt CpuPerc
     * @see org.hyperic.sigar.CpuPerc
     */
    public CpuPerc getCpuPerc() {
        try {
            cpuPerc = sigar.getCpuPerc();
        } catch (SigarException se) {
            log.error(se.getMessage());
        }

        return cpuPerc;
    }

    /**
     * Metoda vrati zoznam naplneny informaciami o sietovych adapteroch. Naplnenie
     * zoznamu sa vykona v konstruktore triedy.
     *
     * @return zoznam informacii o sietovych adapteroch
     */
    public List<NetCompleteInfo> getNetInformation() {
        return netInformation;
    }

    /**
     * Funkcia vrati objekt DirUsage, ktory poskytuje informacie o adresari.
     *
     * @param path cesta k adresaru s dokumentami
     * @return  objekt DirUsage
     * @throws SigarException
     * @see org.hyperic.sigar.DirUsage;
     */
    public DirUsage getDocumentsDir(String path) throws SigarException {
        DirUsage dir = new DirUsage();
        dir.gather(sigar, path);

        return dir;
    }

    /**
     * Funkcia vrati objekt DirUsage, ktory poskytuje informacie o adresari.
     * Pouzije standardnu cestu k adresaru C:\Documents
     *
     * @return  objekt DirUsage
     * @throws SigarException
     * @see org.hyperic.sigar.DirUsage;
     */
    public DirUsage getDocumentsDir() throws SigarException {
        return this.getDocumentsDir(DEFAULT_PATH);
    }

    /**
     * Funkcia vrati objekt FileSystemInformation, ktory sluzi na poskytovanie
     * informacii o suborovom systeme.
     *
     * @return objet FileSystemInformation
     * @throws SigarException
     * @see vut.msa.monitor.system.FileSystemInformation
     */
    public FileSystemInformation getFileSystemInfo() throws SigarException {
        return new FileSystemInformation(this.sigar);
    }

    /**
     * Metoda naplni zoznam objektami FileSystem, ktore sluzia na ziskanie
     * informacii o pripojenych uloznych mediach.
     * 
     * @return zoznam ArrayList
     */
    public List<FileSystem> getFsList() {

        fsList = new ArrayList<FileSystem>();

        try {
           try {
               sigar.load();
           } catch (Exception ex) {}
           FileSystem[] fileSystems = sigar.getFileSystemList();
           fsList.addAll(Arrays.asList(fileSystems));
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }

        return fsList;
    }

    //Iba na testovanie
    public static void main(String[] args) {

        ServerInformation info = new ServerInformation();

        try {
           FileSystem[] fileSystems = info.sigar.getFileSystemList();
           for (FileSystem fs : fileSystems) {
               System.out.println(fs.getType());
           }

        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
    }

    /**
     * Funkcia naplni informacie o systeme. Sluzi len na prenos medzi strojmi, 
     * nakolko Sigar nie je mozne preniest (nie je serializovatelny).
     * 
     * @throws SigarException
     */
    private void fillSystemInformations() throws SigarException {

        this.memoryRAM = this.getMemoryInfo().getRam();
        this.memoryUsedPerc = this.getMemoryInfo().getUsedPercent();

        this.totalDisk = this.getFileSystemInfo().getFileSystemUsage("C:\\").getTotal();
        this.usedDisk = this.getFileSystemInfo().getFileSystemUsage("C:\\").getUsed();
        this.freeDisk = totalDisk - usedDisk;

        DirUsage dir = getDocumentsDir();
        this.documentsCount = (int) dir.getFiles();
        this.documentsSize = dir.getDiskUsage();
        
    }
}
