/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.system;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedList;
import java.util.List;
import net.ranides.assira.generic.ValueUtils;
import net.ranides.assira.text.FormatNumber;
import net.ranides.assira.trace.LoggerUtils;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class RuntimeUtils {
    
    private static final org.slf4j.Logger LOGGER = LoggerUtils.getLogger();
    
    private RuntimeUtils() {
        // utility class
    }
    
    public static long getMemoryUse() {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
    
    
    /**
     * Runs the garbage collector. 
     * @return 
     *      Estimated amount of memory released by GC
     *      
     */
    @SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
    @SuppressFBWarnings("DM_GC")
    public static long performGC() {
        long prev = getMemoryUse();
        Runtime.getRuntime().gc();
        return prev - getMemoryUse();
    }
    
    /**
     * Forces releasing {@code SoftReferences}, by causing OutOfMemoryError in VM.
     * @return 
     *      Estimated amount of memory released by GC
     */
    @SuppressWarnings({
        "PMD.DoNotCallGarbageCollectionExplicitly", 
        "MismatchedQueryAndUpdateOfCollection"
    })
    @SuppressFBWarnings("DM_GC")
    public static long forceGC() {
        long prev = Runtime.getRuntime().totalMemory();
        int block = (int)Math.min(Integer.MAX_VALUE, Runtime.getRuntime().freeMemory());
        try {
            final List<long[]> memhog = new LinkedList<>();
            while(true) {
                memhog.add(new long[block]);
            }
        }
        catch(final OutOfMemoryError e) {
            // at this point all SoftReferences have been released - GUARANTEED
            long alloc = Runtime.getRuntime().totalMemory() - prev;
            LOGGER.debug("Forced OutOfMemoryError by allocation {} bytes", FormatNumber.group(alloc));
            Runtime.getRuntime().gc();
            // at this point we should use minimal amount of memory
            return prev - getMemoryUse();
        }
    }
    
    public static <T> T doPrivileged(PrivilegedAction<T> action) {
        return AccessController.doPrivileged(action);
    }
    
    public static void doPrivileged(Runnable action) {
        doPrivileged(()->{ action.run(); return null; });
    }
    
    public static String getProperty(String name) {
        return getProperty(name, null);
    }
    
    public static String getProperty(String name, String ddefault) {
        try {
            return ValueUtils.or(doPrivileged(() -> System.getProperty(name)), ddefault);
        } catch(AccessControlException $0) {
            return ddefault;
        }
    }

}
