/* System.Collections.Specialized.StringCollection.cs
 * Authors:
 *   John Barnette (jbarn@httcb.net)
 *   Sean MacIsaac (macisaac@ximian.com)
 *
 *  Copyright (C) 2001 John Barnette
 * (C) Ximian, Inc.  http://www.ximian.com
 *
 * NOTES:
 * I bet Microsoft uses ArrayList as a backing store for this; I wonder what
 * the performance difference will be.
*/

using System;

namespace System.Collections.Specialized {
	public class StringCollection : IList, ICollection, IEnumerable {
		private static int   InitialCapacity    = 11;
		private static float CapacityMultiplier = 2.0f;
		
		private int count;
		private int modCount;
		
		private string[] entries;
		
		// Public Constructor
		public StringCollection() {
			entries  = new string[InitialCapacity];
			count    = 0;
			modCount = 0;
		}
		
		// Public Instance Properties
		public int Count {
			get { return count; }
		}
		
		public bool IsFixedSize {
			get { return false; }
		}
		
		public bool IsReadOnly {
			get { return false; }
		}
		
		public bool IsSynchronized {
			get { return false; }
		}
		
		object IList.this[int index] {
			get { return this[index]; }
			set { this[index] = value.ToString(); } 
		}
		
		public string this[int index] {
			get {
				if (index < 0 || index >= count) {
					throw new ArgumentOutOfRangeException("index");
				}
				
				return entries[index];
			}
			
			set {
				if (index < 0 || index >= count) {
					throw new ArgumentOutOfRangeException("index");
				}
				
				modCount++;
				entries[index] = value;
			}
		}
		
		public object SyncRoot {
			get { return this; }
		}
		
		
		// Public Instance Methods
		
		int IList.Add(object value) {
			return Add(value.ToString());
		}
		
		public int Add(string value) {
			modCount++;
			Resize(count + 1);
			int index = count++;
			entries[index] = value;
			
			return index;
		}
		
		public void AddRange(string[] value) {
			int numEntries = value.Length;
			
			modCount++;
			Resize(count + numEntries);
			Array.Copy(value, 0, entries, count, numEntries);
			count += numEntries;
		}
		
		public void Clear() {
			modCount++;
			count = 0;
		}
		
		bool IList.Contains(object value) {
			return Contains(value.ToString());
		}
		
		public bool Contains(string value) {
			foreach (string entry in entries) {
				if (value.Equals(entry)) {
					return true;
				}
			}
			
			return false;
		}
		
		void ICollection.CopyTo(Array array, int index) {
			if (array == null) {
				throw new ArgumentNullException("array");
			} else if (index < 0) {
				throw new ArgumentOutOfRangeException("index");
			} else if (array.Rank > 1) {
				throw new ArgumentException("array");
			} else if (index >= array.Length) {
				throw new ArgumentException("index");
			} else if (array.Length - index < count) {
				throw new ArgumentException("array");
			}
			
			Array.Copy(entries, 0, array, index, count);
		}
		
		public void CopyTo(string[] array, int index) {
			if (array == null) {
				throw new ArgumentNullException("array");
			} else if (index < 0) {
				throw new ArgumentOutOfRangeException("index");
			} else if (array.Rank > 1) {
				throw new ArgumentException("array");
			} else if (index >= array.Length) {
				throw new ArgumentException("index");
			} else if (array.Length - index < count) {
				throw new ArgumentException("array");
			}
			
			Array.Copy(entries, 0, array, index, count);
		}
		
		IEnumerator IEnumerable.GetEnumerator() {
			return new InternalEnumerator(this);
		}
		
		public StringEnumerator GetEnumerator() {
			return new StringEnumerator(this);
		}
		
		int IList.IndexOf(object value) {
			return IndexOf(value.ToString());
		}
		
		public int IndexOf(string value) {
			for (int i = 0; i < count; i++) {
				if (value.Equals(entries[i])) {
					return i;
				}
			}
			
			return -1;
		}
		
		void IList.Insert(int index, object value) {
			Insert(index, value.ToString());
		}
		
		public void Insert(int index, string value) {
			if (index < 0 || index > count) {
				throw new ArgumentOutOfRangeException("index");
			}
			
			modCount++;
			Resize(count + 1);
			Array.Copy(entries, index, entries, index + 1, count - index);
			entries[index] = value;
			count++;
		}

		
		void IList.Remove(object value) {
			Remove(value.ToString());
		}
		
		public void Remove(string value) {
			for (int i = 0; i < count; i++) {
				if (value.Equals(entries[i])) {
					RemoveAt(i);
					return;
				}
			}
		}
		
		public void RemoveAt(int index) {
			if (index < 0 || index >= count) {
				throw new ArgumentOutOfRangeException("index");
			}
			
			int remaining = count - index - 1;
			
			modCount++;
			
			if (remaining > 0) {
				Array.Copy(entries, index + 1, entries, index, remaining);
			}
			
			count--;
			entries[count] = null;
		}
		
		
		// Private Instance Methods
		
		private void Resize(int minSize) {
			int oldSize = entries.Length;
			
			if (minSize > oldSize) {
				string[] oldEntries = entries;
				int newSize = (int) (oldEntries.Length * CapacityMultiplier);
				
				if (newSize < minSize) newSize = minSize;
				entries = new string[newSize];
				Array.Copy(oldEntries, 0, entries, 0, count);
			}
		}
		
		
		// Private classes
		
		private class InternalEnumerator : IEnumerator {
			private StringCollection data;
			private int index;
			private int myModCount;
			
			public InternalEnumerator(StringCollection data) {
				this.data  = data;
				myModCount = data.modCount;
				index      = -1;
			}
			
			
			// Public Instance Properties
			
			public object Current {
				get {
					if (myModCount != data.modCount) {
						throw new InvalidOperationException();
					} else if (index < 0 || index > data.count - 1) {
						throw new InvalidOperationException();
					}
					
					return data[index];
				}
			}
			
			
			// Public Instance Methods
			
			public bool MoveNext() {
				if (myModCount != data.modCount) {
					throw new InvalidOperationException();
				}
				
				if (++index >= data.count - 1) {
					return false;
				}
				
				return true;
			}
			
			public void Reset() {
				if (myModCount != data.modCount) {
					throw new InvalidOperationException();
				}
				
				index = -1;
			}
		}
	}
}
