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.VariableMapper;
import de.renew.net.NetInstance;
import de.renew.net.Place;
import de.renew.net.Transition;
import de.renew.net.inscription.TransitionInscription;


/**
 * An {@code Arc} instance represents an arc from or to a place.
 * <p>
 * Arcs can be of different types distinguished by the public
 * class constants defined in this class.
 * The arc types that are annotated with an asterisk require a
 * time expression.
 * </p>
 **/
public class Arc implements TransitionInscription {

    /**
     * Enum representing the type of the arc.
     */
    public enum Type {
        /** Arc type: input arc (*). **/
        IN,
        /** Arc type: test arc. **/
        TEST,
        /** Arc type: output arc (*). **/
        OUT,
        /** Arc type: double arc (*) that reserves the token and applies the time to the input token. **/
        BOTH,
        /** Arc type: double arc (*) that releases the token early during the firing. **/
        FAST_BOTH,
        /** Arc type: test arc that releases the token early during the firing. **/
        FAST_TEST,
        /** Arc type: inhibitor arc. **/
        INHIBITOR,
        /** Arc type: double arc (*) that reserves the token and applies the time to the output token. **/
        BOTH_OT;
    }

    /**
     * The place that the arc connects to.
     */
    private final Place _place;
    /**
     * The transition that the arc connects to.
     */
    private final Transition _transition;
    /**
     * The arc's type.
     */
    private final Type _arcType;
    /**
     * The arc's token expression.
     */
    private final Expression _tokenExpr;
    /**
     * The arc's time expression.
     */
    private final Expression _timeExpr;
    /**
     * {@code true}, if tracing should be activated or {@code false}, otherwise.
     */
    private boolean _trace;

    /**
     * The constructor for Arc.
     *
     * @param place the place the arc connects to
     * @param transition the transition the arc connects to
     * @param arcType the type of the arc
     * @param tokenExpr the token expression of the arc
     * @param timeExpr the time expression of the arc
     */
    public Arc(
        Place place, Transition transition, Type arcType, Expression tokenExpr, Expression timeExpr)
    {
        if (arcType == null) {
            throw new RuntimeException("ArcType cannot be null.");
        }
        _place = place;
        _transition = transition;
        _arcType = arcType;
        _tokenExpr = tokenExpr;
        _timeExpr = timeExpr;
        _trace = true;
    }

    /**
     * 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 the arc is a test arc or {@code false} otherwise.
     *
     * @return {@code true}, if the arc is a test arc or {@code false}, otherwise.
     */
    public boolean isTestArc() {
        return _arcType == Type.TEST || _arcType == Type.FAST_TEST;
    }

    /**
     * Returns {@code true} if the arc is an untimed arc or {@code false} otherwise.
     *
     * @return {@code true}, if the arc is an untimed arc or {@code false}, otherwise.
     */
    public boolean isUntimedArc() {
        return isTestArc() || _arcType == Type.INHIBITOR;
    }

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

    /**
     * Returns {@code true} if this arc is of the specified arc type or {@code false} otherwise.
     *
     * @param type the arc type to check against
     * @return {@code true}, if this arc is of the specified arc type or {@code false}, otherwise.
     */
    public boolean isTypeOf(Type type) {
        return _arcType == type;
    }

    /**
     * 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;
    }

    /**
     * Returns the place this arc connects to.
     *
     * @return the place
     */
    public Place getPlace() {
        return _place;
    }

    /**
     * Returns the transition this arc connects to.
     *
     * @return the Transition
     */
    public Transition getTransition() {
        return _transition;
    }

    /**
     * Returns the type of this arc.
     *
     * @return the ArcType
     */
    public Type getArcType() {
        return _arcType;
    }

    /**
     * Returns the TokenExpression.
     *
     * @return the TokenExpression
     */
    public Expression getTokenExpression() {
        return _tokenExpr;
    }

    /**
     * Returns the TimeExpression.
     *
     * @return the TimeExpression
     */
    public Expression getTimeExpression() {
        return _timeExpr;
    }
}