package de.renew.engine.searcher;

import java.util.Collection;

import de.renew.unify.ICalculationChecker;
import de.renew.unify.IStateRecorder;
import de.renew.unify.Variable;

/**
 * An {@code ISearcher} coordinates the search for an activated binding
 * and keeps track of various possibilities to
 * assign values to variables. To this end, it keeps
 * track of a set of {@link Binder}s that may represent input
 * arcs, synchronous channels, or the like.
 * The searcher also makes sure to inform a
 * {@link Finder} about every acceptable binding that was
 * found.
 * <p>
 * Each run of a searcher may produce multiple possible
 * binding, but at most one of these bindings may be executed.
 */
public interface ISearcher {

    /**
     * Returns the {@code ICalculationChecker} used by this searcher.
     *
     * @return the {@code ICalculationChecker}.
     */
    ICalculationChecker getCalculationChecker();

    /**
     * Returns the {@code IStateRecorder} used by this searcher.
     *
     * @return the {@code IStateRecorder}.
     */
    IStateRecorder getStateRecorder();

    /**
     * Returns, whether the current search has been completed and thus should not proceed further.
     *
     * @return {@code true}, if the search has been completed and no further binding should be found, {@code false} otherwise.
     */
    boolean isCompleted();

    /**
     * Includes the given {@code TriggerableCollection} in the search.
     *
     * @param triggerables the  {@code TriggerableCollection} to include
     */
    void insertTriggerable(TriggerableCollection triggerables);

    /**
     * Adds the given {@code Occurrence} object into the search.
     * The given object may then be used to create a new {@link Binder} that will be used during searching.
     *
     * @param occurrence the {@code Occurrence} to add
     */
    void addOccurrence(Occurrence occurrence);

    /**
     * Excludes the given {@code Occurrence} object from the search.
     *
     * @param occurrence the {@code Occurrence} to exclude
     */
    void removeOccurrence(Occurrence occurrence);

    /**
     * Return the occurrences that are currently registered
     * at this searcher.
     * See also: {@link Occurrence}
     *
     * @return an enumeration of all occurrences
     */
    Collection<Occurrence> getOccurrences();

    /**
     * Retrieve one delta set object for use with this
     * searcher. The delta set is chosen based on the category
     * answered by the argument factory. If no appropriate
     * object is available, create one using the given
     * factory object.
     *
     * @param factory the factory object which is responsible for creating
     *   delta sets of the desired category
     * @return an appropriate delta set
     */
    DeltaSet getDeltaSet(DeltaSetFactory factory);

    /**
     * When in a finder called by this searcher,
     * retrieve the earliest possible moment of time
     * when the binding might be enacted.
     *
     * @return the time
     */
    double getEarliestTime();

    /**
     * Performs a search using the current configuration of {@link Binder}s.
     * At the end of this method the original state of the search is restored.
     */
    void search();

    /**
     * Performs a search using the given {@code Occurrence}.
     * At the end of this method the original state of the search is restored.
     *
     * @param occurrence the {@code Occurrence} to use for searching
     */
    void search(Occurrence occurrence);

    /**
     * Add a new binder that must be processed before
     * this searcher can contact the finder.
     *
     * @param binder the binder to be added
     */
    void addBinder(Binder binder);

    /**
     * Remove a previously added binder.
     *
     * @param binder the binder to be removed
     */
    void removeBinder(Binder binder);

    /**
     * Convenience method for adding an entire set
     * of binders.
     *
     * @param binders the {@link Binder} objects to be added
     */
    void addBinders(Collection<Binder> binders);

    /**
     * Convenience method for removing an entire set
     * of binders.
     *
     * @param binders the {@link Binder} objects to be removed
     */
    void removeBinders(Collection<Binder> binders);

    /**
     * Performs the search on a given searchable, making sure to register
     * dependencies. The given triggerable might be triggered
     * as soon as this method begins its work. In that case
     * the result of this search should be discarded.
     * The finder will be informed of all bindings, even those
     * bindings that will only become activated in the future.
     *
     * @param finder the finder that will be informed about
     *   possible bindings
     * @param searchable the object to be searched
     * @param triggerable the object that will be notified
     *   when an object changes, if that object is relevant to this
     *   search process
     */
    void searchAndRecover(Finder finder, Searchable searchable, Triggerable triggerable);

    /**
     * Tries to find an activated binding by targeting
     * a channel request at the given {@code ChannelTarget}.
     *
     * @param channelTarget the channel target to be searched
     * @param name the name of the channel
     * @param params the channel parameters, typically a tuple
     * @param isOptional if true, the search will succeed even
     *   if the channel target does not declare a channel
     *   of the given name
     * @param finder the finder to be informed about the binding
     * @param triggerable the triggerable to be informed about changes
     */
    void initiatedSearch(
        ChannelTarget channelTarget, String name, Variable params, boolean isOptional,
        Finder finder, Triggerable triggerable);
}
