/*********************************************************************/
/*                                                                   */
/*                     AgentService Framework                        */
/*                       www.agentservice.it                         */
/*                                                                   */
/* Copyright (C) 2007 l.i.d.o. - DIST, University of Genoa, Italy    */
/* GNU Lesser General Public License                                 */
/*                                                                   */
/* This library is free software; you can redistribute it and/or     */
/* modify it under the terms of the GNU Lesser General Public        */
/* License as published by the Free Software Foundation,             */
/* version 2.1 of the License.                                       */
/*                                                                   */
/* This library is distributed in the hope that it will be useful,   */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of    */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU */
/* Lesser General Public License for more details.                   */
/*                                                                   */
/* You should have received a copy of the GNU Lesser General Public  */
/* License along with this library; if not, write to the             */
/* Free Software Foundation, Inc., 59 Temple Place - Suite 330,      */
/* Boston, MA  02111-1307, USA.                                      */
/*                                                                   */
/*********************************************************************/
using System;
using System.Collections;

using AgentService.Platform.AMS;

namespace AgentService.Agent
{
	/// <summary>
	/// Class AgentCollection. Specialized Collection of Agent objects.
	/// </summary>
	public class AgentCollection : ICollection, IEnumerator, IEnumerable
	{
		/// <summary>
		/// InitialDimension, default value of the Size of the collection.
		/// </summary>
		private readonly static int InitialDimension = 10;

		/// <summary>
		/// incFactor, defines the increment factor when the internal data structure 
		/// needs to increase in Size.
		/// </summary>
		private int incFactor = 2;

		/// <summary>
		/// Size of the collection.
		/// </summary>
		private int size = 0;

		/// <summary>
		/// Index of the current element in the collection.
		/// </summary>
		private int index = 0;

		/// <summary>
		/// Internal data structure that stores the Agent objects.
		/// </summary>
		private IAgent[] items;

		/// <summary>
		/// Constructs an empty collection with initial Size equal to
		/// AgentCollection.InitialDimension.
		/// </summary>
		public AgentCollection()
		{
			this.items = new IAgent[AgentCollection.InitialDimension];
			this.index = -1;
		}

		/// <summary>
		/// Constructs an empty collection with initial Size equal to
		/// the argument, if the argument is less than 1 the collection
		/// is constructed by using the default Size.
		/// </summary>
		/// <param name="initialDimension">initial dimension of the collection</param>
		public AgentCollection(int initialDimension)
		{
			if (initialDimension > 0)
			{
				this.items = new IAgent[initialDimension];
			}
			else
			{
				this.items = new IAgent[AgentCollection.InitialDimension];
			}
			this.index = -1;
		}

		/// <summary>
		/// Constructs an empty collection with initial Size equal to the first argument
		/// and increment factor equal to second parameter. If the supplied initial Size is less than 1 
		/// the constructor will use the default Size, while if the increment factor is less than 1 it
		/// will be ignored.
		/// </summary>
		/// <param name="initialDimension">initial Size of the collection</param>
		/// <param name="incFactor">increment factor</param>
		public AgentCollection(int initialDimension, int incFactor)
		{
			if (initialDimension > 0)
			{
				this.items = new IAgent[initialDimension];
			}
			else
			{
				this.items = new IAgent[AgentCollection.InitialDimension];
			}

			if (incFactor > 0)
			{
				this.incFactor = incFactor;
			}
			this.index = -1;
		}


		#region ICollection Methods
		/// <summary>
		/// Gets the number of the elements actually stored in the collection.
		/// </summary>
		public int Count 
		{ 
			get 
			{ 
				return size; 
			} 
		}

		/// <summary>
		/// Returns true if the collection is synchronized.
		/// </summary>
		public bool IsSynchronized 
		{ 
			get 
			{ 
				return false; 
			} 
		}

		/// <summary>
		/// Gets the sinchronization object for the collection.
		/// </summary>
		public object SyncRoot 
		{ 
			get 
			{ 
				return null; 
			} 
		}

		/// <summary>
		/// Copies the elements of the collection into a System.Array object, starting at the given index.
		/// Throws NullArgumetException if the supplied array is null.
		/// Throws ArgumentOutOfRangeException  if
		/// - the index argument is negative
		/// - the index argument is greater than the array's Size
		/// - the sum of the index argument and of the collection Size is greater then the array's Size
		/// - the rank of the array argument is greater than 1
		/// </summary>
		/// <param name="array">array where the items of the collection will be copied to</param>
		/// <param name="index">starting index</param>
		public void CopyTo(System.Array array, int index)
		{
			if (array == null) throw new ArgumentNullException("array", "the argument cannot be null");
			if (index < 0) throw new ArgumentOutOfRangeException("index",index,"the index must be >= 0");
			if (array.Length <= index) throw new ArgumentOutOfRangeException("index",index,"index is greater then array Size");
			if (array.Length < index + this.size) throw new ArgumentOutOfRangeException("index", index, "the index value is too forward for the insertion of the elements in the collection");
			if (array.Rank != 1)throw new ArgumentOutOfRangeException("array",array,"the array has more than one dimension");


			for(int ndx=0; ndx<this.size; ndx++)
				array.SetValue(this.items[ndx], index+ndx);
		}

