/*
 * @(#)FobManager.java
 *
 * Copyright 2001 by Intelligent Technology Inc. All rights reserved.
 */

package jp.co.iti.fagot.fob;

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

import jp.co.iti.fagot.util.ZDebug;
/**
 * Fobマネージャ
 *
 * FElementの管理クラス。
 * <ul>
 * <li>beginTransaction,commit,rollbackなどトランザクションを定義する
 * <li>トランザクション制御に従った、undo,redoを定義する
 * <li>undo,redoによるデータの変更を取得するリスナーを管理する
 * <li>undo,redoによるデータの変更をリスナーに通知する
 * </ul>
 *
 * @author  千曳秀喜
 * @version 1.01 2001/01/07
 */

public class FobManager {

	/**
	 * デバッグフラグ
	 */
	protected boolean bTrace = false;

	/**
	 * コンストラクタ
	 */
	public FobManager(){
	reset();
	}

	//## 更新状態の取得 ###############################################
	/**
	 * 更新状態の初期化
	 * ドキュメントが保存されたときに更新状態を初期化するために呼び出されます
	 */
	public void setModified() {
	mIsModified = false ;
	}

	/**
	 * 更新状態の取得
	 */
	public boolean isModified() {
	return mIsModified ;
	}


	//## トランザクション管理 #########################################
	/**
	 * undoスタックのクリア
	 */
	public void reset() {
	mUndoTran = null;
	mUndo = new Stack();
	mRedo = new Stack();
	}

	/**
	 * トランザクション中か？
	 */
	public boolean isTransaction() {
	return ( mUndoTran != null );
	}

//	private String mPrevStack = null;
	/**
	 * トランザクションの開始
	 */
	public void beginTransaction() {
	if ( bTrace ) ZDebug.trace("begin");
	if ( mUndoTran == null ) {
		mUndoTran = new Stack();
//		mPrevStack = ZDebug.getStack();
	} else {
		ZDebug.trace("Duplicate Transaction");
//		ZDebug.printStack();
//		ZDebug.trace("PrevStack ---");
//		System.out.println(mPrevStack);
	}
	}

	/**
	 * コミット
	 */
	public void commit() {

	if ( mUndoTran == null ) {
		if ( bTrace ) ZDebug.trace("commit:none");
	} else if ( !mUndoTran.isEmpty() ) {
		Vector items = getFobs( mUndoTran ) ;
		firePropertyChange( FPropertyChangeEvent.PRE_COMMIT, items );
		mUndo.push( mUndoTran ) ;
		if ( bTrace ) ZDebug.trace("commit:"+mUndoTran.size()+" total:"+mUndo.size());
		mRedo.removeAllElements();
		items = getFobs( mUndoTran ) ;
		firePropertyChange( FPropertyChangeEvent.COMMIT, items );
		mIsModified = true;
	} else {
		if ( bTrace ) ZDebug.trace("commit:empty");
	}
	mUndoTran = null ;
	}

	/**
	 * 追加コミット
	 */
	public void commitPlus() {
	if ( mUndoTran == null ) {
		if ( bTrace ) ZDebug.trace("commit plus:none");
	} else if ( !mUndoTran.isEmpty() ) {
		if ( mUndo.isEmpty() ) {
			if ( bTrace ) ZDebug.trace("commit plus -> commit");
			commit();
		} else {
			Vector items = getFobs( mUndoTran ) ;
			firePropertyChange( FPropertyChangeEvent.PRE_COMMIT, items );
			Stack oldTran = (Stack)mUndo.peek();
			oldTran.addAll( mUndoTran );
			if ( bTrace ) ZDebug.trace("commit plus:"+mUndoTran.size()+" total:"+mUndo.size()+" transize:"+oldTran.size());
//			ZDebug.printStack();
			mRedo.removeAllElements();
			items = getFobs( mUndoTran ) ;
			firePropertyChange( FPropertyChangeEvent.COMMIT, items );
			mIsModified = true;
		}
	} else {
		if ( bTrace ) ZDebug.trace("commit plus:empty");
	}
	mUndoTran = null ;
	}

