package pipe.historyActions;


import uk.ac.imperial.pipe.models.petrinet.Transition;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;

/**
 * AnimationHistory for an individual PetriNet
 */
public final class AnimationHistoryImpl extends Observable implements AnimationHistory {
    /**
     * List to hold transitions fired in their order
     * Used for going back/forward in time
     */
    private List<Transition> firingSequence = new ArrayList<>();

    /**
     * Current index of the firingSequence;
     * Initialised to -1 so when the first item is added it points to zero
     */
    private int currentPosition = -1;


    /**
     * Cannot step forward if head of the list
     *
     * @return true if stepping forward within the animation is allowed, that is if there are transition firings to redo
     */
    @Override
    public boolean isStepForwardAllowed() {
        return currentPosition < firingSequence.size() - 1;
    }


    /**
     * Can step back if currentPosition points to any transitions
     *
     * @return true if stepping backward within the animation is allowed, that is if there are transition firings to undo
     */
    @Override
    public boolean isStepBackAllowed() {
        return currentPosition >= 0;
    }

    /**
     * Steps forward firing the transition associated with the latest action
     */
    @Override
    public void stepForward() {
        if (isStepForwardAllowed()) {
            currentPosition++;
            flagChanged();
        }
    }


    /**
     * Steps backwards updating the current transition highlighted in the list
     */
    @Override
    public void stepBackwards() {
        if (isStepBackAllowed()) {
            currentPosition--;
            flagChanged();
        }
    }

    /**
     * Remove all steps past the current step
     */
    @Override
    public void clearStepsForward() {
        if (currentPosition >= -1 && currentPosition + 1 < firingSequence.size()) {
            while (firingSequence.size() > currentPosition + 1) {
                firingSequence.remove(firingSequence.size() - 1);
            }
        }
    }

    /**
     *
     * @return list of transitions in the firing sequence
     */
    @Override
    public List<Transition> getFiringSequence() {
        return firingSequence;
    }

    /**
     *
     * @return current position in the firing sequence
     */
    @Override
    public int getCurrentPosition() {
        return currentPosition;
    }

    /**
     * Add a transition to the head of the firing sequence
     * @param transition to be added 
     */
    @Override
    public void addHistoryItem(Transition transition) {
        firingSequence.add(transition);
        currentPosition++;
        flagChanged();
    }

    /**
     *
     * @return transition at current position in the firing sequence
     */
    @Override
    public Transition getCurrentTransition() {
        if (currentPosition >= 0) {
            return firingSequence.get(currentPosition);
        }
        throw new RuntimeException("No transitions in history");
    }

    /**
     *
     * @param index of the transition 
     * @return transition at the given index in the firing sequence
     */
    @Override
    public Transition getTransition(int index) {
        if (index <= firingSequence.size()) {
            return firingSequence.get(index);
        }
        throw new RuntimeException("Index is greater than number of transitions stored");
    }

    /**
     * Clears the firing sequence
     */
    @Override
    public void clear() {
        currentPosition = -1;
        firingSequence.clear();
        flagChanged();
    }

    /**
     * Rolls the setting changed and notifying observers into one method call.
     * It tells Observers that it has changed
     */
    private void flagChanged() {
        setChanged();
        notifyObservers();
    }
}
