//
// System.Collections.Queue
//
// Author:
//    Ricardo Fernández Pascual
//
// (C) 2001 Ricardo Fernández Pascual
//

using System;
using System.Collections;

namespace System.Collections {

	public class Queue : ICollection, IEnumerable, ICloneable {

		private object[] contents;
		private int head = 0;   // points to the first used slot
		private int count = 0;
		private int capacity = 16;
		private float growFactor = 2.0F;
		private int modCount = 0;

		public Queue () {
			contents = new object[capacity];
		}

		public Queue (ICollection collection) {
			capacity = collection.Count;
			contents = new object[capacity];
			count = capacity;
			collection.CopyTo (contents, 0);
		}

		public Queue (int initialCapacity) {
			capacity = initialCapacity;
			contents = new object[capacity];
		}

		public Queue (int initialCapacity, float growFactor) {
			capacity = initialCapacity;
			contents = new object[capacity];
			// LAMESPEC: The spec says nothing, but I think this 
			// should throw an exception if growFactor <= 1.0
			this.growFactor = growFactor;
		}
		
		// from ICollection

		public virtual int Count {
			get { return count; }
		}

		public virtual bool IsSynchronized {
			get { return false; }
		}

		public virtual object SyncRoot {
			get { return this; }
		}

		public virtual void CopyTo (Array array, int index) {
			if (array == null) {
				throw new ArgumentNullException ();
			}

			if (index < 0) {
				throw new ArgumentOutOfRangeException ();
			}

			if (array.Rank > 1 
			    || index >= array.Length 
			    || count > array.Length - index) {
				throw new ArgumentException ();
			}
			
			// copy the contents of the circular array
			Array.Copy (contents, head, array, index,
				    Math.Max (count, capacity - head));
			if (count > capacity - head)
				Array.Copy (contents, 0, array, 
					    index + capacity - head,
					    count - (capacity - head));
		}

		// from IEnumerable
		
		public virtual IEnumerator GetEnumerator () {
			return new QueueEnumerator (this);
		}

		// from ICloneable
		
		public virtual object Clone () {
			Queue newQueue;
			
			newQueue = new Queue (); // FIXME: improve this...
			
			newQueue.contents = (object[]) this.contents.clone ();
			newQueue.head = this.head;
			newQueue.count = this.count;
			newQueue.capacity = this.capacity;
			newQueue.growFactor = this.growFactor;

			return newQueue;
		}

		// FIXME: should override Equals?

		// from Queue spec

		public virtual bool IsReadOnly {
			get { return false; }
		}

		public virtual void Clear () {
			modCount++;
			head = 0;
			count = 0;
			// FIXME: Should allocate a new contents array? 
			//        Should null the current array?
		}

		public virtual bool Contains (object obj) {
			int tail = head + count;
			if (obj == null) {
				for (int i = head; i < tail; i++) {
					if (contents[i % capacity] == null) 
						return true;
				}
			} else {
				for (int i = head; i < tail; i++) {
					if (obj.Equals (contents[i % capacity]))
						return true;
				}
			}
			return false;
		}
		
		public virtual object Dequeue ()
		{
			modCount++;
			if (count < 1)
				throw new InvalidOperationException ();
			object result = contents[head];
			head = (head + 1) % capacity;
			count--;
			return result;
		}

		public virtual void Enqueue (object obj) {
			modCount++;
			if (count == capacity) 
				grow ();
			contents[(head + count) % capacity] = obj;
			count++;
		}

		public virtual object Peek () {
			if (count < 1)
				throw new InvalidOperationException ();
			return contents[head];
		}

		public static Queue Synchronized (Queue queue) {
			if (queue == null) {
				throw new ArgumentNullException ();
			}
			return new SyncQueue (queue);
		}

		public virtual object[] ToArray () {
			object[] ret = new object[count];
			CopyTo (ret, 0);
			return ret;
		}

		// private methods

		private void grow () {
			int newCapacity = capacity * 2;
			int newContents = new object[newCapacity];
			CopyTo (newContents, 0);
			contents = newContents;
			head = 0;
		}

		// private classes

		private class SyncQueue : Queue {
			Queue queue;
			
			internal SyncQueue (Queue queue) {
				this.queue = queue;
			}
			
			public override int Count {
				get { 
					lock (queue) {
						return queue.count; 
					}
				}
			}

			public override bool IsSynchronized {
				get { 
					lock (queue) {
						return queue.IsSynchronized;
					}
				}
			}

			public override object SyncRoot {
				get { 
					return queue.SyncRoot; 
				}
			}

			public override void CopyTo (Array array, int index) {
				lock (queue) {
					queue.CopyTo (array, index);
				}
			}
			
			public override IEnumerator GetEnumerator () {
				lock (queue) {
					return queue.GetEnumerator ();
				}
			}
			
			public override object Clone () {
				lock (queue) {
					return queue.Clone ();
				}
			}
			
			public override bool IsReadOnly {
				get { 
					lock (queue) {
						return queue.IsReadOnly;
					}
				}
			}

			public override void Clear () {
				lock (queue) {
					queue.Clear ();
				}
			}

			public override bool Contains (object obj) {
				lock (queue) {
					return queue.Contains (obj);
				}
			}
		
			public override object Dequeue () {
				lock (queue) {
					return queue.Dequeue ();
				}
			}
			
			public override void Enqueue (object obj) {
				lock (queue) {
					queue.Enqueue (obj);
				}
			}

			public override object Peek () {
				lock (queue) {
					return queue.Peek ();
				}
			}

			public override object[] ToArray () {
				lock (queue) {
					return queue.ToArray ();
				}
			}
		}

		private class QueueEnumerator : IEnumerator {
			Queue queue;
			private int modCount;
			private int current;

			internal QueueEnumerator (Queue q) {
				queue = q;
				modCount = q.modCount;
				current = -1;  // one element before the head
			}

			public virtual object Current {
				get {
					if (modCount != queue.modCount 
					    || current < 0
					    || current >= queue.count)
						throw new InvalidOperationException ();
					return queue.contents[(queue.head + current) % queue.capacity];
				}
			}

			public virtual bool MoveNext () {
				if (modCount != queue.modCount) {
					throw new InvalidOperationException ();
				}

				if (current >= queue.count) {
					return false;
				} else {
					current++;
					return true;
				}
			}

			public virtual void Reset () {
				if (modCount != queue.modCount) {
					throw new InvalidOperationException();
				}
				current = -1;
			}
		}
	}
}

