/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/09/26, 13:22
!  AUTHOR(S): KOGA, Junichiro
!  File : UndoRedo.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.inputinterface;

import java.util.Stack;
import java.util.Vector;

import org.apache.log4j.Logger;

/**
 * InputInterfaceのundo/redoを行うクラス. ただし, redoはちょっと難しいのでペンディング.
 * 
 * @author
 */
class UndoRedo implements InputInterfaceEntryChangeListener {
    private Logger logger = Logger.getLogger(UndoRedo.class.getName());
    private InputInterface inputInterface;

    private Stack undoStack = new Stack();
    private Stack undoTagStack = new Stack();

    private Stack redoStack = new Stack();
    private Stack redoTagStack = new Stack();

    private boolean undoing = false;

    private Vector undoRedoListeners = new Vector();

    /**
     * Creates a new instance of UndoRedo
     * 
     * @param inputInterface
     *            undo/redo操作を制御したいInputInterfaceオブジェクト.
     */
    protected UndoRedo(InputInterface inputInterface) {
        this.inputInterface = inputInterface;
    }

    protected void clear() {
        undoStack = new Stack();
        undoTagStack = new Stack();
        redoStack = new Stack();
        redoTagStack = new Stack();
        stateChanged(new UndoRedoEvent(UndoRedoEvent.STACK_UPDATE,
                inputInterface, undoStack.size(), redoStack.size()));
    }

    /**
     * Undo/Redoイベントを察知するリスナーをこのクラスに登録する.
     * 
     * @param UndoRedoイベントリスナー
     *            .
     */
    public void addUndoRedoListener(UndoRedoListener listener) {
        undoRedoListeners.addElement(listener);
    }

    private void stateChanged(UndoRedoEvent e) {
        for (int i = 0; i < undoRedoListeners.size(); i++) {
            ((UndoRedoListener) undoRedoListeners.elementAt(i))
                    .undoRedoStateChanged(e);
        }
    }

    /**
     * スタックにアンドゥ用のエントリーを追加する. (リドゥ用のスタックは, アンドゥ時にこのクラスが勝手に作成する)
     * 
     * @param tag
     *            このエントリーの「フルパス」
     * @param entry
     *            追加したいInputInterfaceEntryオブジェクト.
     */
    protected void addEntry(String tag, InputInterfaceEntry entry) {
        // entry.saveState();
        undoTagStack.push(tag);
        undoStack.push(entry);
        stateChanged(new UndoRedoEvent(UndoRedoEvent.STACK_UPDATE,
                inputInterface, undoStack.size(), redoStack.size(), entry));
    }

    /**
     * このオブジェクトに登録されているInputInterfaceオブジェクトに対してundo操作を行う.
     */
    protected void undo() {
        if (!undoStack.empty() && !undoTagStack.empty()) {
            // get the component that will be undo-ed.
            String tag = (String) undoTagStack.pop();
            InputInterfaceEntry entry = (InputInterfaceEntry) undoStack.pop();
            doIt(tag, entry, true);
            stateChanged(new UndoRedoEvent(UndoRedoEvent.UNDO, inputInterface,
                    undoStack.size(), redoStack.size()));
        } else {
            logger.info("no data to \"undo\".");
        }

    }

    /**
     * このオブジェクトに登録されているInputInterfaceオブジェクトに対してredo操作を行う. (あまりうまく行かないのでペンディング).
     */
    protected void redo() {
        if (!redoStack.empty() && !redoTagStack.empty()) {
            // get the component that will be redo-ed.
            String tag = (String) redoTagStack.pop();
            InputInterfaceEntry entry = (InputInterfaceEntry) redoStack.pop();
            doIt(tag, entry, false);
            stateChanged(new UndoRedoEvent(UndoRedoEvent.REDO, inputInterface,
                    undoStack.size(), redoStack.size()));
        } else {
            logger.info("no data to \"redo\".");
        }
    }

    private void doIt(String tag, InputInterfaceEntry entry, boolean undo) {
        // do not update the "undo stack" while undoing (but this is not the
        // case for "redo")!

        if (undo) {
            // logger.debug("redoStack.push("+entry+")");
            redoStack.push(entry);
            redoTagStack.push(tag);
        }

        inputInterface.setUpdateUndoStack(!undo);

        if (undo) {
            logger.debug("undoing tag: " + tag + " entry: " + entry);
            entry.undo();
        } else {
            entry.redo();
        }

        inputInterface.selectRoot();
        inputInterface.selectBlock(tag);
        inputInterface.replaceEntry(entry, this, true);

        // restore the "undo stack" update mechanism (this is always true).
        inputInterface.setUpdateUndoStack(true);
    }

    public void inputInterfaceEntryChanged(InputInterfaceEntryChangeEvent e) {
    }

    public void inputInterfaceInitialized() {
    }

    public void inputInterfaceInitializing() {
    }

}
