package de.renew.net.inscription.arc;

import de.renew.engine.common.SimulatorEventLogger;
import de.renew.engine.events.Inhibiting;
import de.renew.engine.searcher.EarlyExecutable;
import de.renew.net.SimulatablePlaceInstance;
import de.renew.simulatorontology.simulation.StepIdentifier;
import de.renew.unify.Impossible;

/**
 * An {@code InhibitorExecutable} executes whenever a transition fires that was not inhibited by the inhibitor
 * arc it belongs to. Except for a last check in the {@link #verify} method, the inhibition itself is handled by the
 * {@link InhibitorArcBinder}.
 */
class InhibitorExecutable implements EarlyExecutable {
    /** The place instance that is connected to the inhibitor arc. */
    private final SimulatablePlaceInstance _placeInstance;
    /** The token that the expression on the inhibitor arc has been bound to. */
    private final Object _token;
    /** Whether the execution of this {@code InhibitorExecutable} should be logged. */
    private final boolean _trace;

    /**
     * Constructs a new {@code InhibitorExecutable} based on the transition/place instances the inhibitor arc
     * connects, a token and whether tracing should be active on it.
     *
     * @param placeInstance the place instance that can contain tokens that inhibit the arc.
     * @param token a token that would inhibit the firing of {@code transitionInstance}.
     * @param trace whether the execution of the {@code InhibitorExecutable} should be logged.
     */
    InhibitorExecutable(SimulatablePlaceInstance placeInstance, Object token, boolean trace) {
        _placeInstance = placeInstance;
        _token = token;
        _trace = trace;
    }

    @Override
    public long lockPriority() {
        return _placeInstance.getLockOrder();
    }

    @Override
    public int phase() {
        // Test before other executables have the chance to remove
        // tokens from the place.
        return INHIBIT;
    }

    /**
     * Locks the {@link de.renew.net.PlaceInstance#_lock PlaceInstance} associated with this arc.
     **/
    @Override
    public void lock() {
        _placeInstance._lock.lock();
    }

    @Override
    public void verify(StepIdentifier stepIdentifier) throws Impossible {
        if (_placeInstance.containsTestableToken(_token)) {
            // Undo locking.
            throw new Impossible();
        }
    }

    @Override
    public void execute(StepIdentifier stepIdentifier) {
        // Does it really make sense to output a trace message?
        if (_trace) {
            // log activities on net level
            SimulatorEventLogger
                .log(stepIdentifier, new Inhibiting(_token, _placeInstance), _placeInstance);
        }
    }

    @Override
    public void rollback() {
        // No harm done.
    }

    /**
     * Unlocks the {@link de.renew.net.PlaceInstance#_lock PlaceInstance} associated with this arc.
     **/
    @Override
    public void unlock() {
        _placeInstance._lock.unlock();
    }
}