/*********************************************************************/
/*                                                                   */
/*                     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 System.Runtime.Serialization;

namespace AgentService.Platform.MTS
{
	/// <summary>
	/// Class IConversationCollection. Specialized Collection of IConversation objects
	/// </summary>
    [DataContract]
	public class IConversationCollection : ICollection, IEnumerator, IEnumerable
	{
		/// <summary>
		/// Synchronization object for the concurrent access.
		/// </summary>
		private object syncObject;
		
		/// <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>
		[DataMember]
        private int incFactor = 2;

		/// <summary>
		/// Size of the collection
		/// </summary>
		[DataMember]
        private int size = 0;

		/// <summary>
		/// Index of the current element in the collection
		/// </summary>
		[DataMember]
        private int index = 0;

		/// <summary>
		/// Internal data structure that stores the IConversation objects
		/// </summary>
        [DataMember]
		private IConversation[] items;

		/// <summary>
		/// Constructs an empty collection with initial Size equal to
		/// IConversationCollection.InitialDimension.
		/// </summary>
		public IConversationCollection()
		{
			this.syncObject = new Object();

			this.items = new IConversation[IConversationCollection.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 IConversationCollection(int initialDimension)
		{
			this.syncObject = new Object();

			if (initialDimension > 0)
			{
				this.items = new IConversation[initialDimension];
			}
			else
			{
				this.items = new IConversation[IConversationCollection.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 IConversationCollection(int initialDimension, int incFactor)
		{
			this.syncObject = new Object();
			
			if (initialDimension > 0)
			{
				this.items = new IConversation[initialDimension];
			}
			else
			{
				this.items = new IConversation[IConversationCollection.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 true; 
			} 
		}

		/// <summary>
		/// Gets the sinchronization object for the collection
		/// </summary>
		public object SyncRoot 
		{ 
			get 
			{ 
				return this.syncObject; 
			} 
		}

		/// <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)
		{
			lock(this.syncObject) 
			{
				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 
			{ 
				lock(this.syncObject) 
				{
					if ((this.index < this.size) && (this.index > -1))
						return this.items[index];
					else
						return null;
				}
			}
		}
		/// <summary>
		/// Resets the collection enumerator.
		/// </summary>
		public void Reset()
		{
			lock(this.syncObject) 
			{
				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()
		{
			lock(this.syncObject) 
			{
				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(IConversation item)
		{
			lock(this.syncObject) 
			{
				if (item == null)
					throw new ArgumentNullException("item","cannot Add a null item");

				if (this.size >= this.items.Length)
				{
					int ndx;
					IConversation[] backing = new IConversation[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(IConversation item)
		{
			lock(this.syncObject) 
			{
				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>
        /// Removes the first occurrence of the element equal to the argument.
        /// </summary>
        /// <param name="item">item to Remove</param>
        public void Remove(string convId)
        {
            lock (this.syncObject)
            {
                if (convId != null)
                {
                    int found = size;
                    int finalSize = size;
                    for (int ndx = 0; ndx < this.size; ndx++)
                    {
                        if ((this.items[ndx].ConvId.Equals(convId)) && (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(IConversation item)
		{
			lock(this.syncObject) 
			{
				return (this.IndexOf(item) != -1);
			}
		}
		
		/// <summary>
		/// Returns the index of the item passed as argument.
		/// </summary>
		/// <param name="mi">item to found</param>
		/// <returns>zero-based index of the item, -1 otherwise</returns>
		public int IndexOf(IConversation item)
		{
			lock(this.syncObject) 
			{
				int retval = -1;
				for(int ndx = 0; ndx<this.size; ndx++)
					if (this.items[ndx] == 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 IConversation this[int index]
		{
			get 
			{
                lock (this.syncObject)
                {
                    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 
			{
                lock (this.syncObject)
                {
                    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;
                }
			}
		}
	}

}
