using System;
using System.Diagnostics;
using UnityEngine.Assertions;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.Collections
{
    /// 
    /// A double rewindable allocators .
    /// 
    unsafe public struct DoubleRewindableAllocators : IDisposable
    {
        RewindableAllocator* Pointer;
        AllocatorHelper UpdateAllocatorHelper0;
        AllocatorHelper UpdateAllocatorHelper1;
        /// 
        /// Update the double rewindable allocators, switch Pointer to another allocator and rewind the newly switched allocator.
        /// 
        public void Update()
        {
            var UpdateAllocator0 = (RewindableAllocator*)UnsafeUtility.AddressOf(ref UpdateAllocatorHelper0.Allocator);
            var UpdateAllocator1 = (RewindableAllocator*)UnsafeUtility.AddressOf(ref UpdateAllocatorHelper1.Allocator);
            Pointer = (Pointer == UpdateAllocator0) ? UpdateAllocator1 : UpdateAllocator0;
            CheckIsCreated();
            Allocator.Rewind();
        }
        [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
        void CheckIsCreated()
        {
            if (!IsCreated)
            {
                throw new InvalidOperationException($"DoubleRewindableAllocators is not created.");
            }
        }
        /// 
        /// Retrieve the current rewindable allocator.
        /// 
        /// The Allocator retrieved.
        public ref RewindableAllocator Allocator
        {
            get
            {
                CheckIsCreated();
                return ref UnsafeUtility.AsRef(Pointer);
            }
        }
        /// 
        /// Check whether the double rewindable allocators is created.
        /// 
        /// True if current allocator is not null, otherwise false.
        public bool IsCreated => Pointer != null;
        /// 
        /// Construct a double rewindable allocators by allocating the allocators from backingAllocator and registering them.
        /// 
        /// Allocator used to allocate the double rewindable allocators.
        /// The initial capacity of the allocators, in bytes
        public DoubleRewindableAllocators(AllocatorManager.AllocatorHandle backingAllocator, int initialSizeInBytes)
        {
            this = default;
            Initialize(backingAllocator, initialSizeInBytes);
        }
        /// 
        /// Initialize a double rewindable allocators by allocating the allocators from backingAllocator and registering them.
        /// 
        /// Allocator used to allocate the double rewindable allocators.
        /// The initial capacity of the allocators, in bytes
        public void Initialize(AllocatorManager.AllocatorHandle backingAllocator, int initialSizeInBytes)
        {
            UpdateAllocatorHelper0 = new AllocatorHelper(backingAllocator);
            UpdateAllocatorHelper1 = new AllocatorHelper(backingAllocator);
            UpdateAllocatorHelper0.Allocator.Initialize(initialSizeInBytes);
            UpdateAllocatorHelper1.Allocator.Initialize(initialSizeInBytes);
            Pointer = null;
            Update();
        }
        /// 
        /// the double rewindable allocators and unregister it.
        /// 
        public void Dispose()
        {
            if (!IsCreated)
            {
                return;
            }
            UpdateAllocatorHelper0.Allocator.Dispose();
            UpdateAllocatorHelper1.Allocator.Dispose();
            UpdateAllocatorHelper0.Dispose();
            UpdateAllocatorHelper1.Dispose();
        }
        internal bool EnableBlockFree
        {
            get
            {
                Assert.IsTrue(UpdateAllocatorHelper0.Allocator.EnableBlockFree == UpdateAllocatorHelper1.Allocator.EnableBlockFree);
                return UpdateAllocatorHelper0.Allocator.EnableBlockFree;
            }
            set
            {
                UpdateAllocatorHelper0.Allocator.EnableBlockFree = value;
                UpdateAllocatorHelper1.Allocator.EnableBlockFree = value;
            }
        }
    }
}