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;


// Recently added synchronisation. Will be necessary
// in concurrent environments. Should be covered by
// the place locking scheme, but let's not take any risks,
// because this might change.

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

    /**
     * Constructs a new TriggerableCollectionImpl.
     * Initially, the collection is empty.
     */
    public TriggerableCollectionImpl() {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        _triggerables = new HashSet<>();
    }

    @Override
    public synchronized void include(Triggerable triggerable) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        TriggerCollection triggers = triggerable.triggers();
        synchronized (triggers) {
            _triggerables.add(triggerable);
            triggers.include(this);
        }
    }

    @Override
    public synchronized void exclude(Triggerable triggerable) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        TriggerCollection triggers = triggerable.triggers();
        synchronized (triggers) {
            _triggerables.remove(triggerable);
            triggers.exclude(this);
        }
    }

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

        // Send the notifications. No need to synchronize here.
        // Better allow concurrent accesses to reduce deadlock potential.
        // This way the triggerables can exclude themselves during the
        // notification.
        triggerables.forEach(Triggerable::proposeSearch);
    }
}