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

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

//**************************************************************************************************
class FatalMBoxExchange {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public string? Text;
public string? Caption;
public uint Type;
public int Result;
public Exception? Exception;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // FatalMBoxExchange
//**************************************************************************************************


//**************************************************************************************************
/// <summary>
/// Fatal termination implementation
/// </summary>
public static partial class fatal_impl {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[u.doesnt_return]
public static void RaisePreparationFail( Exception e ) =>
   u.fatal.RaiseError( "Raise preparation has failed with exception.", e );

public static u.EFatalTerminationError DeadBranchError() =>
   new EFatalTerminationError( "Fatal error implementation entered in a some dead branch." );

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

public static void TraceError( string reportText )
{
   var sb = new StringBuilder( Environment.NewLine );
   sb.Append( TXT( reportText ) );
   TraceLn( sb.ToString() );
}

public static void TraceReportFilePath( string filePath ) =>
   TraceLn( TXT( $"(Report-file: “{filePath}”.)\n" ) );

static void TraceLn( string str )
{
   string outputText =
      AsAnsi( str + Environment.NewLine );
   try {
      //if ( IsWin32 )
      if ( False )
         w32_core.OutputDebugString( outputText );
      else {
         Trace.IndentLevel = Trace.IndentSize = 0;
         Trace.Write( outputText );
         Trace.Flush();
      }
   }
   catch {}
}

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

/// <exception cref="u.EFatalTerminationError" />
public static void PrintToConsole( string reportText ) =>
   PrintToConsole( true, reportText );
/// <exception cref="u.EFatalTerminationError" />
public static void PrintToConsole( [u.def_true] bool insertNewLine, string reportText )
{
   reportText = AppendText(
      new StringBuilder( insertNewLine ? Environment.NewLine : "" ),
      false, reportText ).ToString();
   try {
      var saveFGColor = Console.ForegroundColor;
      Console.ForegroundColor = ConsoleColor.Red;
      try {
         Console.WriteLine( reportText );
      }
      finally {
         Console.ForegroundColor = saveFGColor;
      }
   }
   catch ( Exception e ) {
      throw e_fatal_termination_error.New(
         "Can not write to console.\n"+
         "{0}", e.Message );
   }
}

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

/// <exception cref="u.EFatalTerminationError" />
public static bool MessageDialog( u.FatalAttributes attr, u.FatalErrorArgs parameters )
{
   var mbExchange = new FatalMBoxExchange() {
      Text = MakeReportText( attr, parameters, false, true ) +
         "\n\nDO YOU WISH TO VIEW DETAILED REPORT?",
      Caption = attr.AppSimpleName+": Fatal Error",
      Type =
         w32_core.MB_ICONERROR |
         w32_core.MB_YESNO |
         w32_core.MB_SYSTEMMODAL
   };
   var thread = new Thread( MessageBoxThreadRoutine );
   thread.Priority = ThreadPriority.AboveNormal;
   thread.Start( mbExchange );
   thread.Join();
   var exception = mbExchange.Exception;
   if ( exception != null )
      throw e_fatal_termination_error.New( exception );
   bool showHtmlReport = mbExchange.Result == w32_core.IDYES;
   // May be it'll prevent prevent report content desappearance when browser opens instantly:
   if ( showHtmlReport )
      System.Threading.Thread.Sleep( 100 );
   return showHtmlReport;
}

/// <exception cref="u.EFatalTerminationError" />
public static void ShowError( u.FatalAttributes attr, string errorMessage )
{
   Try(() => w32_core.Beep( 800, 200 ));
   var mbExchange = new FatalMBoxExchange() {
      Text = TXT( errorMessage ),
      Caption = attr.AppSimpleName+": Crash",
      Type = w32_core.MB_ICONERROR | w32_core.MB_SYSTEMMODAL
   };
   var thread = new Thread( MessageBoxThreadRoutine );
   thread.Priority = ThreadPriority.AboveNormal;
   thread.Start( mbExchange );
   thread.Join();
   var exception = mbExchange.Exception;
   if ( exception != null )
      throw e_fatal_termination_error.New( exception );
}

static void MessageBoxThreadRoutine( object? data )
{
   var mbExchange = (FatalMBoxExchange)NN( data );
   try {
      Thread.CurrentThread.CurrentCulture =
         Thread.CurrentThread.CurrentUICulture =
            CultureInfo.InvariantCulture;
      mbExchange.Result = w32_core.MessageBox( IntPtr.Zero,
         mbExchange.Text, mbExchange.Caption, mbExchange.Type );
   }
   catch ( Exception e ) {
      mbExchange.Exception = e;
   }
}

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

public static Exception? Try( Action action )
{
   try { action(); }
   catch (Exception e) { return e; }
   return null;
}

public static string ComposeExceptionMessage( Exception e, bool withStackTrace=false )
{
   var sb = new StringBuilder();
   sb.Append( e.GetType() );
   sb.Append( ": " );
   sb.Append( e.Message );
   if ( withStackTrace ) {
      string? stackTraceText = e.StackTrace;
      if ( stackTraceText != null ) {
         sb.AppendLine();
         sb.Append( stackTraceText );
      }
   }
   return TXT( sb.ToString() );
}

public static string MakeExceptionText( Exception e, bool withStackTrace=false )
{
   var sb = new StringBuilder();
   for ( Exception? x=e; x!=null; x=GetFlattenedInnerException( x ) ) {
      if ( x != e )
         sb.AppendLine();
      if ( e.InnerException != null )
         sb.Append( "\x2022 " );
      sb.Append( ComposeExceptionMessage( x, withStackTrace ) );
   }
   return sb.ToString();
}

public static Exception? GetFlattenedInnerException( Exception e )
{
   var x = e.InnerException;
   return (x is AggregateException ea) ? ea.Flatten() : x;
}

public static string StackTraceToText( StackTrace stackTrace )
{
   string str = stackTrace.ToString();
   int len = str.Length, l = len;
   l -= str.EndsWith( "\r\n" ) ? 2 :
      str.EndsWith( '\n' ) || str.EndsWith( '\r' ) ? 1 : 0;
   return TXT( l<len ? str.Remove( l ) : str );
}

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

public static string AsAnsi( string str )
{
   IntPtr ipAnsiStr = default;
   try {
      return NN( Marshal.PtrToStringAnsi(
         ipAnsiStr = Marshal.StringToHGlobalAnsi( str ) ));
   }
   catch {
      return str;
   }
   finally {
      if ( ipAnsiStr != default )
         try { Marshal.FreeHGlobal( ipAnsiStr ); }
         catch {}
   }
}

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

public static bool IsWin32 =>
   Environment.OSVersion.Platform == PlatformID.Win32NT;

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

public static bool True => true;
public static bool False => false;

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

/// <exception cref="NullReferenceException" />
public static T NN<T>( T? instance )
where T: class =>
   instance ?? throw new NullReferenceException();

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

public static bool HasNN( params object?[] values )
{
   foreach ( var v in values )
      if ( v != null )
         return true;
   return false;
}

public static bool HasNull( params object?[] values )
{
   foreach ( var v in values )
      if ( v == null )
         return true;
   return false;
}

public static bool NotNulls( params object?[] values ) =>
   values.Length > 0 && !HasNull( values );
public static bool AllNulls( params object?[] values ) =>
   values.Length > 0 && !HasNN( values );

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // fatal_impl
//**************************************************************************************************

} // user_rtx (.wasm)
