package de.renew.simulatorontology.shadow;

import java.io.Serial;

/**
 * This class is responsible for creating shadow arcs that connect
 * shadow nodes to shadow transitions, and vice versa.
 * <p>
 * There are different types of shadow arcs. These can be created by using
 * the public constants of this class.
 */
public class ShadowArc extends ShadowConnection {

    /* Serialization support. */
    @Serial
    private static final long serialVersionUID = -774253458615347237L;
    /** Test arc without arrowheads. */
    public static final int TEST = 0;
    /** Standard arc. */
    public static final int ORDINARY = 1;
    /** Reserve arc, shorthand both an input and an output arc. */
    public static final int BOTH = 2;
    /** Inhibitor arc, makes sure that a certain token is not in the place. */
    public static final int INHIBITOR = 3;
    /** Flexible arc, allows multiple tokens to be moved. */
    public static final int DOUBLE_ORDINARY = 4;
    /** Clear arc, removes all tokens from a place. */
    public static final int DOUBLE_HOLLOW = 5;
    /** The transition connected to a place by the shadow arc. */
    private final ShadowTransition _transition;
    /** The place connected to a transition by the shadow arc. */
    private final ShadowPlace _place;

    /** The arc type is determined by the constants given above. */
    private int _shadowArcType;
    /** {@code true}, if the shadow arc leads from a place to a transition. */
    private boolean _placeToTransition;

    /**
     * Creates a shadow arc from one shadow node to another.
     * An arc can only connect a shadow transition and a
     * shadow place or vice versa.
     * @param from          the start node of the shadow arc
     * @param to            the end node of the shadow arc
     * @param shadowArcType the type of the shadow arc: One of
     *                      the constants {@code test},
     *                      {@code ordinary}, or {@code both}
     */
    public ShadowArc(ShadowNode from, ShadowNode to, int shadowArcType) {
        super(from, to);

        if (from instanceof ShadowTransition) {
            if (to instanceof ShadowPlace) {
                _transition = (ShadowTransition) from;
                _place = (ShadowPlace) to;
                _placeToTransition = false;
            } else {
                throw new RuntimeException("Must connect place and transition.");
            }
        } else if (from instanceof ShadowPlace) {
            if (to instanceof ShadowTransition) {
                _place = (ShadowPlace) from;
                _transition = (ShadowTransition) to;
                _placeToTransition = true;
            } else {
                throw new RuntimeException("Must connect place and transition.");
            }
        } else {
            throw new RuntimeException("Must connect place and transition.");
        }

        setShadowArcType(shadowArcType);

        setTrace(true);

        // Ok, remember the inscription.
        getTransition().add(this);
        getPlace().add(this);
    }

    /**
     * Creates an {@code ordinary} shadow arc from one shadow node to another.
     * An arc can only connect a shadow transition and a
     * shadow place or vice versa.
     *
     * @param from the start node of the shadow arc
     * @param to the end node of the shadow arc
     */
    public ShadowArc(ShadowNode from, ShadowNode to) {
        this(from, to, ORDINARY);
    }

    /**
     * Creates a shadow arc with an inscription from one shadow node to another.
     * An arc can only connect a shadow transition and a
     * shadow place or vice versa.
     *
     * @param from the start node of the shadow arc
     * @param to the end node of the shadow arc
     * @param shadowArcType the type of the shadow arc: One of the constants
     *                      {@code test}, {@code ordinary}, or {@code both}
     * @param inscription the inscription of the shadow arc
     */
    public ShadowArc(ShadowNode from, ShadowNode to, int shadowArcType, String inscription) {
        this(from, to, shadowArcType);
        new ShadowInscription(this, inscription);
    }

    /**
     * Creates an {@code ordinary} shadow arc with an inscription from one
     * shadow node to another. An arc can only connect a shadow transition and a
     * shadow place or vice versa.
     *
     * @param from the start node of the shadow arc
     * @param to the end node of the shadow arc
     * @param inscription the inscription of the shadow arc
     */
    public ShadowArc(ShadowNode from, ShadowNode to, String inscription) {
        this(from, to, ORDINARY, inscription);
    }

    @Override
    public void discard() {
        getTransition().remove(this);
        getPlace().remove(this);
        super.discard();
    }

    @Override
    public String toString() {
        return "ShadowArc (type " + getShadowArcType() + ", " + getTransition()
            + (isPlaceToTransition() ? " <- " : " -> ") + getPlace() + ")";
    }

    /**
     * Deserialization method, behaves like default readObject
     * method, additionally re-registers the arc at its nodes.
     *
     * @param in object to be read
     * @throws java.io.IOException if object can't be read
     * @throws ClassNotFoundException if class can't be found while reading the object
     */
    @Serial
    private void readObject(java.io.ObjectInputStream in)
        throws java.io.IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        getTransition().add(this);
        getPlace().add(this);
    }

    /**
     * Getter for the shadow arc type.
     *
     * @return the shadow arc type
     */
    public int getShadowArcType() {
        return _shadowArcType;
    }

    /**
     * Setter for the shadow arc type.
     *
     * @param shadowArcType the shadow arc type
     */
    public void setShadowArcType(int shadowArcType) {
        _shadowArcType = shadowArcType;
    }

    /**
     * Accessor for if the arc leads from a place to a transition.
     *
     * @return {@code true} if arc is from place to transition
     */
    public boolean isPlaceToTransition() {
        return _placeToTransition;
    }

    /**
     * Getter for the connected transition.
     *
     * @return the connected transition
     */
    public ShadowTransition getTransition() {
        return _transition;
    }

    /**
     * Getter for the connected place.
     *
     * @return the connected place
     */
    public ShadowPlace getPlace() {
        return _place;
    }
}