		#endregion
		
		#region IEnumerable Methods
		/// <summary>
		/// returns the enumerator object for the collection.
		/// </summary>
		/// <returns>enumerator for the collection</returns>
		public IEnumerator GetEnumerator()
		{
			this.Reset();
			return this;
		}

		#endregion

		#region IEnumerator Methods
		/// <summary>
		/// Gets the current object for the collection.
		/// </summary>
		public object Current 
		{
			get 
			{ 
				if ((this.index < this.size) && (this.index > -1))
					return this.items[index];
				else
					return null;
			}
		}
		/// <summary>
		/// Resets the collection enumerator.
		/// </summary>
		public void Reset()
		{
			this.index = -1;

		}

		/// <summary>
		/// Moves to the next element of the collection.
		/// </summary>
		/// <returns>false if there are no more elements to navigate, true otherwise</returns>
		public bool MoveNext()
		{
			
			this.index++;
			return (this.index < this.size);
		}

		#endregion

		/// <summary>
		/// Adds a non null item to the collection.
		/// </summary>
		/// <param name="item">item to Add</param>
		public void Add(IAgent item)
		{
			if (item == null)
				throw new ArgumentNullException("item","cannot Add a null item");

			for(int ndx=0; ndx<this.size; ndx++)
			{
				IAgent agent = this.items[ndx];
				if (agent.AID.Equals(item.AID) == true) 
				{
					throw new AgentService.Agent.AgentException("there is an agent with the same AID in the collection");
				}
			}

			if (this.size >= this.items.Length)
			{
				int ndx;
				IAgent[] backing = new IAgent[this.size*this.incFactor];
				for(ndx = 0; ndx<this.size; ndx++)
					backing[ndx] = this.items[ndx];
				backing[ndx] = item;
				this.size = ndx+1;
				this.items = backing;
			}
			else
				this.items[size++] = item;
		}

		/// <summary>
		/// Removes the first occurrence of the element equal to the argument.
		/// </summary>
		/// <param name="item">item to Remove</param>
		public void Remove(IAgent item)
		{
			if (item != null)
			{
				int found = size;
				int finalSize = size;
				for(int ndx = 0; ndx<this.size; ndx++)
				{
					if ((this.items[ndx] == item) && (found == this.size))
					{
						found = ndx;
						finalSize--;
					}

					if (found < ndx)
					{
						this.items[found++] = this.items[ndx];
					}

				}
				this.size = finalSize;
			}
		}
		
		/// <summary>
		/// Returns true if the collection Contains the supplied item.
		/// </summary>
		/// <param name="item">item candidate</param>
		/// <returns>true if found false otherwise</returns>
		public bool Contains(IAgent item)
		{
			return (this.IndexOf(item) != -1);
		}
		
		/// <summary>
		/// Returns the index of the item passed as argument.
		/// </summary>
		/// <param name="item">item to found</param>
		/// <returns>zero-based index of the item, -1 otherwise</returns>
		public int IndexOf(IAgent item)
		{
			int retval = -1;
			for(int ndx = 0; ndx<this.size; ndx++)
				// original
                if (this.items[ndx] == item)
					retval = ndx;

			return retval;
		}

		/// <summary>
		/// Returns the index of the agent corresponding to the given AID.
		/// </summary>
		/// <param name="item">item to find</param>
		/// <returns>zero-based index of the item, -1 otherwise</returns>
		public int IndexOf(AID item)
		{
			int retval = -1;
			for(int ndx = 0; ndx<this.size; ndx++)
				if (this.items[ndx].AID.Equals(item))
					retval = ndx;

			return retval;
		}

		///<summary>
		/// Returns the item at the given position in the collection.
		/// </summary>
		/// <param name="index">position of the item</param>
		/// <returns>the item at the desired position</returns>
		public IAgent this[int index]
		{
			get 
			{	
				if (index >= this.size)
					throw new IndexOutOfRangeException(String.Format("index {0} is greater or equal than the collection Size [{1}]", index, this.size));

				return this.items[index];
			}
			set 
			{
				if (index >= this.size)
					throw new IndexOutOfRangeException(String.Format("index {0} is greater or equal than the collection Size [{1}]", index, this.size));

				if (value == null)
					throw new NullReferenceException("Cannot insert a null item into the collection");

				this.items[index] = value;
			}
		}
	}

}
