package com.capgemini.framework.common;

import com.capgemini.reports.database.manager.MngMemMonitor;

import java.math.BigDecimal;

/**
 * Utility class for memory monitoring.
 **/
public final class MemoryHelper {

    private static final double TO_PERCENT = 100D;
    private static final double BYTES_TO_MB = 1024D * 1024D;

    private MemoryHelper() {
        // Makes this class static
    }

    /**
     * Log debug message when memory monitoring is enabled.
     *
     * @return Memory message state
     **/
    public static String monitorMemory() {
        Double theTotalMemory = formatByte(Runtime.getRuntime().totalMemory());
        Double theMaxMemory = formatByte(Runtime.getRuntime().maxMemory());
        Double theFreeMemory = getFreeMemory();

        Double theUsedMemory = floor(theMaxMemory - theFreeMemory, 1);
        Double theMaxPercent = floor(theUsedMemory / theMaxMemory, 1) * TO_PERCENT;

        /* Persist current memory state. */
        MngMemMonitor theMemMonitorManager = new MngMemMonitor();
        theMemMonitorManager.insert(theFreeMemory, theUsedMemory, theMaxMemory, theMaxPercent, theTotalMemory);
        return "M&eacute;moire consomm&eacute;e : " + theUsedMemory + " (" + theMaxPercent + "% max)<br/>Allou&eacute;e : " + theTotalMemory
                + " / Capacit&eacute; maximale : " + theMaxMemory;
    }

    /**
     * Get the available memory amount by MBytes.
     **/
    private static Double getFreeMemory() {

        /* Get the available memory in allocated space */
        Double freeMemory = floor(Runtime.getRuntime().freeMemory() / BYTES_TO_MB, 1);

        /* Add complementary memory which could be allocated (when XMS is different than XMX) */
        freeMemory += floor((Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory()) / BYTES_TO_MB, 1);

        return freeMemory;
    }

    /**
     * Format memory value from Bytes to MegaBytes.
     *
     * @param aValue with Mbs
     * @return Formated value
     */
    private static Double formatByte(final double aValue) {
        return floor(aValue / BYTES_TO_MB, 0);
    }

    /**
     * Round numeric value to lower value.
     *
     * @param aValue Converted value.
     * @param aScale Preserved values.
     * @return Round value.
     */
    private static double floor(final double aValue, final int aScale) {
        BigDecimal bigD = BigDecimal.valueOf(aValue);
        BigDecimal bigD2 = bigD.setScale(aScale, BigDecimal.ROUND_DOWN);
        return bigD2.doubleValue();
    }
}
