using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
namespace Unity.Collections
{
    /// 
    /// An unmanaged single value.
    /// 
    /// The functional equivalent of an array of length 1.
    /// When you need just one value, NativeReference can be preferable to an array because it better conveys the intent.
    /// The type of value.
    [StructLayout(LayoutKind.Sequential)]
    [NativeContainer]
    [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
    public unsafe struct NativeReference
        : INativeDisposable
        , IEquatable>
        where T : unmanaged
    {
        [NativeDisableUnsafePtrRestriction]
        internal void* m_Data;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
        internal AtomicSafetyHandle m_Safety;
        static readonly SharedStatic s_SafetyId = SharedStatic.GetOrCreate>();
#endif
        internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
        /// 
        /// Initializes and returns an instance of NativeReference.
        /// 
        /// The allocator to use.
        /// Whether newly allocated bytes should be zeroed out.
        public NativeReference(AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
        {
            Allocate(allocator, out this);
            if (options == NativeArrayOptions.ClearMemory)
            {
                UnsafeUtility.MemClear(m_Data, UnsafeUtility.SizeOf());
            }
        }
        /// 
        /// Initializes and returns an instance of NativeReference.
        /// 
        /// The allocator to use.
        /// The initial value.
        public NativeReference(T value, AllocatorManager.AllocatorHandle allocator)
        {
            Allocate(allocator, out this);
            *(T*)m_Data = value;
        }
        static void Allocate(AllocatorManager.AllocatorHandle allocator, out NativeReference reference)
        {
            CollectionHelper.CheckAllocator(allocator);
            reference = default;
            reference.m_Data = Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf(), UnsafeUtility.AlignOf(), allocator);
            reference.m_AllocatorLabel = allocator;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            reference.m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
            CollectionHelper.InitNativeContainer(reference.m_Safety);
            CollectionHelper.SetStaticSafetyId>(ref reference.m_Safety, ref s_SafetyId.Data);
#endif
        }
        /// 
        /// The value stored in this reference.
        /// 
        /// The new value to store in this reference.
        /// The value stored in this reference.
        public T Value
        {
            get
            {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
                return *(T*)m_Data;
            }
            set
            {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
#endif
                *(T*)m_Data = value;
            }
        }
        /// 
        /// Whether this reference has been allocated (and not yet deallocated).
        /// 
        /// True if this reference has been allocated (and not yet deallocated).
        public readonly bool IsCreated => m_Data != null;
        /// 
        /// Releases all resources (memory and safety handles).
        /// 
        public void Dispose()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
            {
                AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
            }
#endif
            if (!IsCreated)
            {
                return;
            }
            if (CollectionHelper.ShouldDeallocate(m_AllocatorLabel))
            {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                CollectionHelper.DisposeSafetyHandle(ref m_Safety);
#endif
                Memory.Unmanaged.Free(m_Data, m_AllocatorLabel);
                m_AllocatorLabel = Allocator.Invalid;
            }
            m_Data = null;
        }
        /// 
        /// Creates and schedules a job that will release all resources (memory and safety handles) of this reference.
        /// 
        /// A job handle. The newly scheduled job will depend upon this handle.
        /// The handle of a new job that will release all resources (memory and safety handles) of this reference.
        public JobHandle Dispose(JobHandle inputDeps)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
            {
                AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
            }
#endif
            if (!IsCreated)
            {
                return inputDeps;
            }
            if (CollectionHelper.ShouldDeallocate(m_AllocatorLabel))
            {
                var jobHandle = new NativeReferenceDisposeJob
                {
                    Data = new NativeReferenceDispose
                    {
                        m_Data = m_Data,
                        m_AllocatorLabel = m_AllocatorLabel,
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        m_Safety = m_Safety
#endif
                    }
                }.Schedule(inputDeps);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                AtomicSafetyHandle.Release(m_Safety);
#endif
                m_Data = null;
                m_AllocatorLabel = Allocator.Invalid;
                return jobHandle;
            }
            m_Data = null;
            return inputDeps;
        }
        /// 
        /// Copy the value of another reference to this reference.
        /// 
        /// The reference to copy from.
        public void CopyFrom(NativeReference reference)
        {
            Copy(this, reference);
        }
        /// 
        /// Copy the value of this reference to another reference.
        /// 
        /// The reference to copy to.
        public void CopyTo(NativeReference reference)
        {
            Copy(reference, this);
        }
        /// 
        /// Returns true if the value stored in this reference is equal to the value stored in another reference.
        /// 
        /// A reference to compare with.
        /// True if the value stored in this reference is equal to the value stored in another reference.
        [ExcludeFromBurstCompatTesting("Equals boxes because Value does not implement IEquatable")]
        public bool Equals(NativeReference other)
        {
            return Value.Equals(other.Value);
        }
        /// 
        /// Returns true if the value stored in this reference is equal to an object.
        /// 
        /// Can only be equal if the object is itself a NativeReference.
        /// An object to compare with.
        /// True if the value stored in this reference is equal to the object.
        [ExcludeFromBurstCompatTesting("Takes managed object")]
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            return obj is NativeReference && Equals((NativeReference)obj);
        }
        /// 
        /// Returns the hash code of this reference.
        /// 
        /// The hash code of this reference.
        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
        /// 
        /// Returns true if the values stored in two references are equal.
        /// 
        /// A reference.
        /// Another reference.
        /// True if the two values are equal.
        public static bool operator ==(NativeReference left, NativeReference right)
        {
            return left.Equals(right);
        }
        /// 
        /// Returns true if the values stored in two references are unequal.
        /// 
        /// A reference.
        /// Another reference.
        /// True if the two values are unequal.
        public static bool operator !=(NativeReference left, NativeReference right)
        {
            return !left.Equals(right);
        }
        /// 
        /// Copies the value of a reference to another reference.
        /// 
        /// The destination reference.
        /// The source reference.
        public static void Copy(NativeReference dst, NativeReference src)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety);
            AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
