using System;
using System.Collections;


namespace DarkStrideToolbox
{
	//This class is a dynmaic array of objects.  Why use this instead of a Sorted Array or such?  Because I want to return
	//null's instead of errors and wanted to expand the functionality of the standard collections.
	public class DSObjectArray: IEnumerable
	{
		#region Properties
		private long m_nMaxWidth = 1;
		private long m_nMaxHeight = 10;
		private long m_nNextToUseParam = 0;
		private bool m_bElementsHaveBeenRemoved = false;

		private object[,] m_oaParamaters = null;
		private bool[] m_baInUse = null;
		#endregion


		public DSObjectArray()
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.ObjectArray";

			try
			{
				m_oaParamaters = new object[ m_nMaxHeight,m_nMaxWidth ];
				m_baInUse = new bool[ m_nMaxHeight ];
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public DSObjectArray( long nWidth )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.ObjectArray";

			try
			{
				m_nMaxWidth = nWidth;
				m_oaParamaters = new object[ m_nMaxHeight,m_nMaxWidth ];
				m_baInUse = new bool[ m_nMaxHeight ];
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		//These functions are neccessary for the For Each functionality to work.
		#region IEnumerable Interface
		// IEnumerable Interface Implementation: Declaration of the GetEnumerator() method required by IEnumerable
		public IEnumerator GetEnumerator()
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.GetEnumerator";
			IEnumerator oRetVal = null;

			try
			{
				oRetVal = new ArrayEnumerator( this );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oRetVal );
		}

		// Inner class implements IEnumerator interface:
		private class ArrayEnumerator : IEnumerator
		{
			#region Properties
			private int m_nCurEnumeratorPosition = -1;
			private DSObjectArray m_oArrayObject = null;
			#endregion

			public ArrayEnumerator( DSObjectArray oArray )
			{
				const string sRoutineName = "DarkStrideToolbox.ObjectArray.ArrayEnumerator";

				try
				{
					m_oArrayObject = oArray;
				}
				catch( System.Exception oEx )
				{
					throw new System.Exception( sRoutineName + " Failed.",oEx );
				}
			}


			//Declare the MoveNext method required by IEnumerator:
			public bool MoveNext()
			{
				const string sRoutineName = "DarkStrideToolbox.ObjectArray.MoveNext";
				bool bRetVal = false;

				try
				{
					if( m_nCurEnumeratorPosition < m_oArrayObject.Count -1 )
					{
						m_nCurEnumeratorPosition++;
						bRetVal = true;
					}
					else
					{
						bRetVal = false;
					}
				}
				catch( System.Exception oEx )
				{
					throw new System.Exception( sRoutineName + " Failed.",oEx );
				}
				return( bRetVal );
			}

			//Declare the Reset method required by IEnumerator:
			public void Reset()
			{
				const string sRoutineName = "DarkStrideToolbox.ObjectArray.Reset";

				try
				{
					m_nCurEnumeratorPosition = -1;
				}
				catch( System.Exception oEx )
				{
					throw new System.Exception( sRoutineName + " Failed.",oEx );
				}
			}

			//Declare the Current property required by IEnumerator:
			public object Current
			{
				get
				{
					const string sRoutineName = "DarkStrideToolbox.ObjectArray.Current";
					object oRetVal = null;

					try
					{
						oRetVal = m_oArrayObject.Get( m_nCurEnumeratorPosition );
					}
					catch( System.Exception oEx )
					{
						throw new System.Exception( sRoutineName + " Failed.",oEx );
					}
					return( oRetVal );
				}
			}
		}
		#endregion

		//Gets an element based upon the index.
		public object Get( long nIndex )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Get";
			object oRetVal = null;

			try
			{
				oRetVal = Get( nIndex,0 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oRetVal );
		}

		public object Get( long nYIndex,long nXIndex )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Get";
			object oRetVal = null;

			try
			{
				if( nYIndex<0 || nYIndex>=m_nNextToUseParam || nXIndex<0 || nXIndex>=m_nMaxWidth )
				{
					throw new IndexOutOfRangeException("Index passed in (" + nYIndex + "," + nXIndex + ") is less than 0 or greater than the highest existing paramater (" + m_nNextToUseParam + "," + m_nMaxWidth + ")" );
				}
				else if( m_baInUse[ nYIndex ] == false )
				{
					throw new IndexOutOfRangeException("Index passed in (" + nYIndex + ") is referencing an element that has been removed" );
				}
				else
				{
					oRetVal = m_oaParamaters[ nYIndex,nXIndex ];
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oRetVal );
		}


		//Sets an element based upon the index.
		public void Set( long nIndex,object sValue )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Set";

			try
			{
				Set( nIndex,0,sValue );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public void Set( long nYIndex,long nXIndex,object sValue )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Set";

			try
			{
				if( nYIndex<0 || nYIndex>=m_nNextToUseParam )
				{
					throw new IndexOutOfRangeException("Index passed in (" + nYIndex + "," + nXIndex + ") is less than 0 or greater than the highest existing paramater (" + m_nNextToUseParam + "," + m_nMaxWidth + ")" );
				}
				else
				{
					AddFixed( nYIndex,nXIndex,sValue );
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		//Add some paramaters to the array.  The multiple paramaters are in case you want to add multi dimensions.
		public long Add( object sKey )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Add";
			long i = 0;
			long nRetVal = -1;

			try
			{
				if( m_bElementsHaveBeenRemoved == true )
				{
					for( i=0 ; i<m_nNextToUseParam ; i++ )
					{
						if( m_baInUse[ i ] == false )
						{
							m_baInUse[ i ] = true;
							m_oaParamaters[ i,0 ] = sKey;
							nRetVal = i;
							break;
						}
					}

					if( nRetVal == -1 )
					{
						nRetVal = m_nNextToUseParam;
						m_bElementsHaveBeenRemoved = false;
						AddFixed( m_nNextToUseParam,0,sKey );
					}
				}
				else
				{
					nRetVal = m_nNextToUseParam;
					AddFixed( m_nNextToUseParam,0,sKey );
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}

		public long Add( object sKey,object sParam1 )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Add";
			long nRetVal = -1;

			try
			{
				nRetVal = Add( sKey );
				Set( nRetVal,1,sParam1 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}

		public long Add( object sKey,object sParam1,object sParam2 )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Add";
			long nRetVal = -1;

			try
			{
				nRetVal = Add( sKey,sParam1 );
				Set( nRetVal,2,sParam2 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}

		public long Add( object sKey,object sParam1,object sParam2,object sParam3 )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Add";
			long nRetVal = -1;

			try
			{
				nRetVal = Add( sKey,sParam1,sParam2 );
				Set( nRetVal,3,sParam3 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}

		public long Add( object sKey,object sParam1,object sParam2,object sParam3,object sParam4 )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Add";
			long nRetVal = -1;

			try
			{
				nRetVal = Add( sKey,sParam1,sParam2,sParam3 );
				Set( nRetVal,4,sParam4 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}

		public long Add( object sKey,object sParam1,object sParam2,object sParam3,object sParam4,object sParam5 )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Add";
			long nRetVal = -1;

			try
			{
				nRetVal = Add( sKey,sParam1,sParam2,sParam3,sParam4 );
				Set( nRetVal,5,sParam5 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}


		//Returns the object for the key passed in.  This is versus the get functions which return by index.
		public object Find( object sKey,long nColToMatchTo )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Find";
			object oRetVal = null;

			try
			{
				for( int i=0 ; i<m_nNextToUseParam ; i++ )
				{
					if( m_baInUse[ i ] == true )
					{
						if( m_oaParamaters[ i,nColToMatchTo ].Equals( sKey ) == true )
						{
							oRetVal = m_oaParamaters[ i,0 ];
							break;
						}
					}
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oRetVal );
		}

		public object Find( object sKey )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.Find";
			object oRetVal = null;

			try
			{
				oRetVal = Find( sKey,0 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( oRetVal );
		}
		

		//Add an element to this exact location.  This is for internal use only.
		private void AddFixed( long nYIndex,long nXIndex,object sValue )
		{
			const string sRoutineName = "DarkStrideToolbox.ObjectArray.AddFixed";
			object[,] saTemp = null;
			bool[] baTemp = null;
			long nX = 0,nY = 0;
			long nNewMaxHeight = m_nMaxHeight;
			long nNewMaxWidth = m_nMaxWidth;

			try
			{
				//Redim array
				if( (nYIndex == m_nNextToUseParam && m_nNextToUseParam >= m_nMaxHeight) || 
					nXIndex >= m_nMaxWidth )
				{
					if( nYIndex == m_nNextToUseParam && m_nNextToUseParam >= m_nMaxHeight )
					{
						nNewMaxHeight = m_nMaxHeight *= 2;
					}
					if( nXIndex >= m_nMaxWidth )
					{
						nNewMaxWidth = nXIndex+1;
					}

					saTemp = new object[ nNewMaxHeight,nNewMaxWidth ];
					baTemp = new bool[ nNewMaxHeight ];
	
					for( nY=0 ; nY<m_nNextToUseParam ; nY++ )
					{
						for( nX=0 ; nX<m_nMaxWidth ; nX++ )
						{
							saTemp[ nY,nX ] = m_oaParamaters[ nY,nX ];
						}
						baTemp[ nY ] = m_baInUse[ nY ];
					}
					m_oaParamaters = saTemp;
					m_baInUse = baTemp;
					m_nMaxWidth = nNewMaxWidth;
					m_nMaxHeight = nNewMaxHeight;

					m_baInUse[ nYIndex ] = true;
					m_oaParamaters[ nYIndex,nXIndex ] = sValue;

					if( nYIndex == m_nNextToUseParam )
					{
						m_nNextToUseParam++;
					}
				}

				m_baInUse[ nYIndex ] = true;
				m_oaParamaters[ nYIndex,nXIndex ] = sValue;

				if( nYIndex == m_nNextToUseParam )
				{
					m_nNextToUseParam++;
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}



		#region Properties
		public bool InUse( long nIndex )
		{
			return( m_baInUse[ nIndex ] );
		}

		public long Count
		{
			get
			{
				long nRetVal = 0;
				long i = 0;

				if( m_bElementsHaveBeenRemoved == true )
				{
					for( i=0 ; i<m_nNextToUseParam ; i++ )
					{
						if( m_baInUse[ i ] == true )
						{
							nRetVal += 1;
						}
					}
				}
				else
				{
					nRetVal = m_nNextToUseParam;
				}

				return( nRetVal );
			}
		}

		public long Width
		{
			get
			{
				return( m_nMaxWidth );
			}
		}

		public long Height
		{
			get
			{
				return( m_nMaxHeight );
			}
		}

		#endregion
	}
}
