using System;

namespace Pathfinding.Util {
	/// <summary>Various utilities for handling arrays and memory</summary>
	public static class Memory {
		/// <summary>
		/// Sets all values in an array to a specific value faster than a loop.
		/// Only faster for large arrays. Slower for small ones.
		/// Tests indicate it becomes faster somewhere when the length of the array grows above around 100.
		/// For large arrays this can be magnitudes faster. Up to 40 times faster has been measured.
		///
		/// Note: Only works on primitive value types such as int, long, float, etc.
		///
		/// <code>
		/// //Set all values to 8 in the array
		/// int[] arr = new int[20000];
		/// Pathfinding.Util.Memory.MemSet<int> (arr, 8, sizeof(int));
		/// </code>
		/// See: System.Buffer.BlockCopy
		/// </summary>
		/// <param name="array">the array to fill</param>
		/// <param name="value">the value to fill the array with</param>
		/// <param name="byteSize">size in bytes of every element in the array. e.g 4 bytes for an int, or 8 bytes for a long.
		/// It can be efficiently got using the sizeof built-in function.</param>
		public static void MemSet<T>(T[] array, T value, int byteSize) where T : struct {
			if (array == null) {
				throw new ArgumentNullException("array");
			}

			int block = 32, index = 0;
			int length = Math.Min(block, array.Length);

			//Fill the initial array
			while (index < length) {
				array[index] = value;
				index++;
			}

			length = array.Length;
			while (index < length) {
				Buffer.BlockCopy(array, 0, array, index*byteSize, Math.Min(block, length-index)*byteSize);
				index += block;
				block *= 2;
			}
		}

		/// <summary>
		/// Sets all values in an array to a specific value faster than a loop.
		/// Only faster for large arrays. Slower for small ones.
		/// Tests indicate it becomes faster somewhere when the length of the array grows above around 100.
		/// For large arrays this can be magnitudes faster. Up to 40 times faster has been measured.
		///
		/// Note: Only works on primitive value types such as int, long, float, etc.
		///
		/// It can be efficiently got using the sizeof built-in function.
		///
		/// <code>
		/// //Set all values to 8 in the array
		/// int[] arr = new int[20000];
		/// Pathfinding.Util.Memory.MemSet<int> (arr, 8, sizeof(int));
		/// </code>
		/// See: System.Buffer.BlockCopy
		/// </summary>
		/// <param name="array">the array to fill</param>
		/// <param name="value">the value to fill the array with</param>
		/// <param name="byteSize">size in bytes of every element in the array. e.g 4 bytes for an int, or 8 bytes for a long.</param>
		/// <param name="totalSize">all indices in the range [0, totalSize-1] will be set</param>
		public static void MemSet<T>(T[] array, T value, int totalSize, int byteSize) where T : struct {
			if (array == null) {
				throw new ArgumentNullException("array");
			}

			int block = 32, index = 0;
			int length = Math.Min(block, totalSize);

			//Fill the initial array
			while (index < length) {
				array[index] = value;
				index++;
			}

			length = totalSize;
			while (index < length) {
				Buffer.BlockCopy(array, 0, array, index*byteSize, Math.Min(block, totalSize-index)*byteSize);
				index += block;
				block *= 2;
			}
		}

		/// <summary>
		/// Returns a new array with at most length newLength.
		/// The array will contain a copy of all elements of arr up to but excluding the index newLength.
		/// </summary>
		public static T[] ShrinkArray<T>(T[] arr, int newLength) {
			newLength = Math.Min(newLength, arr.Length);
			var shrunkArr = new T[newLength];
			Array.Copy(arr, shrunkArr, newLength);
			return shrunkArr;
		}

		/// <summary>Swaps the variables a and b</summary>
		[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
		public static void Swap<T>(ref T a, ref T b) {
			T tmp = a;

			a = b;
			b = tmp;
		}
	}
}
