//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// AFXUI auxiliary module for Blazor-application (WASM) | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.JSInterop;

using CommonHelpers.WASM;

namespace AFXUI {

//**************************************************************************************************
public static partial class UFatalBreakdown {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//static IJSRuntime JSRT => ServicesAFX.JSRuntime;
//==================================================================================================

public static bool IsInFatalError =>
   Thread.VolatileRead(ref _IsInFatalError_) > 0;

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

[HReturnsFalse]
public static bool ProcessError( string message ) =>
   DoProcessError( message, null );
[HReturnsFalse]
public static bool ProcessErrorFmt( string messageFmt, params object?[] parameters ) =>
   ProcessError( HString.FormatAsText( messageFmt, parameters ) );

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

public static Exception ProcessUnhandledException( Exception e ) =>
   ProcessUnhandledException( null, e );
public static Exception ProcessUnhandledException( string? exceptionCause, Exception e )
{
   DoProcessError( exceptionCause, e );
   return e;
}

public static Exception ProcessJSInvocationError( Exception e ) =>
   ProcessUnhandledException( JSInvocationErrorCause, e );

/// <exception cref="Exception" />
public static Exception ProcessRenderingError( Type componentType, Exception e )
{
   DoProcessError( GetRenderingErrorCause( componentType ), e, true );
   throw HAssertionFailure.DEAD_BRANCH();
}

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

public static Task<HFakeException> ProcessUnhandledException_A( Exception e ) =>
   ProcessUnhandledException_A( UnhandledExceptionCause, e );
public static async Task<HFakeException> ProcessUnhandledException_A( string? exceptionCause,
                                                                      Exception e )
{
   DoProcessError( exceptionCause ?? UnhandledExceptionCause, e );
   throw await HAsyncAwait.Hang_A();
}

public static Task<HFakeException> ProcessJSInvocationError_A( Exception e ) =>
   ProcessUnhandledException_A( JSInvocationErrorCause, e );

/// <exception cref="Exception" />
public static async Task<HFakeException> ProcessRenderingError_A( Type componentType, Exception e )
{
   DoProcessError( GetRenderingErrorCause( componentType ), e, true );
   throw await HAsyncAwait.Hang_A();
}

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

public static void Do(Action operation)
{
   try { operation(); }
   catch (Exception e) {
      ProcessUnhandledException(e); }
}
public static R Do<R>(Func<R> function)
{
   try { return function(); }
   catch (Exception e) {
      throw ProcessUnhandledException(e); }
}

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

public static async Task Do_A(Func<Task> operationA)
{
   try { await operationA(); }
   catch (Exception e) {
      await ProcessUnhandledException_A(e); }
}

public static async Task<R> Do_A<R>(Func<Task<R>> functionA)
{
   try { return await functionA(); }
   catch (Exception e) {
      throw await ProcessUnhandledException_A(e); }
}
public static async ValueTask<R> Do_A<R>(Func<ValueTask<R>> functionA)
{
   try { return await functionA(); }
   catch (Exception e) {
      throw await ProcessUnhandledException_A(e); }
}

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

static string ErrorMessageOfUndefined(string? message) =>
   UBreakdown.ErrorMessageOfUndefined( message );

static string ComposeErrorMessage(string? exceptionCause,Exception e,bool showDetails=false) =>
   UBreakdown.ComposeErrorMessage( exceptionCause, e, showDetails );

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

[HReturnsFalse]
internal static bool DoProcessError( string? errorCauseOrErrorMessage,
                                     Exception? e, bool isRenderingError=false )
{
   if ( Interlocked.Exchange(ref _IsInFatalError_,1) > 0 )
      return false;
   try
   {
      string debugMessage = e != null ?
         ComposeErrorMessage( errorCauseOrErrorMessage, e, UProgram.IsDebugConfiguration ) :
         ErrorMessageOfUndefined( errorCauseOrErrorMessage );
      try { UConsole.PrintError( debugMessage ); }
      catch { Console.WriteLine( debugMessage ); }
      string popupMessage = e != null ?
         ComposeErrorMessage( errorCauseOrErrorMessage, e ) :
         ErrorMessageOfUndefined( errorCauseOrErrorMessage );
      UJS.Invoke( "FatalBreakdown_ShowWasmErrorReloadSPA", popupMessage );
   }
   catch
   {
      HFatalError.Raise( ExceptionInBreakdownCause, e );
   }
/*
   if ( isRenderingError && e!=null )
      throw e;
*/
   throw HFatalError.Raise( "SEE REPORT ABOVE (EXCEPTION DETAILS)." );
}

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

[JSInvokable]
public static void wasm_FatalBreakdown_BlockSPA() =>
   Interlocked.Exchange(ref _IsInFatalError_,1);

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

static string UnhandledExceptionCause =>
   UBreakdown.UnhandledExceptionCause;
static string ExceptionInBreakdownCause =>
   UBreakdown.ExceptionInBreakdownCause;
static string PageScriptErrorCause =>
   UBreakdown.PageScriptErrorCause;
static string JSInvocationErrorCause =>
   UBreakdown.JSInvocationErrorCause;
static string GetRenderingErrorCause(Type componentType) =>
   UBreakdown.GetRenderingErrorCause( componentType );

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

static int _IsInFatalError_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UFatalBreakdown
//**************************************************************************************************

} // AFXUI
