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


		MCPtrIterator.h


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


		Author:	Ian
		Date:	6/3/99

		This file contains the class declaration for TMCPtrIterator.

		Revision History

		when		who			what & why
		==========	==========	======================================================
		06/26/99	del			Next() was dereferencing address outside range.

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

#ifndef __MCPtrIterator__
#define __MCPtrIterator__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCArrayTypes.h"
#include "MCAssert.h"
#include "MCBasicTypes.h"
#include "MCBasicDefines.h"

template <class T,class ArrayT> class TMCPtrIterator : public TMCObject
{
public:
	// TMCPtrIterator(): Constructor.
	TMCPtrIterator (ArrayT& inArray, uint32 startIndex = 0) :
		fMyArray(inArray)
	{
		fCurrentIndex = startIndex;
	}

	TMCPtrIterator (const ArrayT& inArray, uint32 startIndex = 0) :
		fMyArray(const_cast<ArrayT&> (inArray))
	{
		fCurrentIndex = startIndex;
	}

	// operator*: Dereference operator, returns the array element at the current index.
	T*&	operator* (void)
	{
		MCAssertIfTrue(fCurrentIndex == kEndIndex);
		return (T*&)fMyArray.ArrayData()[fCurrentIndex];
	}

	const T*& operator* (void)const
	{
		MCAssertIfTrue(fCurrentIndex == kEndIndex);
		return fMyArray.ArrayData()[fCurrentIndex];
	}

	// ++operator: Pre-increment operator advances to then next element in the array.
	//	Note: Use this if you have a choice between pre and post increment, it is cheaper.
	TMCPtrIterator& operator++ (void)
	{
		fCurrentIndex++;
		if(fCurrentIndex >=fMyArray.GetElemCount())
		{
			fCurrentIndex = kEndIndex;
		}
		return *this;
	}
    
	// operator++: Post-increment operator advances to then next element in the array.
	//	Note: Use pre-increment if you have a choice it is cheaper.
	TMCPtrIterator operator++ (int)
    {
        TMCPtrIterator tmp = *this;
        ++*this;
        return tmp;
    }
	

	// --operator: Pre-decrement operator retreats to the previous element of the array
	//	Note: Use this if you have a choice between pre and post decrement, it is cheaper.
	TMCPtrIterator&	operator-- (void)
	{
		if(fCurrentIndex == kEndIndex)
		{
			fCurrentIndex = fMyArray.GetElemCount() - 1;
		}
		else
		{
			MCAssertIfFalse(fCurrentIndex < fMyArray.GetElemCount());
			fCurrentIndex--;
		}
		return *this;
	}
    
	// operator--: Post-decrement operator retreats to the previous element of the array
	//	Note: Use pre-decrement if you have a choice it is cheaper.
	TMCPtrIterator operator-- (int)
    {
        TMCPtrIterator tmp = *this;
        --*this;
        return tmp;
    }
	
	// operator== : Equality operator.
	boolean operator== (const TMCPtrIterator& rhs) const
	{
		boolean result = (&fMyArray == &rhs.fMyArray);

		//You're comparing iterators from different arrays you maniac!!
		MCAssertIfFalse(result);

		if(result)
		{
			MCAssertIfFalse(fMyArray.GetElemCount() == 0 ||
				((rhs.fCurrentIndex == kEndIndex || rhs.fCurrentIndex < fMyArray.GetElemCount()) &&
				 (fCurrentIndex == kEndIndex || fCurrentIndex < fMyArray.GetElemCount())));
			if(fMyArray.GetElemCount() == 0)
			{
				result = true;
			}
			else if((rhs.fCurrentIndex != kEndIndex) || (fCurrentIndex != kEndIndex))
			{
				result = (fCurrentIndex == rhs.fCurrentIndex);
			}
			else
			{
				
			}
		}
		return result;
	}

	// operator!= : Inequality operator.
	boolean operator!= (const TMCPtrIterator& rhs) const
	{
		return !(*this == rhs);
	}
	
	// operator= : Assignment operator.
	TMCPtrIterator& operator= (const TMCPtrIterator& rhs)
	{
		//Can't assign iterators from different arrays
		//We've got a reference to the array we constructed from,
		//and you can't change that.
		MCAssert(&fMyArray == &rhs.fMyArray);
		fCurrentIndex = rhs.fCurrentIndex;
		return *this;
	}

	// -------RD Like Iteration
	//Return the begining of the array.
	void Reset (void)
	{
		*this = fMyArray.Begin();
	} 

	//Return a pointer the first element of the array, returns NULL if the array is empty.
	T* First (void)
	{
		fCurrentIndex = 0;
		if(fCurrentIndex<fMyArray.GetElemCount())
		{
			return fMyArray[fCurrentIndex];
		}
		else
		{
			return NULL;
		}
	}

	//Return a pointer the next element in the array, returns NULL if the array is empty.
	T* Next (void)
	{
#if Debug_
		if(fCurrentIndex == fMyArray.GetElemCount())
		{
			MCNotify("Went past the end of the Array!!!");
			return NULL;
		}
#endif
		fCurrentIndex++;

		if(fCurrentIndex<fMyArray.GetElemCount())
		{
			return fMyArray[fCurrentIndex];
		}
		else
		{
			return NULL;
		}
	}

	boolean More (void)
	{
		return (fCurrentIndex < fMyArray.GetElemCount());
	}

	T* Last (void)
	{
		fCurrentIndex = fMyArray.GetElemCount() - 1;
		if(fCurrentIndex == (uint32)-1)
		{
			return nil;
		}
		return fMyArray[fCurrentIndex];
	}

	T* Prev (void)
	{
#if Debug_
		if(fCurrentIndex == (uint32)-1)
		{
			MCNotify("Went past the beginning of the Array!!!");
			return NULL;
		}
#endif
		fCurrentIndex--;
		if(fCurrentIndex == (uint32)-1)
		{
			return NULL;
		}
		else
		{
			return fMyArray[fCurrentIndex];
		}
	}

	boolean MorePrev (void)
	{
		return (fCurrentIndex != (uint32)-1);
	}

	uint32 GetIndex (void) const
	{
		return fCurrentIndex;
	}

	T* GetCurrent (void)
	{
		if( this->fCurrent)
		{
			return *(this->fCurrent);
		}
		else
		{
			return NULL;
		}
	}

	const T* GetCurrent (void) const
	{
		return fMyArray[fCurrentIndex];
	}

//friend class ArrayT;

protected:
	ArrayT&		fMyArray;
	uint32		fCurrentIndex;
};

