using System;
using System.Buffers;
using System.Runtime.CompilerServices;

/*https://github.com/sidristij/memory-pools*/

namespace MemoryPools.Memory
{
    /// <summary>
    ///     Encapsulates manual memory management mechanism. Holds
    ///     IMemoryOwner instance goes to GC (link = null!) only when
    ///     all owning entities called Dispose() method. This means,
    ///     that only this mechanism should be used for covering
    ///     managed instances.
    /// </summary>
    public sealed class CountdownMemoryOwner<T> : IMemoryOwner<T>
	{
		private int _length;
		private int _offset;
		private int _owners;
		private T[] _arr;
		private CountdownMemoryOwner<T> _parent;
		private Memory<T> _memory;

		public Memory<T> Memory
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get => _memory;

			private set => _memory = value;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		internal CountdownMemoryOwner<T> Init(CountdownMemoryOwner<T> parent, int offset, int length, bool defaultOwner = true)
		{
			_owners = defaultOwner ? 1 : 0;
			_offset = offset;
			_length = length;
			_parent = parent;
			_parent.AddOwner();
			Memory = _parent.Memory.Slice(_offset, _length);
			return this;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		internal CountdownMemoryOwner<T> Init(T[] array, int length)
		{
			_owners = 1;
			_offset = 0;
			_length = length;
			_parent = default!;
			_arr = array;
			Memory = _arr.AsMemory(0, _length);
			return this;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void AddOwner() => _owners++;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void Dispose()
		{
			_owners--;
			if (_owners > 0)
			{
				return;
			}

			if (_parent != default)
			{
				_parent.Dispose();
				_parent = null!;
			}
			else
			{
				ArrayPool<T>.Shared.Return(_arr);
			}
			Pool<CountdownMemoryOwner<T>>.Return(this);
		}
	}
}
