package nl.graphit.util.jsf.history;

import java.io.Serializable;
import java.util.Stack;

import javax.faces.context.FacesContext;

/**
 * A request scope managed bean to keep track of previous pages, including all
 * their state. To use this bean, define it as a request scope managed bean in
 * JSF using the name <code>historyManager</code>. Then on all pages that
 * need to remember previous pages, save the bean using the myfaces saveState
 * tag: <code>&lt;t:saveState value="#{historyManager}" /%gt;</code>
 * 
 * @author Nico Krijnen <nico.krijnen@graphit.nl>
 */
public class HistoryManager implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final String BEAN_NAME = "historyManager";

    private Stack history = new Stack();

    /**
     * Returns the current instance of the history manager by evaluating the EL
     * expression #{historyManager}.
     * 
     * @return history manager
     */
    public static HistoryManager getCurrentInstance() {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return (HistoryManager) facesContext.getApplication()
                .createValueBinding("#{" + BEAN_NAME + "}").getValue(
                        facesContext);
    }

    /**
     * Saves the state of the current FacesContext and pushes it onto the
     * history stack.
     * 
     * @see ViewState
     */
    public void addCurrentViewState() {
        FacesContext facesContext = FacesContext.getCurrentInstance();

        ViewState viewState = new ViewState(facesContext);
        history.push(viewState);
    }

    /**
     * Restores the current FacesContext to the last state that was pushed onto
     * the history stack (and removes that state from the stack).
     * 
     * @return always returns null so that this method can be used as a JSF
     *         action method.
     * @throws IllegalStateException
     *             when the history stack is empty.
     */
    public String returnToLast() {
        if (history.empty()) {
            throw new IllegalStateException(
                    "History is empty, cannot retreive last state. Make sure you save the 'historyManager' in the page state.");
        }

        FacesContext facesContext = FacesContext.getCurrentInstance();

        ViewState viewState = (ViewState) history.pop();
        viewState.restore(facesContext);

        return null;
    }

    /**
     * Returns the view-id of the last entry that was pushed onto the history
     * stack.
     * 
     * @return view id of last state added to history.
     */
    public String getLastViewId() {
        ViewState viewState = (ViewState) history.peek();
        return (viewState == null) ? null : viewState.getViewId();
    }

    /**
     * Removes the last state from the history stack.
     */
    public void removeLast() {
        history.pop();
    }

    ViewState getLastViewState() {
        return (ViewState) history.peek();
    }

}
