package de.renew.engine.simulator;

import de.renew.engine.searcher.Finder;
import de.renew.engine.searcher.Searchable;
import de.renew.engine.searcher.Searcher;
import de.renew.engine.searchqueue.SearchQueue;
import de.renew.engine.thread.SimulationThreadPool;

/**
 * A finder that keeps track of the overall earliest time
 * of all searchers it is informed about.
 * <p>
 * This finder informs the aggregated finder only about those
 * searchers that are usable at the current simulation time.
 * </p>
 */
public class OverallEarliestTimeFinder implements Finder {
    private final Finder _finder;
    private double _overallEarliestTime = Double.POSITIVE_INFINITY;

    /**
     * Creates a new overall earliest time finder
     * that aggregates the given finder.
     *
     * @param finder the aggregated finder
     */
    public OverallEarliestTimeFinder(Finder finder) {
        _finder = finder;
    }

    @Override
    public void found(Searcher searcher) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        // We must prepare a reinsertion of this transition
        // into the list of activated elements, regardless whether this
        // transition instance is actually selected by the binder.
        double possibleTime = searcher.getEarliestTime();
        double currentTime = SearchQueue.getTime();
        if (possibleTime < currentTime) {
            possibleTime = currentTime;
        }

        // Keep track of the earliest time stamp found.
        // In theory, no searchable should be checked twice.
        if (possibleTime < _overallEarliestTime) {
            _overallEarliestTime = possibleTime;
        }

        // Only inform aggregated finder if the searchable is usable now.
        if (possibleTime == currentTime) {
            _finder.found(searcher);
        }
    }

    @Override
    public boolean isCompleted() {
        return _finder.isCompleted();
    }

    /**
     * Returns the overall earliest time of all searchers
     * that have been reported to this finder so far.
     *
     * @return the overall earliest time
     */
    public double getOverallEarliestTime() {
        return _overallEarliestTime;
    }

    /**
     * Insert the given searchable into the {@link SearchQueue}
     * at that point of time that is currently determined
     * as the earliest overall firing time by this finder.
     *
     * @param searchable the searchable to insert
     */
    public void insertIntoSearchQueue(Searchable searchable) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        if (_overallEarliestTime < Double.POSITIVE_INFINITY) {
            SearchQueue.include(searchable, _overallEarliestTime);
        }
    }
}