package de.renew.net.inscription.arc;

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

import de.renew.engine.searcher.Binder;
import de.renew.engine.searcher.Executable;
import de.renew.engine.searcher.OccurrenceDescription;
import de.renew.engine.searcher.Searcher;
import de.renew.engine.searcher.VariableMapperCopier;
import de.renew.expression.VariableMapper;
import de.renew.net.NetInstance;
import de.renew.net.SimulatablePlaceInstance;
import de.renew.net.inscription.AbstractOccurrence;
import de.renew.unify.ICalculationChecker;
import de.renew.unify.IStateRecorder;
import de.renew.unify.Impossible;
import de.renew.unify.Variable;

/**
 * A {@code FlexibleArcOccurrence} is the occurrence of a {@link FlexibleArc}.
 */
class FlexibleArcOccurrence extends AbstractOccurrence {
    /**
     * The place instance connected to the instance of {@link #_arc} in this occurrence's {@code NetInstance}.
     */
    private final SimulatablePlaceInstance _placeInstance;
    /**
     * The {@code VariableMapper} used to evaluate the inscription of the {@code FlexibleArc}.
     */
    private final VariableMapper _mapper;
    /**
     * The variable representing the token that are transferred through the {@code FlexibleArc} in this occurrence.
     * When the arc's inscription is evaluated, its result is stored in this variable.
     */
    private Variable _tokenVar;
    /**
     * The {@code FlexibleArc} this is an occurrence of.
     */
    private final FlexibleArc _arc;
    /**
     * This variable is assigned by the binder during the bind
     * procedure for input arcs. For output arcs, it has no meaning.
     */
    private Vector<Object> _inTokens;

    /**
     * Constructs a new {@code FlexibleArcOccurrence} based on a {@code FlexibleArc}, a {@code VariableMapper},
     * and the {@code netInstance} it occurs in.
     * 
     * @param arc the {@code FlexibleArc} the {@code FlexibleArcOccurrence} will be an occurrence of.
     * @param mapper the {@code VariableMapper} that will be used to evaluate the arc's inscription.
     * @param netInstance the {@code NetInstance} that the {@code FlexibleArcOccurrence} occurs in.
     */
    public FlexibleArcOccurrence(FlexibleArc arc, VariableMapper mapper, NetInstance netInstance) {
        super(netInstance.getInstance(arc.getTransition()));
        _arc = arc;
        _mapper = mapper;
        _placeInstance = (SimulatablePlaceInstance) netInstance.getInstance(arc.getPlace());
    }

    @Override
    public Collection<Binder> makeBinders(Searcher searcher) throws Impossible {
        IStateRecorder stateRecorder = searcher.getStateRecorder();
        ICalculationChecker calculationChecker = searcher.getCalculationChecker();

        _tokenVar = new Variable(
            _arc.getExpression().startEvaluation(_mapper, stateRecorder, calculationChecker),
            stateRecorder);
        if (_arc.getArcType() == FlexibleArc.OUT) {
            calculationChecker.addLateVariable(_tokenVar, stateRecorder);
            return Collections.emptySet();
        } else {
            Collection<Binder> coll = new Vector<Binder>();
            coll.add(new FlexibleArcBinder(this));
            return coll;
        }
    }

    @Override
    public Collection<Executable> makeExecutables(VariableMapperCopier copier) {
        Collection<Executable> coll = new Vector<>();
        switch (_arc.getArcType()) {
            case OUT:
                Variable copiedTokenVar = (Variable) copier.getCopier().copy(_tokenVar);
                coll.add(new FlexibleOutArcExecutable(_placeInstance, copiedTokenVar, _arc));
                return coll;
            case IN:
            case FAST_BOTH:
                coll.add(new FlexibleInArcExecutable(_placeInstance, _inTokens, _arc));
                return coll;
            default:
                throw new RuntimeException("Bad arc type.");
        }
    }

    @Override
    public OccurrenceDescription makeOccurrenceDescription(
        VariableMapperCopier variableMapperCopier)
    {
        return null;
    }

    SimulatablePlaceInstance getPlaceInstance() {
        return _placeInstance;
    }

    Variable getTokenVar() {
        return _tokenVar;
    }

    FlexibleArc getArc() {
        return _arc;
    }

    Vector<Object> getInTokens() {
        return _inTokens;
    }

    void setInTokens(Vector<Object> inTokens) {
        _inTokens = inTokens;
    }
}