/****************************************************************************************************

		MCUndoStack.h
		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Julien
		Date:	4/16/2003

****************************************************************************************************/

#ifndef __MCUndoStack__
#define __MCUndoStack__

#if CP_PRAGMA_ONCE
#pragma once
#endif

/// \addtogroup MCCore
/// \{

/// TMCUndoStack store data in a stack and can return a pointer to previous states using undo/redo. 
/// It can be use for a simple auxiliary mecanism of undo/redo 
///
template <class T> class TMCUndoStack
{
public:
	// ****************************************************************************
	//								TMCUndoStack
	// 
	/// Prepare a new TMCUndoStack. maxSize is fixed to 16 elem
	///
	TMCUndoStack();

	// ****************************************************************************
	//								TMCUndoStack
	// 
	/// Prepare a new TMCUndoStack
	/// 
	/// \param maxSize  maximum number of element in the undo stack
	///
	TMCUndoStack( int32 maxSize );

	// ****************************************************************************
	//								Do
	// 
	/// Creates and adds a new param T to the stack and returns a pointer to it
	///
	inline T* Do();

	// ****************************************************************************
	//								DoMore
	// 
	/// Return a pointer to the current state to allow modification on it
	///	
	inline T* DoMore();

	// ****************************************************************************
	//								Undo
	// 
	/// Access to the data of the previous state.
	/// \return a pointer to the previous state. NULL if none exist.
	///
	inline T* Undo();

	// ****************************************************************************
	//								Redo
	// 
	/// Access to the data of the next state.
	/// \return	a pointer to the next state. NULL if none exist.
	///
	inline T* Redo();

	// ****************************************************************************
	//								SetMaxSize
	//
	/// Sets the maximum number of element in the undo stack
	///
	inline void SetMaxSize( int32 maxSize );

	// ****************************************************************************
	//								GetElemCount
	// 
	/// Gets the number of elements allocated.
	///
	inline uint32 GetElemCount() const;

protected:
	int32 fMaxSize;
	int32 fCurrent;
	int32 fLast;
	TMCPtrArray<T> fArrayData;
};

/// \}


template<class T> TMCUndoStack<T>::TMCUndoStack()
{
	fCurrent = 0;
	fLast = 0;
	fMaxSize = 16;
	fArrayData.SetElemSpace(16);
}

template<class T> TMCUndoStack<T>::TMCUndoStack( int32 maxSize )
{
	fCurrent = 0;
	fLast = 0;
	fMaxSize = maxSize;
	fArrayData.SetElemSpace(maxSize);
}

template<class T> T* TMCUndoStack<T>::Do()
{
	if(fCurrent != 0)
	{
		// remove the existing elements between fCurrent and 0
		fArrayData.RemoveElem(0, fCurrent);
		fLast-=fCurrent;
		fCurrent = 0;
	}
	if(fLast > fMaxSize-1) // done by shailesh on 6/27/07 for bug id 16190
	{
		// Remove the last element before adding the new one if oversized
		fLast--;
		fArrayData.RemoveElem(fLast, 1);
	}

	T* newElem = new T();
	fArrayData.InsertElem(0, newElem);
	fLast++;
	MCAssert(fLast<=fMaxSize);
	return newElem;
}

template<class T> T* TMCUndoStack<T>::DoMore()
{
	return fArrayData[fCurrent];
}

template<class T> T* TMCUndoStack<T>::Undo()
{
	if((fCurrent+1)<fLast)
	{
		fCurrent++;
		return fArrayData[fCurrent];
	}
	else
		return NULL;
}

template<class T> T* TMCUndoStack<T>::Redo()
{
	if(fCurrent>0)
	{
		fCurrent--;
		return fArrayData[fCurrent];
	}
	else
		return NULL;
}

template<class T> void TMCUndoStack<T>::SetMaxSize( int32 maxSize )
{
	if( fMaxSize != maxSize )
	{
		fMaxSize = maxSize;
		fArrayData.SetElemSpace(maxSize);
	}
}

template<class T> uint32 TMCUndoStack<T>::GetElemCount() const 
{
	return fArrayData.GetElemCount();
}


#endif
