using System;
using System.Collections.Generic;

namespace miew.Array
{
	using SysArray = System.Array;

	public static class Extensions
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// remember to re-store the return value of this extension
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static T[] RemoveAt<T>(this T[] arr, int idx)
		{
			int c = arr.Length;
			if (idx < 0 || idx >= c)
				throw new IndexOutOfRangeException();

			T[] newarr = new T[c - 1];

			SysArray.Copy(arr, 0, newarr, 0, idx);					// part I.

			int i_src = idx + 1;
			SysArray.Copy(arr, i_src, newarr, idx, c - i_src);		// part II.

			return newarr;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// remember to re-store the return value of this extension
		/// Array.Resize always copies to a new array. Rather than sliding the original array and doing a 
		/// resize, we'll just create the new array ourselves and avoid Array.Resize.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static T[] RemoveRange<T>(this T[] arr, int idx, int count)
		{
			int c = arr.Length;
			if (idx < 0 || idx >= c)
				throw new IndexOutOfRangeException();
			if (count == 0)
				return arr;
			if (idx + count > c)
				throw new ArgumentOutOfRangeException();

			T[] newarr = new T[c - count];

			SysArray.Copy(arr, 0, newarr, 0, idx);					// part I.

			int i_src = idx + count;
			SysArray.Copy(arr, i_src, newarr, idx, c - i_src);		// part II.

			return newarr;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static bool ValueCompare<T>(this T[] a, T[] b) where T : struct
		{
			if (a.Length != b.Length)
				return false;

			EqualityComparer<T> q = EqualityComparer<T>.Default;
			for (int i = 0; i < a.Length; i++)
				if (!q.Equals(a[i], b[i]))
					return false;

			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static bool ValueCompare<T>(this T[] a, int a_offset, T[] b) where T : struct
		{
			if (a_offset >= a.Length || a_offset + b.Length > a.Length)
				return false;

			EqualityComparer<T> q = EqualityComparer<T>.Default;
			for (int i = 0; i < b.Length; i++, a_offset++)
				if (!q.Equals(a[a_offset], b[i]))
					return false;

			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// In a list of objects of type T which are sorted in order based on type K, search for a value of type K.
		/// </summary>
		/// <remarks>
		/// http://www.removingalldoubt.com/permalink.aspx/f7e6feff-8257-4efe-ad64-acd1c7a4a1e3
		/// </remarks>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static int BinarySearch<T, K>(this IList<T> list, K value, Converter<T, K> convert, Comparison<K> compare)
		{
			int i = 0;
			int j = list.Count - 1;
			while (i <= j)
			{
				int m = i + (j - i) / 2;
				int r = compare(convert(list[m]), value);
				if (r == 0)
					return m;
				if (r < 0)
					i = m + 1;
				else
					j = m - 1;
			}
			return ~i;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// In a list of objects of type T which are sorted an order based on type K, search for a value of type K.
		/// </summary>
		/// <typeparam name="T">Type the objects in the list</typeparam>
		/// <typeparam name="K">Type of the key by by which the objects in the list are sorted</typeparam>
		/// <param name="list">IList interface handle to a list of objects of type T</param>
		/// <param name="item">Object of type K to search for</param>
		/// <param name="convert">Function which derives a key of type K from an object of type T</param>
		/// <returns>
		/// The zero-based index of the item in the list if it is found; otherwise, a negative number that is 
		/// the bitwise complement of the index of the next element that is larger than item
		/// </returns>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static int BinarySearch<T, K>(this IList<T> list, K item, Converter<T, K> convert)
			where K : IComparable<K>
		{
			return BinarySearch<T, K>(list, item, list.Count, convert);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Among the first 'c' elements of a list of objects of type T which are sorted an order based on type K, 
		/// search for a value of type K.
		/// </summary>
		/// <typeparam name="T">Type the objects in the list</typeparam>
		/// <typeparam name="K">Type of the key by by which the objects in the list are sorted</typeparam>
		/// <param name="list">IList interface handle to a list of objects of type T</param>
		/// <param name="item">Object of type K to search for</param>
		/// <param name="c">Number of objects at the beginning of the list to search within</param>
		/// <param name="convert">Function which derives a key of type K from an object of type T</param>
		/// <returns>
		/// The zero-based index of the item in the list if it is found; otherwise, a negative number that is 
		/// the bitwise complement of the index of the next element that is larger than item
		/// </returns>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static int BinarySearch<T, K>(this IList<T> list, K item, int c, Converter<T, K> convert)
			where K : IComparable<K>
		{
			int i = 0;
			int j = c - 1;
			while (i <= j)
			{
				int m = i + (j - i) / 2;
				int r = convert(list[m]).CompareTo(item);
				if (r == 0)
					return m;
				if (r < 0)
					i = m + 1;
				else
					j = m - 1;
			}
			return ~i;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		static public void Swap<T>(this IList<T> rg, int i_l, int i_r)
		{
			if (i_l != i_r)
			{
				T temp = rg[i_r];
				rg[i_r] = rg[i_l];
				rg[i_l] = temp;
			}
		}
	};
}