package de.renew.net.inscription.arc;

import java.util.Collection;
import java.util.Vector;

import de.renew.engine.searcher.Occurrence;
import de.renew.engine.searcher.Searcher;
import de.renew.expression.Expression;
import de.renew.expression.Function;
import de.renew.expression.VariableMapper;
import de.renew.net.NetInstance;
import de.renew.net.Place;
import de.renew.net.Transition;
import de.renew.net.inscription.TransitionInscription;

/**
 * A FlexibleArc (also called multiple input arc) moves a varying amount of tokens depending on the binding of its
 * variables.
 * <p>
 * The arc types that are annotated with an asterisk require a time expression.
 */
public class FlexibleArc implements TransitionInscription {
    // Only some of the arc types known from ordinary arcs are implemented
    // for flexible arcs so far.
    /**
     * Arc type: input arc (*).
     **/
    public static final Arc.Type IN = Arc.Type.IN;

    /**
     * Arc type: output arc (*).
     **/
    public static final Arc.Type OUT = Arc.Type.OUT;

    /**
     * Arc type: double arc (*) that releases the token early
     * during the firing.
     **/

    public static final Arc.Type FAST_BOTH = Arc.Type.FAST_BOTH;

    /**
     * The {@code Place} that the arc connects to.
     */
    private final Place _place;

    /**
     * The {@code Transition} that the arc connects to.
     */
    private final Transition _transition;

    /**
     * The arc's type.
     */
    private final Arc.Type _arcType;

    /**
     * The expression is evaluated and must result in
     * an array, a vector, a list, or (in the case of output arcs)
     * an enumeration.
     * <p>
     * Enumerations are not supported for input arcs, because they
     * can be easily destroyed by reading them once. Because input
     * arcs might have to be checked multiple times, this is unacceptable.
     * <p>
     * Copying the contents of the enumeration into a temporary
     * vector using an auxiliary expression will not work in all cases.
     * Consider the following: One transition has a flexible output arcs
     * inscribed x. x is received through a synchronous channel
     * this:ch(x,y). y is bound to an enumeration in the spontaneous
     * transition. The channel can be satisfied by two transitions,
     * both of which set x=y. Now the first channel is searched,
     * x is let to y and the enumeration is consumed during a
     * copying operation. The search fails, backtracks and tries the
     * second matching channel. Now x is rebound to the enumeration y,
     * but the enumeration is already corrupted and emptied. The search
     * might succeed illegally.
     */
    private final Expression _expression;
    /**
     * The function that converts tokens going through the arc in one direction.
     */
    private final Function _forwardFunction;
    /**
     * The function that converts tokens going through the arc in the backward direction.
     */
    private final Function _backwardFunction;
    /**
     * {@code true}, if tracing is enabled or {@code false}, otherwise.
     */
    private boolean _trace;

    /**
     * Constructs a new FlexibleArc.
     *
     * @param place the {@code Place} the arc connects to
     * @param transition the {@code Transition} the arc connects to
     * @param arcType the type of arc the FlexibleArc represents
     * @param expression the {@code Expression} inscribed on the arc, which must result in an array, a vector, a list or
     *                   (only on an output arc) an enumeration.
     * @param forwardFunction the {@code Function} that converts tokens going through the arc in one direction
     * @param backwardFunction the {@code Function} that converts tokens going through the arc in the backward direction
     */
    public FlexibleArc(
        Place place, Transition transition, Arc.Type arcType, Expression expression,
        Function forwardFunction, Function backwardFunction)
    {
        _place = place;
        _transition = transition;
        _arcType = arcType;
        _expression = expression;
        _forwardFunction = forwardFunction;
        _backwardFunction = backwardFunction;
        _trace = true;
    }

    boolean isOutputArc() {
        return _arcType == Arc.Type.OUT;
    }

    /**
     * Activate/Deactivate tracing on this arc according to the given parameter.
     *
     * @param trace {@code true}, if tracing is enabled or {@code false}, otherwise.
     */
    public void setTrace(boolean trace) {
        _trace = trace;
    }

    /**
     * Returns {@code true} if tracing is enabled or {@code false} otherwise.
     *
     * @return {@code true}, if tracing is enabled or {@code false}, otherwise.
     */
    public boolean getTrace() {
        return _trace;
    }

    @Override
    public Collection<Occurrence> makeOccurrences(
        VariableMapper mapper, NetInstance netInstance, Searcher searcher)
    {
        Collection<Occurrence> coll = new Vector<>();
        coll.add(new FlexibleArcOccurrence(this, mapper, netInstance));
        return coll;
    }

    Place getPlace() {
        return _place;
    }

    Transition getTransition() {
        return _transition;
    }

    Arc.Type getArcType() {
        return _arcType;
    }

    Expression getExpression() {
        return _expression;
    }

    Function getForwardFunction() {
        return _forwardFunction;
    }

    Function getBackwardFunction() {
        return _backwardFunction;
    }

    boolean isTrace() {
        return _trace;
    }
}