	/**
	 * ロールバック
	 */
	public void rollback() {
	if ( mUndoTran == null ) {
		if ( bTrace ) ZDebug.trace("rollback:none");
		return ;
	}
//	Vector items = getFobs( mUndoTran ) ;
//	firePropertyChange( FPropertyChangeEvent.PRE_ROLLBACK, items );
	while ( !mUndoTran.isEmpty() ) {
		IFUndoElement item = (IFUndoElement)mUndoTran.pop();
		item.undo();
	}
//	items = getFobs( mUndoTran ) ;
//	firePropertyChange( FPropertyChangeEvent.ROLLBACK, items );
	if ( bTrace ) ZDebug.trace("rollback");
	mUndoTran = null ;
	}

	/**
	 * アンドゥアイテムの追加
	 */
	public void push( IFUndoElement item ) {
	if ( mUndoTran != null ) {
		mUndoTran.push( item );
	}
	}

	/**
	 * アンドゥの可否
	 */
	public boolean canUndo() {
//	ZDebug.trace("this:"+this+" size:"+mUndo.size());
	return( !mUndo.isEmpty() );
	}

	/**
	 * アンドゥの実行
	 */
	public void undo() {
	if ( bTrace ) ZDebug.trace("undo size:"+mUndo.size()+","+mRedo.size());
	if (canUndo()) {
		Vector items = new Vector();
		Stack undo = (Stack)mUndo.pop();
		Stack redo = new Stack();
		while ( !undo.isEmpty() ) {
			IFUndoElement item = (IFUndoElement)undo.pop();
			item.undo();
			item.getFobs(items);
			redo.push(item);
		}
		mRedo.push(redo);
		firePropertyChange( FPropertyChangeEvent.UNDO, items );
	}
	if ( bTrace ) ZDebug.trace("         :"+mUndo.size()+","+mRedo.size());
	}

	/**
	 * リドゥの可否
	 */
	public boolean canRedo() {
	return( !mRedo.isEmpty() );
	}

	/**
	 * リドゥの実行
	 */
	public void redo() {
	if ( bTrace ) ZDebug.trace("undo size:"+mUndo.size()+","+mRedo.size());
	if (canRedo()) {
		Vector items = new Vector();
		Stack redo = (Stack)mRedo.pop();
		Stack undo = new Stack();
		while ( !redo.isEmpty() ) {
			IFUndoElement item = (IFUndoElement)redo.pop();
			item.redo();
			item.getFobs(items);
			undo.push(item);
		}
		mUndo.push(undo);
		firePropertyChange( FPropertyChangeEvent.REDO, items );
	}
	if ( bTrace ) ZDebug.trace("         :"+mUndo.size()+","+mRedo.size());
	}

	/**
	 * Fobの収集
	 * @param 収集するundoグループ
	 */
	public Vector getFobs(Stack st) {
	Vector items = new Vector();
	Enumeration elm = st.elements();
	while( elm.hasMoreElements() ) {
		IFUndoElement item = (IFUndoElement)elm.nextElement();
		item.getFobs(items);
	}
	return items;
	}

	/**
	 * リスナー登録
	 * @param listerner リスナー
	 */
	public void addPropertyChangeListener(
		IFPropertyChangeListener listerner) {
	mListener.add( listerner );
	}

	/**
	 * リスナー削除
	 * @param listerner リスナー
	 */
	public void removePropertyChangeListener(
		IFPropertyChangeListener listerner) {
	mListener.remove( listerner );
	}

	/**
	 * イベント発生
	 * @param type  イベントタイプ
	 * @param items イベントオブジェクト
	 */
	public void firePropertyChange(String type, Vector items) {
	FPropertyChangeEvent evt = new FPropertyChangeEvent( type, items);

	// リスナーに通知
	Enumeration elm = mListener.elements() ;
	while ( elm.hasMoreElements() ) {
		IFPropertyChangeListener listerner
			= (IFPropertyChangeListener)elm.nextElement();
		listerner.propertyChange( evt );
	}
	}

	//## アトリビュート################################################
	/**
	 * 現在のトランザクションで変更が発生したElementを格納したStack
	 */
	protected Stack mUndoTran = null ;

	/**
	 * undo待ちElementを格納したStackのStack
	 */
	protected Stack mUndo ;

	/**
	 * redo待ちElementを格納したStackのStack
	 */
	protected Stack mRedo ;

	/**
	 * プロパティ変更リスナーのVector
	 */
	protected Vector mListener = new Vector();

	/**
	 * 更新されたか？
	 */
	protected boolean mIsModified = false;
}
