package de.renew.engine.simulator;

import java.util.HashSet;
import java.util.Set;


/**
 * The binding list class keeps track of all currently firing bindings.
 * All bindings are automatically registered at this list. This is no
 * problem with respect to garbage collection, because active bindings
 * are references by a thread anyway.
 * <p>
 * Currently, no special structure is given to this list.
 * <p>
 * Accesses to the net list are synchronized to forbid concurrent updates.
 * No special actions must be taken on the caller side.
 * No deadlocks are possible.
 */
public class BindingList {

    /**
     * This set contains all currently active Bindings.
     */
    private static final Set<Binding> BINDINGS = new HashSet<>();

    /**
     * A lock object is used to trigger the waiting threads.
     */
    private static final Object LOCK = new Object();

    /**
     * This is an entirely static class.
     * No instance creation is allowed.
     */
    private BindingList() {}

    /**
     * Register a binding. If the binding has already
     * been registered, ignore.
     *
     * @param binding the binding to be registered.
     */
    public static void register(Binding binding) {
        synchronized (LOCK) {
            BINDINGS.add(binding);
        }
    }

    /**
     * Remove a binding from the list. If the binding is not
     * contained in the list, ignore.
     *
     * @param binding the binding to be removed.
     */
    public static void remove(Binding binding) {
        synchronized (LOCK) {
            BINDINGS.remove(binding);
            if (BINDINGS.isEmpty()) {
                LOCK.notifyAll();
            }
        }
    }

    /**
     * Create a snapshot of the list of bindings.
     * The bindings are returned in an arbitrary order.
     *
     * @return an array that references all well-known net instances
     */
    public static Binding[] getAll() {
        synchronized (LOCK) {
            return BINDINGS.toArray(new Binding[0]);
        }
    }

    /**
     * Wait until all firing transitions have finished.
     * <p>
     * Make sure that no simulator is running while this method is called,
     * otherwise the simulator could start another transition before
     * the result of this method can be processed.
     */
    public static void waitUntilEmpty() {
        synchronized (LOCK) {
            while (!BINDINGS.isEmpty()) {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    // This is expected.
                }
            }
        }
    }
}