package de.renew.net.inscription.transition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import de.renew.engine.searcher.Binder;
import de.renew.engine.searcher.ChannelBinder;
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.engine.thread.SimulationThreadPool;
import de.renew.expression.Expression;
import de.renew.expression.VariableMapper;
import de.renew.net.TransitionInstance;
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;

/**
 * Occurrence of a downlink on a transition.
 */
public class DownlinkOccurrence extends AbstractOccurrence {
    private final Expression _params;
    private final Expression _callee;
    private final String _name;
    private final boolean _isOptional;
    private final VariableMapper _mapper;

    /**
     * Assigns the given values to {@code params}, {@code callee}, {@code name}, {@code isOptional}, {@code mapper}
     * and {@code tInstance} and checks if a {@link SimulationThreadPool} is a {@link SimulationThread}.
     *
     * @param params the {@link Variable} to be bound
     * @param callee the {@link Expression} to be assigned a given value to
     * @param name the name of the given {@link DownlinkOccurrence}
     * @param isOptional {@code true} when it indicates that this channel need not be used if
     *                   the called net has no appropriate channels at all
     * @param mapper the {@link VariableMapper} to be assigned a given value to
     * @param tInstance the {@link TransitionInstance} which is used for the initialization
     *                  of the new Occurrence related to the given transition instance
     */
    public DownlinkOccurrence(
        Expression params, Expression callee, String name, boolean isOptional,
        VariableMapper mapper, TransitionInstance transitionInstance)
    {
        super(transitionInstance);
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        _params = params;
        _callee = callee;
        _name = name;
        _isOptional = isOptional;
        _mapper = mapper;
    }

    @Override
    public Collection<Binder> makeBinders(Searcher searcher) throws Impossible {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        IStateRecorder stateRecorder = searcher.getStateRecorder();
        ICalculationChecker calculationChecker = searcher.getCalculationChecker();
        Variable calleeVariable = new Variable(
            _callee.startEvaluation(_mapper, stateRecorder, calculationChecker), stateRecorder);
        Variable paramsVariable = new Variable(
            _params.startEvaluation(_mapper, stateRecorder, calculationChecker), stateRecorder);
        List<Binder> binders = new ArrayList<>();
        binders.add(new ChannelBinder(calleeVariable, _name, paramsVariable, _isOptional));
        return binders;
    }

    @Override
    public Collection<Executable> makeExecutables(VariableMapperCopier copier) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        // A downlink does not cause any explicit action
        // during execution.
        return Collections.emptySet();
    }

    @Override
    public OccurrenceDescription makeOccurrenceDescription(
        VariableMapperCopier variableMapperCopier)
    {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        return null;
    }
}