//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Reflection;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.CompilerServices;

#if !WASM
namespace CommonHelpers {
#else
namespace CommonHelpers.WASM {
#endif

//**************************************************************************************************
/// <summary>
/// </summary>
[HSyncOnlyDisposable]
public partial class HAssertion: HDisposable {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[DefaultValue(true)]
public static bool? UseExceptions
{
   get { return HOps.IntToNullableBool( _UseExceptions_ ); }
   set { _UseExceptions_ = HOps.NullableBoolToInt( value ); }
}

[HThreadStatic]
public static bool? UseExceptionsContext
{
   get { return _UseExceptionsContext_; }
}

[HThreadStatic]
public static bool? ExceptionsUsing
{
   get
   {
      return UseExceptionsContext.HasValue ? UseExceptionsContext.Value : UseExceptions;
   }
}

[DefaultValue(_SaveFileInfoForDispatcherCallerStackDefault_)]
public static bool SaveFileInfoForDispatcherCallerStack
{
   get { return _SaveFileInfoForDispatcherCallerStack_; }
   set { _SaveFileInfoForDispatcherCallerStack_ = value; }
}

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

/// <exception cref="InvalidOperationException" />
public HAssertion([HOptional] StackTrace dispatcherCallerStackTrace)
{
   HProgram.VerifyInMainThread();
   using (new HMemoryBarrier())
   {
      _DispatcherCallerPreviousStack_ = _DispatcherCallerStackTrace_;
      _DispatcherCallerStackTrace_ = dispatcherCallerStackTrace;
   }
   ValidateDisposable();
}

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

/// <exception cref="HAssertionFailedException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Verify(bool condition)
{
   if ( !condition )
      RaiseError( Assembly.GetCallingAssembly() );
   return null;
}

/// <exception cref="HAssertionFailedException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Deny(bool unacceptableCondition)
{
   if ( unacceptableCondition )
      RaiseError( Assembly.GetCallingAssembly() );
   return null;
}

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

/// <exception cref="HAssertionFailedException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException FAIL()
{
   return RaiseError( Assembly.GetCallingAssembly() );
}
/// <exception cref="HAssertionFailedException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException FAIL([HOptional] string message)
{
   return RaiseError( Assembly.GetCallingAssembly(), message );
}
/// <exception cref="ArgumentNullException" />
/// <exception cref="HAssertionFailedException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException FAIL([HOptional] string messageFmt,params object[] objects)
{
   return RaiseError( Assembly.GetCallingAssembly(), string.Format(messageFmt,objects) );
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
[MethodImpl(MethodImplOptions.NoInlining)]
public static StackTrace PrepareStackTraceForDispatcherCall(Assembly callingAssembly)
{
   return PrepareStackTraceForDispatcherCall( callingAssembly, 1 );
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
[MethodImpl(MethodImplOptions.NoInlining)]
[HOptionalResult]
public static StackTrace PrepareStackTraceForDispatcherCall( Assembly callingAssembly,
                                                             int skipFramesInStack )
{
   HProgram.VerifyNotInMainThread();
   HArgChecking.VerifyNotNull( callingAssembly );
   if ( skipFramesInStack < 0 )
      throw new ArgumentOutOfRangeException( "skipFramesInStack" );
   return GetCallerExceptionsUsing( callingAssembly ) ? null :
      new StackTrace( skipFramesInStack + 1, SaveFileInfoForDispatcherCallerStack );
}

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

/// <exception cref="InvalidOperationException" />
protected override void OnDispose(bool synchronous)
{
   HProgram.VerifyInMainThread();
   using (new HMemoryBarrier())
      _DispatcherCallerStackTrace_ = _DispatcherCallerPreviousStack_;
}

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

/// <exception cref="HAssertionFailedException" />
[MethodImpl(MethodImplOptions.NoInlining)]
internal static HFakeException RaiseError(Assembly assembly,string message=null)
{
   if ( GetCallerExceptionsUsing( Assembly.GetCallingAssembly() ) )
      throw message != null ?
         new HAssertionFailedException( message ) :
         new HAssertionFailedException();
   StackTrace dispatcherCallerStackTrace = null;
   if ( HProgram.IsInMainThread )
      using (new HMemoryBarrier())
         dispatcherCallerStackTrace = _DispatcherCallerStackTrace_;
   HFatalError.RaiseEx( assembly,
      Verbose.SOURCE_CODE_ASSERTION_FAILURE_Cause, message, null,
      new StackTrace( 2, true ), dispatcherCallerStackTrace );
   return null;
}

[HThreadStatic]
internal static void SetUseExceptionsContext(bool? value)
{
   _UseExceptionsContext_ = value;
}

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

static HAssertion()
{
   CommonHelpers_Core.Access();
}

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

[HThreadStatic]
static bool GetCallerExceptionsUsing(Assembly callingAssembly)
{
   return ExceptionsUsing ??
      CommonHelpers_Info.IsDebugConfiguration ||
      HAssembly.IsConfiguredForDebug( callingAssembly );
}

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

readonly StackTrace
   _DispatcherCallerPreviousStack_;

static volatile int
   _UseExceptions_ = 1;
static volatile bool
   _SaveFileInfoForDispatcherCallerStack_ = _SaveFileInfoForDispatcherCallerStackDefault_;
static StackTrace
   _DispatcherCallerStackTrace_;
[ThreadStatic]
static bool?
   _UseExceptionsContext_;

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

const bool
   _SaveFileInfoForDispatcherCallerStackDefault_ = true;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HAssertion
//**************************************************************************************************

} // CommonHelpers (.WASM)
