package com.hypirion.beckon;

import sun.misc.Signal;
import sun.misc.SignalHandler;

import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import java.util.concurrent.Callable;

import clojure.lang.PersistentList;

public class SignalRegistererHelper {

    /**
     * A set of modified signal handlers.
     */
    private final static Set<String> modifiedHandlers = new HashSet<String>();

    /**
     * Registers the new list of functions to the signal name, and returns the
     * old SignalHandler.
     */
    private static SignalHandler reset_BANG_(String signame, List fns) {
        Signal sig = new Signal(signame);
        SignalFolder folder = new SignalFolder(fns);
        SignalHandler oldHandler = Signal.handle(sig, folder);
        return oldHandler;
    }

    /**
     * Registers the signal name to a List of Callables, where each callable
     * returns an Object. The signal handling is performed as follows: The first
     * callable is called, and if it returns a value equal to <code>false</code>
     * or <code>null</code> it will stop. Otherwise it will repeat on the next
     * callable, until there are no more left.
     *
     * @param signame the signal name to register this list of callables on.
     * @param fns the list of Callables to (potentially) call.
     */
    static synchronized void register(String signame, List fns) {
        SignalHandler old = reset_BANG_(signame, fns);
        modifiedHandlers.add(signame);
    }

    /**
     * Resets/reinits the signal back to its original signal handler, discarding
     * all possible changes.
     *
     * @param signame the name of the signal to reinit.
     */
    static synchronized void reinit_signal_handler_BANG_(String signame) {
        SignalHandler original = SignalHandler.SIG_DFL;
        Signal sig = new Signal(signame);
        Signal.handle(sig, original);
        modifiedHandlers.remove(sig);
    }

    /**
     * Resets/reinits all the signals back to their original signal handlers,
     * discarding all possible changes done to them.
     */
    static synchronized void reinit_all_BANG_() {
        // To get around the fact that we cannot remove elements from a set
        // while iterating over it.
        List<String> signames = new ArrayList<String>(modifiedHandlers);
        for (String signame : signames) {
            reinit_signal_handler_BANG_(signame);
        }
    }

    static synchronized List get_signal_folder_list(String signame) {
        Signal sig = new Signal(signame);
        // Urgh, no easy way to get current signal handler.
        // Double-handle to get current one without issues.
        SignalHandler current = Signal.handle(sig, SignalHandler.SIG_DFL);
        Signal.handle(sig, current);
        if (current instanceof SignalFolder) {
            return ((SignalFolder)current).originalList;
        }
        else {
            Callable<Boolean> wrappedHandler = new CallableSignalHandler(sig, current);
            return new PersistentList(wrappedHandler);
        }
    }

    private static class CallableSignalHandler implements Callable<Boolean> {
        private final Signal sig;
        private final SignalHandler handler;

        CallableSignalHandler(Signal sig, SignalHandler handler) {
            this.sig = sig;
            this.handler = handler;
        }

        @Override
        public Boolean call() {
            try {
                handler.handle(sig);
                return true;
            } catch (Exception e) { // Not throwable, will still die on errors.
                return false;
            }
        }
    }
}

@@ -2445,24 +2445,295 @@
    %7D%0A    %7D%0A%0A
+    /**%0A     * Returns a list of Callables which is used within the SignalFolder%0A     * handling the Signal, or a PersistentList with a Callable SignalHandler if%0A     * the SignalHandler is not a SignalFolder.%0A     *%0A     * @param signame The name of the Signal.%0A     */%0A
     static s
@@ -2782,32 +2782,32 @@
 ring signame) %7B%0A
-
         Signal s
@@ -3349,24 +3349,339 @@
    %7D%0A    %7D%0A%0A
+    /**%0A     * A Callable SignalHandler is simply a Callable which wraps a%0A     * SignalHandler. This is used internally to ensure that people can perform%0A     * %3Ccode%3Eswap!%3C/code%3E in Clojure programs without worrying that the default%0A     * SignalHandler will cause issues as it's not Callable by default.%0A     */%0A
     private 
@@ -3826,16 +3826,147 @@
 ndler;%0A%0A
+        /**%0A         * Returns a Callable which will call %3Ccode%3Ehandler.handle(sig)%3C/code%3E%0A         * whenever called.%0A         */%0A
         
@@ -4095,24 +4095,432 @@
 %0A        %7D%0A%0A
+        /**%0A         * Calls the SignalHandler with the signal provided at construction, and%0A         * returns true if the handler doesn't cast any exception. If the%0A         * handler cast an exception, false is returned, and if the handler%0A         * casts an error, that error is cast.%0A         *%0A         * @return true if the handler doesn't throw an exception, false%0A         * otherwise.%0A         */%0A
         @Ove

Document internals.