
package jp.riken.brain.ni.samuraigraph.base;

import java.util.ArrayList;

/**
 * A class to control undo/redo operation.
 *
 */
public class SGUndoManager
{

	// an undoable object
	private SGIUndoable mUndoable = null;
	

	// a list of the mement
	private ArrayList mMementList = new ArrayList();


	//
	private int mElementStateCounter = 0;


	/**
	 * 現在、状態が履歴のどの位置にあるのかを示すカウンタ
	 */
	private int mCurrentStateCounter = 0;


	// A list of undoable objects.
	// This list can hold the undoable object in the attributes
	// and its undoable child objects.
	private ArrayList mUndoableObjectHistoryList = new ArrayList();



	/**
	 * Create this object with an undoable object.
	 * @param obj - an undoable object
	 */
	public SGUndoManager( SGIUndoable obj )
	{
		super();
		this.mUndoable = obj;
	}


	/**
	 * 
	 */
	public boolean initPropertiesHistory()
	{
		this.addPropertyHistory( this.mUndoable.getMement() );
		return true;
	}


	/**
	 * Undo the undoable object.
	 */
	public boolean undo()
	{
		this.mElementStateCounter--;
		return this.updateMement();
	}


	/**
	 * Redo the undoable object.
	 */
	public boolean redo()
	{
		this.mElementStateCounter++;
		return this.updateMement();
	}

	//
	private boolean updateMement()
	{
		SGProperties p
			= (SGProperties)this.mMementList.get( this.mElementStateCounter );
		return this.mUndoable.setMement(p);
	}



	/**
	 * アンドゥ操作を依頼する
	 */
	public boolean onUndo()
	{
		if( this.isUndoable() == false )
		{
			return false;
		}

		ArrayList objList = (ArrayList)this.mUndoableObjectHistoryList.get(
			this.mCurrentStateCounter - 1 );

		for( int ii=0; ii<objList.size(); ii++ )
		{
			SGIUndoable obj = (SGIUndoable)objList.get(ii);
			// アンドゥ操作の実行の依頼
			boolean flag;
			if( obj.equals(this.mUndoable) )
			{
				flag = this.mUndoable.undo();
			}
			else
			{
				flag = obj.onUndo();
			}
		
			if( !flag )
			{
				throw new Error("undo erorr:"+obj);
			}
		}

		// decrement
		this.mCurrentStateCounter--;

		return true;
	}

	
	
	/**
	 * リドゥ操作を依頼する
	 */
	public boolean onRedo()
	{
		if( this.isRedoable() == false )
		{
			return false;
		}

		ArrayList objList = (ArrayList)this.mUndoableObjectHistoryList.get(
			this.mCurrentStateCounter );
		for( int ii=0; ii<objList.size(); ii++ )
		{
			SGIUndoable obj = (SGIUndoable)objList.get(ii);
			// リドゥ操作の実行の依頼
			boolean flag;
			if( obj.equals( this.mUndoable ) )
			{
				flag = this.mUndoable.redo();
			}
			else
			{
				flag = obj.onRedo();
			}

			if( !flag )
			{
				throw new Error("redo erorr:"+obj);
			}
		}

		// increment
		this.mCurrentStateCounter++;

		return true;
	}



	/**
	 * オブジェクトの履歴を更新する
	 * @return
	 */
	public boolean updateObjectHistory( final SGIUndoable obj )
	{
		ArrayList objList = new ArrayList();
		objList.add(obj);
		boolean flag = this.updateObjectHistory(objList);
		if( !flag )
		{
			return false;
		}

		return true;
	}



	/**
	 * オブジェクトの履歴を更新する
	 */
	public boolean updateObjectHistory( final ArrayList objList )
	{
		final int len = this.mCurrentStateCounter;
		ArrayList hList = this.mUndoableObjectHistoryList;

		ArrayList list = new ArrayList();
		for( int ii=0; ii<len; ii++ )
		{
			Object obj = hList.get(ii);
			list.add(obj);
		}
		list.add( new ArrayList(objList) );

		this.mUndoableObjectHistoryList = list;
		this.mCurrentStateCounter++;

		return true;
	}



	/**
	 * 履歴の更新の実行。アンドゥ対象の操作が行われたときに、真っ先に呼ばれる。
	 */
	public boolean updateHistory()
	{
		if( this.mUndoable.isChanged() )
		{
			this.updateThisObjectHistory();
			this.mUndoable.setChanged(false);
			this.updateObjectHistory(this.mUndoable);
		}
		return true;
	}


	/**
	 * 
	 */
	public boolean updateThisObjectHistory()
	{
		this.mElementStateCounter++;
		this.addPropertyHistory( this.mUndoable.getMement() );

		return true;
	}


	/**
	 * 
	 * @return
	 */
	private boolean addPropertyHistory( SGProperties p )
	{
		final int len = this.mElementStateCounter;
		ArrayList hList = this.mMementList;

		ArrayList list = new ArrayList();
		for( int ii=0; ii<len; ii++ )
		{
			list.add( hList.get(ii) );
		}
		list.add(p);

		this.mMementList = list;

		return true;
	}


	/**
	 * Return whether this object can undo.
	 * @return
	 */
	public boolean isUndoable()
	{
		return (this.mCurrentStateCounter!=0);
	}


	/**
	 * Return whether this object can redo.
	 * @return
	 */
	public boolean isRedoable()
	{
		return ( this.mCurrentStateCounter!=this.mUndoableObjectHistoryList.size() );
	}


	/**
	 * 
	 * @return
	 */
	public SGIUndoable getUndoable()
	{
		return this.mUndoable;
	}


	public void dump()
	{
		System.out.println(
			this.mElementStateCounter+"  "
			+this.mCurrentStateCounter+"  "
			+this.mMementList.size()+"  "
			+this.mUndoableObjectHistoryList.size());
	}

}

