//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;

namespace CommonHelpers {

//**************************************************************************************************
/// <summary>
/// </summary>
public partial class HDisposableBuffer<T>: HDisposable where T: struct {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public bool IsValid => IsValidDisposable;

public IntPtr Buffer
{
   get
   {
      VerifyValidDisposable();
      return BufferPtr;
   }
}

public int Capacity
{
   get
   {
      VerifyValidDisposable();
      return HMemory.GetAllocatedCapacity<T>( BufferPtr );
   }
}

public int SizeInBytes
{
   get
   {
      VerifyValidDisposable();
      return Capacity * HMarshal.SizeOf( typeof(T) );
   }
}

public bool IsTwiceEnlargeCapacityPossible
{
   get
   {
      return unchecked( Capacity * 2 / 2 ) == Capacity;
   }
}

//==================================================================================================

public HDisposableBuffer(int capacity)
{
   InitializeBuffer( capacity );
   ValidateDisposable();
}

public unsafe HDisposableBuffer(T[] initialContent)
{
   InitializeBuffer( initialContent.Length );
   PAccessHlp.CopyArrayToBuffer( initialContent, (void*)Buffer );
   ValidateDisposable();
}

public HDisposableBuffer(IntPtr ptrInitialContent,int elementCount)
{
   InitializeBuffer( elementCount );
   HMemory.Copy<T>( ptrInitialContent, Buffer, elementCount );
   ValidateDisposable();
}

//==================================================================================================

public static implicit operator IntPtr( HDisposableBuffer<T> disposableBuffer )
{
   return disposableBuffer!=null ? disposableBuffer.Buffer : IntPtr.Zero;
}

public static implicit operator T[]( HDisposableBuffer<T> disposableBuffer )
{
   return disposableBuffer!=null ? disposableBuffer.ToArray() : null;
}

//==================================================================================================

/// <exception cref="ArgumentOutOfRangeException" />
public void VerifyIndex(int idx)
{
   if (!HOps.IsBetween(idx,0,Capacity-1))
      throw new ArgumentOutOfRangeException(
         null, "Incorrect index." );
}

//==================================================================================================

public void ReAllocate(int newCapacity)
{
   VerifyValidDisposable();
   HMemory.ReAllocate<T>(ref BufferPtr,newCapacity);
}

/// <exception cref="HOperationFailedException" />
public void TwiceEnlargeCapacity()
{
   if (!IsTwiceEnlargeCapacityPossible)
      throw new HOperationFailedException(
         "Memory allocation block is too large (for signed integer value)." );
   ReAllocate( Capacity * 2 );
}

//==================================================================================================

protected IntPtr BufferPtr;

//--------------------------------------------------------------------------------------------------

protected HDisposableBuffer() {}

//==================================================================================================

protected void InitializeBuffer(int capacity)
{
   BufferPtr = HMemory.Allocate<T>( capacity );
}

protected T[] ToArray()
{
   return HMemory.ToArray<T>( Buffer, Capacity );
}

//--------------------------------------------------------------------------------------------------

protected override void OnDispose(bool synchronous)
{
   HMemory.Free(ref BufferPtr);
}

//==================================================================================================

static HDisposableBuffer()
{
   CommonHelpers_PAccess.Access();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HDisposableBuffer<T>
//**************************************************************************************************

} // CommonHelpers
