//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

#if !WASM
namespace user_rtx { using u = user_rtx;
#else
namespace user_rtx.wasm { using u = user_rtx.wasm;
#endif

/// <summary>
/// Disposable implementation helpers
/// </summary>
public static class disposable_impl {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static ObjectDisposedException DisposedError( Type objectType ) =>
   u.disposable_errors.OBJECT_DISPOSED( objectType );

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

/// <exception cref="ObjectDisposedException" />
public static void VerifyValid( bool isValidDisposable, Type objectType )
{
   if ( !isValidDisposable )
      throw DisposedError( objectType );
}

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

public static void Validate( [u.volatile_access] ref bool isValidDisposable ) =>
   Volatile.Write( ref isValidDisposable, true );

public static void Validate( [u.volatile_access] ref bool isValidDisposable,
                             [u.volatile_access] ref StackTrace? creationStack,
                             StackTrace? creationStackValue )
{
   Volatile.Write( ref creationStack, creationStackValue );
   Validate( ref isValidDisposable );
}

[MethodImpl( MethodImplOptions.NoInlining )]
public static void Validate( [u.volatile_access] ref bool isValidDisposable,
                             [u.volatile_access] ref StackTrace? creationStack ) =>
   Validate( ref isValidDisposable, ref creationStack, 1 );

/// <exception cref="ArgumentOutOfRangeException" />
[MethodImpl( MethodImplOptions.NoInlining )]
public static void Validate( [u.volatile_access] ref bool isValidDisposable,
                             [u.volatile_access] ref StackTrace? creationStack,
                             [u.not_negative] int skipFrames )
{
   u.arg_check.NotNegative( skipFrames, nameof(skipFrames) );
   Volatile.Write( ref creationStack,
      urtx_core.IsDebug ? new StackTrace( skipFrames + 2 ) : null );
   Validate( ref isValidDisposable );
}

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

[MethodImpl( MethodImplOptions.NoInlining )]
public static StackTrace? PrepareCreationStack() =>
   PrepareCreationStack( 1 );

/// <exception cref="ArgumentOutOfRangeException" />
[MethodImpl( MethodImplOptions.NoInlining )]
public static StackTrace? PrepareCreationStack( [u.not_negative] int skipFrames )
{
   u.arg_check.NotNegative( skipFrames, nameof(skipFrames) );
   return urtx_core.IsDebug ? new StackTrace( skipFrames + 2 ) : null;
}

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

public static bool NeedsDisposeValue( [u.volatile_access] ref bool needsDispose ) =>
   Volatile.Read( ref needsDispose );

public static StackTrace? CreationStackValue( [u.volatile_access] ref StackTrace? creationStack ) =>
   Volatile.Read( ref creationStack );

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

public static bool NeedsDispose( [u.volatile_access] ref bool isValidDisposable,
                                 IDisposable disposingInstance )
{
   if ( !Volatile.Read( ref isValidDisposable ) )
      return false;
   Volatile.Write( ref isValidDisposable, false );
   GC.SuppressFinalize( disposingInstance );
   return true;
}

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

/// <exception cref="InvalidOperationException" />
public static void VerifySyncDisposedFromFinalizer( bool isValidDisposable,
                                                    Type objectType,
                                                    StackTrace? creationStack )
{
   if ( !isValidDisposable )
      return;
   var sb = new StringBuilder(u.str.FMT(
      "Object of type “{0}” was not disposed synchronously.",
      objectType ));
   if ( creationStack != null ) {
      u.text.AppendLine( sb,
         "___ Object creation stack: ___" );
      u.text.AppendLine( sb, stack_trace.ToText( creationStack ) );
   }
   throw new InvalidOperationException( sb.ToString() );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // disposable_impl

} // user_rtx (.wasm)
