using System;
using System.Diagnostics;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Burst;
using static Unity.Collections.AllocatorManager;
namespace Unity.Collections
{
    /// 
    /// Extension methods for NativeArray.
    /// 
    [GenerateTestsForBurstCompatibility]
    public unsafe static class NativeArrayExtensions
    {
        /// 
        /// Provides a Burst compatible id for NativeArray types. Used by the Job Safety System.
        /// 
        /// 
        public struct NativeArrayStaticId
            where T : unmanaged
        {
            internal static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate>();
        }
        /// 
        /// Returns true if a particular value is present in this array.
        /// 
        /// The type of elements in this array.
        /// The value type.
        /// The array to search.
        /// The value to locate.
        /// True if the value is present in this array.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static bool Contains(this NativeArray array, U value) where T : unmanaged, IEquatable
        {
            return IndexOf(array.GetUnsafeReadOnlyPtr(), array.Length, value) != -1;
        }
        /// 
        /// Finds the index of the first occurrence of a particular value in this array.
        /// 
        /// The type of elements in this array.
        /// The value type.
        /// The array to search.
        /// The value to locate.
        /// The index of the first occurrence of the value in this array. Returns -1 if no occurrence is found.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static int IndexOf(this NativeArray array, U value) where T : unmanaged, IEquatable
        {
            return IndexOf(array.GetUnsafeReadOnlyPtr(), array.Length, value);
        }
        /// 
        /// Returns true if a particular value is present in this array.
        /// 
        /// The type of elements in this array.
        /// The value type.
        /// The array to search.
        /// The value to locate.
        /// True if the value is present in this array.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static bool Contains(this NativeArray.ReadOnly array, U value) where T : unmanaged, IEquatable
        {
            return IndexOf(array.GetUnsafeReadOnlyPtr(), array.m_Length, value) != -1;
        }
        /// 
        /// Finds the index of the first occurrence of a particular value in this array.
        /// 
        /// The type of elements in this array.
        /// The type of value to locate.
        /// The array to search.
        /// The value to locate.
        /// The index of the first occurrence of the value in this array. Returns -1 if no occurrence is found.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static int IndexOf(this NativeArray.ReadOnly array, U value) where T : unmanaged, IEquatable
        {
            return IndexOf(array.GetUnsafeReadOnlyPtr(), array.m_Length, value);
        }
        /// 
        /// Returns true if a particular value is present in a buffer.
        /// 
        /// The type of elements in the buffer.
        /// The value type.
        /// The buffer.
        /// Number of elements in the buffer.
        /// The value to locate.
        /// True if the value is present in the buffer.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static bool Contains(void* ptr, int length, U value) where T : unmanaged, IEquatable
        {
            return IndexOf(ptr, length, value) != -1;
        }
        /// 
        /// Finds the index of the first occurrence of a particular value in a buffer.
        /// 
        /// The type of elements in the buffer.
        /// The value type.
        /// A buffer.
        /// Number of elements in the buffer.
        /// The value to locate.
        /// The index of the first occurrence of the value in the buffer. Returns -1 if no occurrence is found.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static int IndexOf(void* ptr, int length, U value) where T : unmanaged, IEquatable
        {
            for (int i = 0; i != length; i++)
            {
                if (UnsafeUtility.ReadArrayElement(ptr, i).Equals(value))
                    return i;
            }
            return -1;
        }
        /// 
        /// Copies all elements of specified container to array.
        /// 
        /// The type of elements in this container.
        /// Container to copy to.
        /// An container to copy into this array.
        /// Thrown if the array and container have unequal length.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
        public static void CopyFrom(this ref NativeArray container, NativeList other)
            where T : unmanaged, IEquatable
        {
            container.CopyFrom(other.AsArray());
        }
        /// 
        /// Copies all elements of specified container to array.
        /// 
        /// The type of elements in this container.
        /// Container to copy to.
        /// An container to copy into this array.
        /// Thrown if the array and container have unequal length.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
        public static void CopyFrom(this ref NativeArray container, in NativeHashSet other)
            where T : unmanaged, IEquatable
        {
            using (var array = other.ToNativeArray(Allocator.TempJob))
            {
                container.CopyFrom(array);
            }
        }
        /// 
        /// Copies all elements of specified container to array.
        /// 
        /// The type of elements in this container.
        /// Container to copy to.
        /// An container to copy into this array.
        /// Thrown if the array and container have unequal length.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
        public static void CopyFrom(this ref NativeArray container, in UnsafeHashSet other)
            where T : unmanaged, IEquatable
        {
            using (var array = other.ToNativeArray(Allocator.TempJob))
            {
                container.CopyFrom(array);
            }
        }
        /// 
        /// Returns the reinterpretation of this array into another kind of NativeArray.
        /// See [Array reinterpretation](https://docs.unity3d.com/Packages/com.unity.collections@latest?subfolder=/manual/allocation.html#array-reinterpretation).
        /// 
        /// The array to reinterpret.
        /// Type of elements in the array.
        /// Type of elements in the reinterpreted array.
        /// The reinterpretation of this array into another kind of NativeArray.
        /// Thrown if this array's capacity cannot be evenly divided by `sizeof(U)`.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
        public static NativeArray Reinterpret(this NativeArray array)
            where U : unmanaged
            where T : unmanaged
        {
            var tSize = UnsafeUtility.SizeOf();
            var uSize = UnsafeUtility.SizeOf();
            var byteLen = ((long)array.Length) * tSize;
            var uLen = byteLen / uSize;
            CheckReinterpretSize(ref array);
            var ptr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array);
            var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptr, (int)uLen, Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            var handle = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
            NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref result, handle);
#endif
            return result;
        }
        /// 
        /// Returns true if this array and another have equal length and content.
        /// 
        /// The type of the source array's elements.
        /// The array to compare for equality.
        /// The other array to compare for equality.
        /// True if the arrays have equal length and content.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
        public static bool ArraysEqual(this NativeArray container, NativeArray other)
            where T : unmanaged, IEquatable
        {
            if (container.Length != other.Length)
                return false;
            for (int i = 0; i != container.Length; i++)
            {
                if (!container[i].Equals(other[i]))
                    return false;
            }
            return true;
        }
        [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
        static void CheckReinterpretSize(ref NativeArray array)
            where U : unmanaged
            where T : unmanaged
        {
            var tSize = UnsafeUtility.SizeOf();
            var uSize = UnsafeUtility.SizeOf();
            var byteLen = ((long)array.Length) * tSize;
            var uLen = byteLen / uSize;
            if (uLen * uSize != byteLen)
            {
                throw new InvalidOperationException($"Types {typeof(T)} (array length {array.Length}) and {typeof(U)} cannot be aliased due to size constraints. The size of the types and lengths involved must line up.");
            }
        }
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
        internal static void Initialize(ref this NativeArray array,
                                            int length,
                                            AllocatorManager.AllocatorHandle allocator,
                                            NativeArrayOptions options = NativeArrayOptions.ClearMemory)
            where T : unmanaged
        {
            AllocatorHandle handle = allocator;
            array = default;
            array.m_Buffer = handle.AllocateStruct(default(T), length);
            array.m_Length = length;
            array.m_AllocatorLabel = allocator.IsAutoDispose ? Allocator.None : allocator.ToAllocator;
            if (options == NativeArrayOptions.ClearMemory)
            {
                UnsafeUtility.MemClear(array.m_Buffer, array.m_Length * UnsafeUtility.SizeOf());
            }
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            array.m_MinIndex = 0;
            array.m_MaxIndex = length - 1;
            array.m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
            CollectionHelper.SetStaticSafetyId>(ref array.m_Safety, ref NativeArrayStaticId.s_staticSafetyId.Data);
            handle.AddSafetyHandle(array.m_Safety);
#endif
        }
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(AllocatorManager.AllocatorHandle) })]
        internal static void Initialize(ref this NativeArray array,
                                                int length,
                                                ref U allocator,
                                                NativeArrayOptions options = NativeArrayOptions.ClearMemory)
            where T : unmanaged
            where U : unmanaged, AllocatorManager.IAllocator
        {
            array = default;
            array.m_Buffer = allocator.AllocateStruct(default(T), length);
            array.m_Length = length;
            array.m_AllocatorLabel = allocator.IsAutoDispose ? Allocator.None : allocator.ToAllocator;
            if (options == NativeArrayOptions.ClearMemory)
            {
                UnsafeUtility.MemClear(array.m_Buffer, array.m_Length * UnsafeUtility.SizeOf());
            }
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            array.m_MinIndex = 0;
            array.m_MaxIndex = length - 1;
            array.m_Safety = CollectionHelper.CreateSafetyHandle(allocator.ToAllocator);
            CollectionHelper.SetStaticSafetyId>(ref array.m_Safety, ref NativeArrayStaticId.s_staticSafetyId.Data);
            allocator.Handle.AddSafetyHandle(array.m_Safety);
#endif
        }
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
        internal static void DisposeCheckAllocator(ref this NativeArray array)
            where T : unmanaged
        {
            if (array.m_Buffer == null)
            {
                throw new ObjectDisposedException("The NativeArray is already disposed.");
            }
            if (!AllocatorManager.IsCustomAllocator(array.m_AllocatorLabel))
            {
                array.Dispose();
            }
            else
            {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                AtomicSafetyHandle.DisposeHandle(ref array.m_Safety);
#endif
                AllocatorManager.Free(array.m_AllocatorLabel, array.m_Buffer);
                array.m_AllocatorLabel = Allocator.Invalid;
                array.m_Buffer = null;
            }
        }
    }
}