//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.IO;
using System.Threading;
using System.Reflection;
using System.Diagnostics;

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

public delegate void FatalRoutine_0( u.FatalAttributes attr, u.FatalErrorArgs parameters );
public delegate int? FatalRoutine( u.FatalAttributes attr, u.FatalErrorArgs parameters );

public delegate void EmergencyCallback_0( u.FatalAttributes attr,
                                          string reportText, string? reportTextWithStack );
public delegate void EmergencyCallback( u.FatalAttributes attr,
                                        string reportText, string? reportTextWithStack,
                                        ref int? terminationCode );

//**************************************************************************************************
/// <summary>
/// Fatal termination settings (configuration)
/// </summary>
public class FatalCfg {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public const int DefaultTerminationCodeValue = -1;

public u.FatalRoutine_0? FatalRoutine_0 { get; set; }
public u.FatalRoutine? FatalRoutine { get; set; }

public EmergencyCallback_0? EmergencyCallback_0;
public EmergencyCallback? EmergencyCallback;

public bool AllowProcessTermination { get; set; }

public string? MainModuleName { get; set; }
public string? AppSimpleName { get; set; }
public Thread? MainThread { get; set; }

public string? ReportRootFolder { get; set; }
[u.def_true]
public bool AddDateSubfolder { get; set; } = true;
public Func<string>? ReportPathMaker { get; set; }

public bool UseMessageDialog { get; set; }
[u.def_true]
public bool ShowHtmlReport { get; set; } = true;
[u.def_true]
public bool ReportToConsole { get; set; } = true;
[u.def( DefaultTerminationCodeValue )]
public int? DefaultTerminationCode { get; set; } = DefaultTerminationCodeValue;

public void SetMainThreadValue() =>
   MainThread = Thread.CurrentThread;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // FatalCfg
//**************************************************************************************************


//**************************************************************************************************
/// <summary>
/// Fatal error parameters
/// </summary>
public class FatalErrorArgs {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public Assembly SourceAssembly { get; set; }
public string CauseOrCategory { get; set; }
public string? ErrorMessage { get; set; }
public StackTrace? StackTrace { get; set; }
public StackTrace? DispatcherCallerStack { get; set; } = fatal.DispatcherCallerCurrentStack;
public Exception? Exception { get; set; }

public FatalErrorArgs( Assembly sourceAssembly, string causeOrCategory )
{
   SourceAssembly = sourceAssembly;
   CauseOrCategory = causeOrCategory;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // FatalErrorArgs
//**************************************************************************************************


//**************************************************************************************************
/// <summary>
/// Fatal termination attributes (auxiliary group of values)
/// </summary>
public class FatalAttributes {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public u.FatalRoutine_0? FatalRoutine_0 { get; }
public u.FatalRoutine? FatalRoutine { get; }

public EmergencyCallback_0? EmergencyCallback_0;
public EmergencyCallback? EmergencyCallback;

public bool AllowProcessTermination { get; }

public string MainModuleName { get; }
public string AppSimpleName { get; }
public int? ProcessId { get; }

public string? ReportRootFolder { get; }
public bool AddDateSubfolder { get; }
public Func<string>? ReportPathMaker { get; }

public bool UseMessageDialog { get; }
public bool ShowHtmlReport { get; }
public bool ReportToConsole { get; }

public int ManagedThreadId { get; }
public bool IsInMainThread { get; }
public bool IsInSecondaryThread { get; }
public int? DefaultTerminationCode { get; }

public DateTime ErrorTime { get; } = DateTime.Now;

public string? ReportText { get; internal set; }
public string? ReportFilePath { get; internal set; }

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

public FatalAttributes()
{
   var settings = u.fatal.Settings;
   FatalRoutine_0 = settings.FatalRoutine_0;
   FatalRoutine = settings.FatalRoutine;
   EmergencyCallback_0 = settings.EmergencyCallback_0;
   EmergencyCallback = settings.EmergencyCallback;
   AllowProcessTermination = settings.AllowProcessTermination;
   Process? process = null;
   try { process = Process.GetCurrentProcess(); }
   catch {}
   if ( process != null )
      ProcessId = process.Id;
   if ( settings.MainModuleName != null )
      MainModuleName = settings.MainModuleName;
   else {
      var processModuleName = process?.MainModule?.ModuleName;
      if ( processModuleName != null )
         MainModuleName = processModuleName;
      else {
         var entryAssembly = Assembly.GetEntryAssembly();
         MainModuleName = entryAssembly != null ?
            entryAssembly.ManifestModule.Name :
            "???";
      }
   }
   AppSimpleName = settings.AppSimpleName ?? Path.GetFileNameWithoutExtension( MainModuleName );
   ReportPathMaker = settings.ReportPathMaker;
   AddDateSubfolder = settings.AddDateSubfolder;
   UseMessageDialog = settings.UseMessageDialog && fatal_impl.IsWin32;
   ShowHtmlReport = settings.ShowHtmlReport;
   ReportToConsole = settings.ReportToConsole;
   ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
   IsInMainThread = Thread.CurrentThread == settings.MainThread;
   IsInSecondaryThread = settings.MainThread != null &&
      Thread.CurrentThread != settings.MainThread;
   DefaultTerminationCode = settings.DefaultTerminationCode;
   if ( ReportToConsole && fatal_impl.IsWin32 &&
        w32_core.GetStdHandle( w32_core.STD_OUTPUT_HANDLE ) == w32_core.INVALID_HANDLE_VALUE )
      ReportToConsole = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // FatalAttributes
//**************************************************************************************************

} // user_rtx (.wasm)