template <class T,class ArrayT> class TMCConstPtrIterator : public TMCObject
{
public:
	// TMCPtrIterator(): Constructor.
	TMCConstPtrIterator (const ArrayT& inArray, uint32 startIndex = 0) :
		fMyArray(inArray)
	{
		fCurrentIndex = startIndex;
		fCurrent = NULL;
	}

	const T* operator* (void)const
	{
		MCAssertIfTrue(fCurrentIndex == kEndIndex);
				
		return fMyArray.ArrayData()[fCurrentIndex];
	}

	// ++operator: Pre-increment operator advances to then next element in the array.
	//	Note: Use this if you have a choice between pre and post increment, it is cheaper.
	TMCConstPtrIterator& operator++ (void)
	{
		fCurrentIndex++;
		if(fCurrentIndex < fMyArray.GetElemCount())
		{
			fCurrent++;
		}
		else
		{
			fCurrentIndex = kEndIndex;
		}
		return *this;
	}
    
	// operator++: Post-increment operator advances to then next element in the array.
	//	Note: Use pre-increment if you have a choice it is cheaper.
	TMCConstPtrIterator operator++ (int)
    {
        TMCConstPtrIterator tmp = *this;
        ++*this;
        return tmp;
    }
	

	// --operator: Pre-decrement operator retreats to the previous element of the array
	//	Note: Use this if you have a choice between pre and post decrement, it is cheaper.
	TMCConstPtrIterator& operator-- (void)
	{
		if(fCurrentIndex == kEndIndex)
		{
			fCurrentIndex = fMyArray.GetElemCount() - 1;
			fCurrent = fMyArray.Pointer(fCurrentIndex);
		}
		else
		{
			MCAssertIfFalse(fCurrentIndex < fMyArray.GetElemCount());
			MCAssertPointerCheck(fCurrent);
			fCurrentIndex--;
			fCurrent--;
		}
		return *this;
	}
    
	// operator--: Post-decrement operator retreats to the previous element of the array
	//	Note: Use pre-decrement if you have a choice it is cheaper.
	TMCConstPtrIterator operator-- (int)
    {
        TMCConstPtrIterator tmp = *this;
        --*this;
        return tmp;
    }
	
	// operator== : Equality operator.
	boolean operator== (const TMCConstPtrIterator& rhs) const
	{
		boolean result = (&fMyArray == &rhs.fMyArray);

		//You're comparing iterators from different arrays you maniac!!
		MCAssertIfFalse(result);

		if(result)
		{
			MCAssertIfFalse(fMyArray.GetElemCount() == 0 ||
				((rhs.fCurrentIndex == kEndIndex || rhs.fCurrentIndex < fMyArray.GetElemCount()) &&
				 (fCurrentIndex == kEndIndex || fCurrentIndex < fMyArray.GetElemCount())));
			if(fMyArray.GetElemCount() == 0)
			{
				result = true;
			}
			else if((rhs.fCurrentIndex != kEndIndex) || (fCurrentIndex != kEndIndex))
			{
				result = (fCurrentIndex == rhs.fCurrentIndex);
			}
			else
			{
				
			}
		}
		return result;
	}

	// operator!= : Inequality operator.
	boolean operator!= (const TMCConstPtrIterator& rhs) const
	{
		return !(*this == rhs);
	}
	
	// operator= : Assignment operator.
	TMCConstPtrIterator& operator= (const TMCConstPtrIterator& rhs)
	{
		//Can't assign iterators from different arrays
		//We've got a reference to the array we constructed from,
		//and you can't change that.
		MCAssert(&fMyArray == &rhs.fMyArray);
		fCurrent = rhs.fCurrent;
		fCurrentIndex = rhs.fCurrentIndex;
		return *this;
	}

	// -------RD Like Iteration
	//Return the begining of the array.
	void Reset (void)
	{
		*this = fMyArray.Begin();
	} 

	//Return a pointer the first element of the array, returns NULL if the array is empty.
	const T* First (void)
	{
		fCurrentIndex = 0;
		if(fMyArray.ArrayData() != NULL)
		{
			return *(fCurrent = (const T**)fMyArray.ArrayData());
		}
		else
		{
			return NULL;
		}
	}

	//Return a pointer the next element in the array, returns NULL if the array is empty.
	const T* Next (void)
	{
#if Debug_
		if(fCurrent == NULL)
		{
			MCNotify("fCurrent == NULL, did you forget to call/check More()?");
			return NULL;
		}

		if(fCurrentIndex == fMyArray.GetElemCount())
		{
			MCNotify("Went past the end of the Array!!!");
			return NULL;
		}
#endif
		fCurrentIndex++;
		if(fCurrentIndex<fMyArray.GetElemCount())
		{
			return *(++fCurrent);
		}
		else
		{
			return NULL;
		}
	}

	boolean More (void)
	{
		return (fCurrentIndex < fMyArray.GetElemCount());
	}

	const T* Last (void)
	{
		fCurrentIndex = fMyArray.GetElemCount() - 1;
		if(fCurrentIndex == uint32(-1))
			fCurrent = NULL;
		else
			fCurrent = fMyArray.ArrayData() + fCurrentIndex;
		return *fCurrent;
	}

	const T* Prev (void)
	{
#if Debug_
		if(fCurrent == NULL)
		{
			MCNotify("fCurrent == NULL, did you forget to call/check MorePrev()?");
			return NULL;
		}

		if(fCurrentIndex == (uint32)-1)
		{
			MCNotify("Went past the beginning of the Array!!!");
			return NULL;
		}
#endif
		fCurrentIndex--;
		if(fCurrentIndex == (uint32)-1)
		{
			return ((T*)(fCurrent = NULL));
		}
		else
		{
			return *(--fCurrent);
		}
	}

	boolean MorePrev (void)
	{
		return (fCurrentIndex != (uint32)-1);
	}

	uint32 GetIndex (void) const
	{
		return fCurrentIndex;
	}

	const T* GetCurrent (void)
	{
		if(fCurrent)
		{
			return *fCurrent;
		}
		else
		{
			return NULL;
		}
	}

//friend class ArrayT;

protected:
	const ArrayT&	fMyArray;
	const T**		fCurrent;
		  uint32	fCurrentIndex;
};

#endif