package doglet.tester;

import nova.Machine;
import nova.Store;
import nova.runtime;
import nova.special;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Warranty & Liability
 * To the extent permitted by applicable law and unless explicitly
 * otherwise agreed upon, XLOG Technologies AG makes no warranties
 * regarding the provided information. XLOG Technologies AG assumes
 * no liability that any problems might be solved with the information
 * provided by XLOG Technologies AG.
 *
 * Rights & License
 * All industrial property rights regarding the information - copyright
 * and patent rights in particular - are the sole property of XLOG
 * Technologies AG. If the company was not the originator of some
 * excerpts, XLOG Technologies AG has at least obtained the right to
 * reproduce, change and translate the information.
 *
 * Reproduction is restricted to the whole unaltered document. Reproduction
 * of the information is only allowed for non-commercial uses. Selling,
 * giving away or letting of the execution of the library is prohibited.
 * The library can be distributed as part of your applications and libraries
 * for execution provided this comment remains unchanged.
 *
 * Restrictions
 * Only to be distributed with programs that add significant and primary
 * functionality to the library. Not to be distributed with additional
 * software intended to replace any components of the library.
 *
 * Trademarks
 * Jekejeke is a registered trademark of XLOG Technologies AG.
 */
public final class statlib {

    /****************************************************************/
    /* sys_stat_map/1                                               */
    /****************************************************************/

    /**
     * sys_stat_map(M): internal only
     * The built-in succeeds in W with the statistics map.
     */
    private static boolean test_sys_stat_map(Object[] args) {
        Map res = new HashMap();
        res.put("wall", special.norm_smallint(System.currentTimeMillis()));
        res.put("time", Double.valueOf(Machine.real_time()));
        res.put("maxinfs", special.norm_smallint(Machine.gc_maxinfs));
        res.put("calls", special.norm_smallint(Machine.gc_enter));
        res.put("gctime", Double.valueOf(Machine.gc_time));
        res.put("used", special.norm_smallint(Runtime.getRuntime().totalMemory() -
                Runtime.getRuntime().freeMemory()));
        return Machine.exec_unify(args[0], res);
    }

    /**
     * garbage_collect: internal only
     * The built-in succeeds in attempting a garbage colection.
     */
    private static boolean test_garbage_collect(Object[] args) {
        Machine.gc_major();
        Runtime.getRuntime().gc();
        return true;
    }

    /******************************************************************/
    /* Index Statistics                                               */
    /******************************************************************/

    /**
     * kb_link_stat_has(Q): internal only
     * The built-in succeeds if the provable Q has index statistics.
     */
    private static boolean test_kb_link_stat_has(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        runtime.check_provable(alpha);
        Store.Provable peek = (Store.Provable) alpha;
        if (Store.is_logical(peek.func) || Store.is_stick(peek.func))
            return stats_has(peek.func);
        return false;
    }

    private static boolean stats_has(Object func) {
        if (Store.is_stick(func)) {
            Store.Stick peek = (Store.Stick) func;
            int i = 0;
            for (; i < peek.maps.length; i++) {
                Map temp = peek.maps[i];
                if (temp == null || temp.size() == 0)
                    continue;
                break;
            }
            if (!(i < peek.maps.length))
                return false;
            return true;
        } else {
            return false;
        }
    }

    /**
     * kb_link_stat_get(Q, M): internal only
     * The built-in succeeds in M with the index statistics of the provable Q.
     */
    private static boolean test_kb_link_stat_get(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        runtime.check_provable(alpha);
        Store.Provable peek = (Store.Provable) alpha;
        LinkedHashMap res = new LinkedHashMap();
        if (Store.is_logical(peek.func) || Store.is_stick(peek.func))
            stats_get(peek.func, "", 0, res);
        return Machine.exec_unify(args[1], res);
    }

    private static void stats_get(Object func, String path, int offset, Map res) {
        if (Store.is_stick(func)) {
            Store.Stick peek = (Store.Stick) func;
            for (int i = 0; i < peek.maps.length; i++) {
                Map temp = peek.maps[i];
                if (temp == null || temp.size() == 0)
                    continue;
                String path2 = Integer.toString(i+offset+1);
                if (!"".equals(path))
                    path2 = path + "+" + path2;
                stats_add(path2, temp.size(), res);
                Iterator it = temp.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next();
                    stats_get(entry.getValue(), path2, i+offset+1, res);
                }
                stats_get(peek.guards[i], path2, i+offset+1, res);
            }
        }
    }

    private static void stats_add(String path, int delta, Map res) {
        Integer size = (Integer) res.get(path);
        if (size != null)
            delta += size.intValue();
        res.put(path, Integer.valueOf(delta));
    }

    /******************************************************************/
    /* Stat Lib Init                                                  */
    /******************************************************************/

    public static void main() {
        Store.set("sys_stat_map", 1, special.make_check(statlib::test_sys_stat_map));
        Store.set("garbage_collect", 0, special.make_check(statlib::test_garbage_collect));
        Store.set("kb_link_stat_has", 1, special.make_check(statlib::test_kb_link_stat_has));
        Store.set("kb_link_stat_get", 2, special.make_check(statlib::test_kb_link_stat_get));
    }

}