#endif
            UnsafeUtility.MemCpy(dst.m_Data, src.m_Data, UnsafeUtility.SizeOf());
        }
        /// 
        /// Returns a read-only reference aliasing the value of this reference.
        /// 
        /// A read-only reference aliasing the value of this reference.
        public ReadOnly AsReadOnly()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            return new ReadOnly(m_Data, ref m_Safety);
#else
            return new ReadOnly(m_Data);
#endif
        }
        /// 
        /// Returns a read-only native reference that aliases the content of a native reference.
        /// 
        /// NativeReference to alias.
        /// A read-only native reference that aliases the content of a native reference.
        public static implicit operator ReadOnly(NativeReference nativeReference)
        {
            return nativeReference.AsReadOnly();
        }
        /// 
        /// A read-only alias for the value of a NativeReference. Does not have its own allocated storage.
        /// 
        [NativeContainer]
        [NativeContainerIsReadOnly]
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
        public unsafe struct ReadOnly
        {
            [NativeDisableUnsafePtrRestriction]
            readonly void* m_Data;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle m_Safety;
            internal static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate();
            [GenerateTestsForBurstCompatibility(CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
            internal ReadOnly(void* data, ref AtomicSafetyHandle safety)
            {
                m_Data = data;
                m_Safety = safety;
                CollectionHelper.SetStaticSafetyId(ref m_Safety, ref s_staticSafetyId.Data);
            }
#else
            internal ReadOnly(void* data)
            {
                m_Data = data;
            }
#endif
            /// 
            /// The value aliased by this reference.
            /// 
            /// The value aliased by the reference.
            public T Value
            {
                get
                {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
                    return *(T*)m_Data;
                }
            }
        }
    }
    [NativeContainer]
    unsafe struct NativeReferenceDispose
    {
        [NativeDisableUnsafePtrRestriction]
        internal void* m_Data;
        internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
        internal AtomicSafetyHandle m_Safety;
#endif
        public void Dispose()
        {
            Memory.Unmanaged.Free(m_Data, m_AllocatorLabel);
        }
    }
    [BurstCompile]
    struct NativeReferenceDisposeJob : IJob
    {
        internal NativeReferenceDispose Data;
        public void Execute()
        {
            Data.Dispose();
        }
    }
}
namespace Unity.Collections.LowLevel.Unsafe
{
    /// 
    /// Provides extension methods for NativeReference.
    /// 
    [GenerateTestsForBurstCompatibility]
    public static class NativeReferenceUnsafeUtility
    {
        /// 
        /// Returns a pointer to this reference's stored value.
        /// 
        /// Performs a job safety check for read-write access.
        /// The type of the value.
        /// The reference.
        /// A pointer to this reference's stored value.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
        public static unsafe T* GetUnsafePtr(this NativeReference reference)
            where T : unmanaged
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckWriteAndThrow(reference.m_Safety);
#endif
            return (T*)reference.m_Data;
        }
        /// 
        /// Returns a pointer to this reference's stored value.
        /// 
        /// Performs a job safety check for read-only access.
        /// The type of the value.
        /// The reference.
        /// A pointer to this reference's stored value.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
        public static unsafe T* GetUnsafeReadOnlyPtr(this NativeReference reference)
            where T : unmanaged
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckReadAndThrow(reference.m_Safety);
#endif
            return (T*)reference.m_Data;
        }
        /// 
        /// Returns a pointer to this reference's stored value.
        /// 
        /// Performs no job safety checks.
        /// The type of the value.
        /// The reference.
        /// A pointer to this reference's stored value.
        [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
        public static unsafe T* GetUnsafePtrWithoutChecks(this NativeReference reference)
            where T : unmanaged
        {
            return (T*)reference.m_Data;
        }
    }
}