//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Reflection;
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>
/// Assertion (static class)
/// </summary>
public static class assertion {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static string FailureCategoryName =>
   "Source code assertion has failed.";

[u.volatile_access]
public static bool UseFatalError {
   get => _useFatalError;
   set => _useFatalError = value; }

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

/// <exception cref="u.EAssertionFailed" />
[u.doesnt_return][u.calling_assembly][MethodImpl( MethodImplOptions.NoInlining )]
public static u.EFake FAIL( string message ) =>
   Raise( Assembly.GetCallingAssembly(), message );

/// <exception cref="u.EAssertionFailed" />
[u.doesnt_return][u.calling_assembly][MethodImpl( MethodImplOptions.NoInlining )]
public static u.EFake FAIL( string messageFmt, params object?[] args ) =>
   Raise( Assembly.GetCallingAssembly(), messageFmt, args );

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

/// <exception cref="u.EAssertionFailed" />
[u.returns_source][u.calling_assembly][MethodImpl( MethodImplOptions.NoInlining )]
public static bool Verify( bool condition )
{
   if ( !condition )
      assertion_fail.Raise( Assembly.GetCallingAssembly(),
         "Condition verification has failed." );
   return condition;
}

/// <exception cref="u.EAssertionFailed" />
[u.calling_assembly][MethodImpl( MethodImplOptions.NoInlining )]
public static void Deny( bool unacceptableCondition )
{
   if ( unacceptableCondition )
      assertion_fail.Raise( Assembly.GetCallingAssembly(),
         "Unacceptable condition has been denied." );
}

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

/// <exception cref="u.EAssertionFailed" />
[u.doesnt_return][MethodImpl( MethodImplOptions.NoInlining )]
static u.EFake Raise( Assembly sourceAssembly, string message ) =>
   Raise( sourceAssembly, 1, message );

/// <exception cref="u.EAssertionFailed" />
[u.doesnt_return][MethodImpl( MethodImplOptions.NoInlining )]
static u.EFake Raise( Assembly sourceAssembly, string messageFmt, params object?[] args ) =>
   Raise( sourceAssembly, 1, messageFmt, args );

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

/// <exception cref="u.EAssertionFailed" />
[u.doesnt_return][MethodImpl( MethodImplOptions.NoInlining )]
internal static u.EFake Raise( Assembly sourceAssembly,
                               [u.not_negative] int skipFrames,
                               string message )
{
   if ( UseFatalError )
      u.fatal.RaiseError( sourceAssembly, u.fatal.VerifyStackFrames( skipFrames ) + 1,
         u.assertion.FailureCategoryName, message );
   throw e_assertion_failed.New( message );
}

/// <exception cref="u.EAssertionFailed" />
[u.doesnt_return][MethodImpl( MethodImplOptions.NoInlining )]
internal static u.EFake Raise( Assembly sourceAssembly, [u.not_negative] int skipFrames,
                               string messageFmt, params object?[] args )
{
   if ( UseFatalError )
      u.fatal.RaiseError( sourceAssembly, u.fatal.VerifyStackFrames( skipFrames ) + 1,
         u.assertion.FailureCategoryName, messageFmt, args );
   throw e_assertion_failed.New( messageFmt, args );
}

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

static volatile bool _useFatalError;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // assertion

} // user_rtx (.wasm)
