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


		MCIterator.h


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


		Author:	Ian
		Date:	6/3/99

		This file contains the class declaration for TMCIterator.

		Revision History

		when		who			what & why
		==========	==========	======================================================

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

#ifndef __MCIterator__
#define __MCIterator__

#if CP_PRAGMA_ONCE
#pragma once
#endif

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

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

	
	// 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.
	TMCIterator& 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.
	TMCIterator operator++(int)
    {
        TMCIterator 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.
	TMCIterator& 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.
	TMCIterator operator--(int)
    {
        TMCIterator tmp = *this;
        --*this;
        return tmp;
    }
	
	// operator== : Equality operator.
	boolean operator==(const TMCIterator& 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 TMCIterator& rhs) const {return !(*this == rhs);}
	
	// operator= : Assignment operator.
	TMCIterator& operator=(const TMCIterator& 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
	void Reset(void)
	{
		*this = fMyArray.Begin();
	} 

	T* First(void)
	{
		fCurrentIndex = 0;
		return fCurrent = (T*)fMyArray.ArrayData();
	}

	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;
		return ++fCurrent;
	}
	boolean More(void)
	{
		return (fCurrentIndex < fMyArray.GetElemCount());
	}
	T* Last(void)
	{
		fCurrentIndex = fMyArray.GetElemCount() - 1;
		if(fCurrentIndex == uint32(-1))
		{
			fCurrent = NULL;
		}
		else
		{
			fCurrent = fMyArray.ArrayData() + fCurrentIndex;
		}
		return fCurrent;
	}

	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 (fCurrent = NULL);
		}
		else
		{
			return --fCurrent;
		}
	}

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

	uint32 GetIndex(void) const
	{
		return fCurrentIndex;
	}

	T* GetCurrent(void)
	{
		MCAssertPointerCheck(fCurrent);
		return fCurrent;
	}

	const T* GetCurrent(void) const
	{
		MCAssertPointerCheck(fCurrent);
		return fCurrent;
	}

//friend class ArrayT;

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

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

	// operator*: Dereference operator, returns the array element at the current index.
	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.
	TMCConstIterator& 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.
	TMCConstIterator operator++ (int)
    {
        TMCConstIterator 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.
	TMCConstIterator&	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.
	TMCConstIterator operator--(int)
    {
        TMCConstIterator tmp = *this;
        --*this;
        return tmp;
    }
	
	// operator== : Equality operator.
	boolean operator==(const TMCConstIterator& 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 TMCConstIterator& rhs) const {return !(*this == rhs);}
	
	// operator= : Assignment operator.
	TMCConstIterator& operator=(const TMCConstIterator& 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

	void Reset(void)
	{
		*this = fMyArray.Begin();
	} 

	const T* First(void)
	{
		fCurrentIndex = 0;
		return fCurrent = (T*)fMyArray.ArrayData();
	}

	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;
		return ++fCurrent;
	}

	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 (fCurrent = NULL);
		}
		else
		{
			return --fCurrent;
		}
	}

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

	uint32 GetIndex(void) const
	{
		return fCurrentIndex;
	}

	const T* GetCurrent(void) const
	{
		MCAssertPointerCheck(fCurrent);
		return fCurrent;
	}

//friend class ArrayT;

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

#endif