package de.renew.engine.searcher;

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

import de.renew.engine.thread.SimulationThreadPool;


/**
 * Primary implementation of {@link TriggerCollection}.
 */
public class TriggerCollectionImpl implements Serializable, TriggerCollection {
    /**
     * Keep track of triggering objects.
     */
    private final Set<TriggerableCollection> _triggers;

    /**
     * The object that is to be triggered.
     */
    private final Triggerable _triggerable;

    /**
     * Creates a new trigger collection for the given triggerable.
     *
     * @param triggerable the object that is to be triggered
     */
    public TriggerCollectionImpl(Triggerable triggerable) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        _triggers = new HashSet<>();
        _triggerable = triggerable;
    }

    // The following two method must only be accessed through
    // the include and exclude methods of triggerable collections,
    // otherwise deadlocks might occur. Those method will make sure
    // to lock this object.
    @Override
    public void include(TriggerableCollection trigger) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        _triggers.add(trigger);
    }

    @Override
    public void exclude(TriggerableCollection trigger) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        _triggers.remove(trigger);
    }

    @Override
    public void clear() {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        List<TriggerableCollection> triggers;
        synchronized (this) {
            // Get the triggers.
            triggers = new ArrayList<>(_triggers);
        }

        // Send the notifications. No need to synchronize here.
        // Better allow concurrent accesses to reduce deadlock potential.
        // The triggerable collections are required to do the locking.
        triggers.forEach(triggerables -> triggerables.exclude(_triggerable));
    }
}