package de.renew.net.inscription.arc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;
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;
import de.renew.util.ReflectionSerializer;

/**
 * A ClearArc is used to remove all tokens from a place.
 */
public class ClearArc implements TransitionInscription {
    /**
     * 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 expression.
     */
    private final Expression _expression;
    /**
     * {@code true}, if tracing should be activated or {@code false}, otherwise.
     */
    private boolean _trace;

    /**
     * The type of token that can be removed using the arc.
     * This field is not really transient, but as {@code java.lang.Class}
     * is not always serializable, we have to store it by
     * ourselves.
     **/
    private transient Class<?> _elementType;

    /**
     * Constructs a new ClearArc based on a {@link Place}, a {@link Transition},
     * an {@link Expression} and an element type.
     *
     * @param place the {@code Place} that the arc connects to
     * @param transition the {@code Transition} that the arc connects to
     * @param expression the {@code Expression} that is inscribed on the arc
     * @param elementType the type of token that can be removed using the arc
     */
    public ClearArc(
        Place place, Transition transition, Expression expression, Class<?> elementType)
    {
        _place = place;
        _transition = transition;
        _expression = expression;
        _elementType = elementType;
        _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 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 ClearArcOccurrence(this, mapper, netInstance));
        return coll;
    }

    /**
     * Serialization method, behaves like default writeObject
     * method. Stores the not-really-transient elementType field.
     *
     * @param out the {@link ObjectOutputStream} to serialize to
     * @throws IOException if the I/O operations on the stream fail
     **/
    @Serial
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        ReflectionSerializer.writeClass(out, _elementType);
    }

    /**
     * Deserialization method, behaves like default readObject
     * method. Restores the not-really-transient elementType field.
     *
     * @param in the {@link ObjectInputStream} to deserialize from
     * @throws IOException if the I/O operations on the stream fail
     * @throws ClassNotFoundException if the class of the serialized object cannot be found
     **/
    @Serial
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        _elementType = ReflectionSerializer.readClass(in);
    }

    Place getPlace() {
        return _place;
    }

    Transition getTransition() {
        return _transition;
    }

    Expression getExpression() {
        return _expression;
    }

    Class<?> getElementType() {
        return _elementType;
    }
}