//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Reflection;
using System.Diagnostics;
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>
/// Dispatcher stack saver for fatal error (temporary assigned to “user_rtx.fatal.DispatcherCallerCurrentStack” static property)
/// </summary>
[u.sync_only_disposable]
public class DispatcherCallerStackSaver: u.DisposableSyncImpl<DispatcherCallerStackSaver> {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public const bool SaveFileInfoDF = urtx_core_info.IsDebug;

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

[u.def( SaveFileInfoDF )][u.volatile_access]
public static bool SaveFileInfo {
   get => _saveFileInfo;
   set => _saveFileInfo = value; }

[u.volatile_access]
public static Func<StackTrace?,StackTrace?>? SetLastStackFN {
   get => _setLastStackFN;
   set => _setLastStackFN = value; }

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

public StackTrace CurrentStack { get; }
public StackTrace? PreviousStack { get; }

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

[u.in_target_dispatcher_thread][MethodImpl( MethodImplOptions.NoInlining )]
public DispatcherCallerStackSaver( StackTrace dispatcherCallerStack )
{
   var setLastStack = SetLastStackFN ?? SetLastStack_Default;
   PreviousStack = setLastStack( CurrentStack = dispatcherCallerStack );
   ValidateDisposable();
}

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

[u.returns_previous]
public static StackTrace? SetLastStack_Default( StackTrace? dispatcherCallerStack )
{
   var previousStack = u.fatal.DispatcherCallerCurrentStack;
   u.fatal.DispatcherCallerCurrentStack = dispatcherCallerStack;
   return previousStack;
}

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

[u.not_for_target_dispatcher_thread][MethodImpl( MethodImplOptions.NoInlining )]
public static StackTrace PrepareStackTrace() =>
   PrepareStackTrace( 1 );

/// <exception cref="ArgumentOutOfRangeException" />
[u.not_for_target_dispatcher_thread][MethodImpl( MethodImplOptions.NoInlining )]
public static StackTrace PrepareStackTrace( [u.not_negative] int skipFrames ) =>
   new StackTrace(
      u.arg_check.NotNegative( skipFrames, nameof(skipFrames) ) + 1,
      SaveFileInfo );

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

protected override DispatcherCallerStackSaver DisposableSelf => this;

protected override void OnSyncDispose() =>
   (SetLastStackFN ?? SetLastStack_Default)( PreviousStack );

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

static volatile bool
   _saveFileInfo = SaveFileInfoDF;
static volatile Func<StackTrace?,StackTrace?>?
   _setLastStackFN;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // DispatcherCallerStackSaver

} // user_rtx (.wasm)
