using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace KaoriStudio.Core.Collections
{
	/// <summary>
	/// A list with stack features
	/// </summary>
	/// <typeparam name="T">The type</typeparam>
	public class StackList<T> : List<T>
	{
		

		/// <summary>
		/// Top down accessor
		/// </summary>
		public struct StackListAccessor
		{
			private StackList<T> model;

			/// <summary>
			/// Creates a new top down accessor
			/// </summary>
			/// <param name="model">The underlying value</param>
			public StackListAccessor(StackList<T> model)
			{
				this.model = model;
			}

			/// <summary>
			/// Accesses from the top
			/// </summary>
			/// <param name="index">The number of elements below the top</param>
			/// <returns>The value</returns>
			public T this[int index]
			{
				get
				{
					int count = model.Count;

					if (index < 0 || index >= count)
						throw new IndexOutOfRangeException();

					return model[count - index - 1];
				}
				set
				{
					int count = model.Count;

					if (index < 0 || index >= count)
						throw new IndexOutOfRangeException();

					model[count - index - 1] = value;
				}
			}
		}

		/// <summary>
		/// Creates an empty stack list
		/// </summary>
		public StackList() : base()
		{
		}

		/// <summary>
		/// Creates a stack list with initial elements
		/// </summary>
		/// <param name="collection">The initial elements</param>
		public StackList(IEnumerable<T> collection) : base(collection)
		{
		}

		/// <summary>
		/// Creates a stack list with an initial capacity
		/// </summary>
		/// <param name="capacity">The initial capacity</param>
		public StackList(int capacity) : base(capacity)
		{
		}

		/// <summary>
		/// Accesses the stack from the top
		/// </summary>
		public StackListAccessor FromTop
		{
			get
			{
				return new StackListAccessor(this);
			}
		}

		/// <summary>
		/// Accesses the top of the stack
		/// </summary>
		public T Top
		{
			get
			{
				int count = base.Count;
				if (count > 0)
					return base[count - 1];
				else
					throw new InvalidOperationException();
			}
			set
			{
				int count = base.Count;
				if (count > 0)
					base[count - 1] = value;
				else
					throw new InvalidOperationException();
			}
		}

		

		/// <summary>
		/// Retrieves the top of the stack
		/// </summary>
		/// <returns>The top of the stack</returns>
		public T Peek()
		{
			int count = base.Count;
			if (count > 0)
				return base[count];
			else
				throw new InvalidOperationException();
		}

		/// <summary>
		/// Retrieves the top of the stack and drops it
		/// </summary>
		/// <returns>The top of the stack</returns>
		public T Pop()
		{
			int count = base.Count;
			if (count > 0)
			{
				count--;
				T rval = base[count];
				base.RemoveAt(count);
				return rval;
			}
			else
				throw new InvalidOperationException();
		}

		/// <summary>
		/// Pops a given number of elements from the stack
		/// </summary>
		/// <param name="count">The number of elements to pop</param>
		public void Pop(int count)
		{
			if (count == 0)
				return;
			else if (count < 0)
				throw new ArgumentOutOfRangeException("count", count, "Cannot be less than zero");
			else
			{
				int bcount = base.Count;
				if (count > bcount)
					throw new ArgumentException(string.Format("Cannot remove {0} elements from a stack with {1} elements", count, bcount), "count");
				else
					base.RemoveRange(bcount - count, count);
			}
		}

		/// <summary>
		/// Pushes a value onto the stack
		/// </summary>
		/// <param name="element">The element</param>
		public void Push(T element)
		{
			base.Add(element);
		}

		/// <summary>
		/// Whether the stack is empty
		/// </summary>
		public bool Empty
		{
			get
			{
				return base.Count == 0;
			}
		}
	}
}
