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

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

//**************************************************************************************************
partial class HFatalError {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static HFatalErrorFlags Flags
{
   get
   {
      lock (_SyncRoot_)
         return _Flags_;
   }
   set
   {
      lock (_SyncRoot_)
      {
         AvoidRaiseOnUnhandledExceptionUnderDebugger =
            (value & HFatalErrorFlags.AvoidRaiseOnUnhandledExceptionUnderDebugger) != 0;
         RaiseOnUnhandledException = (value & HFatalErrorFlags.RaiseOnUnhandledException) != 0;
         _Flags_ = value;
      }
   }
}

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

public static bool RaiseOnUnhandledException
{
   get
   {
      lock (_SyncRoot_)
         return _RaiseOnUnhandledException_;
   }
   set
   {
      lock (_SyncRoot_)
         if (_RaiseOnUnhandledException_!=value)
         {
            if (value)
               AppDomain.CurrentDomain.UnhandledException +=
                  AppDomainUnhandledException_Handler;
            else
               AppDomain.CurrentDomain.UnhandledException -=
                  AppDomainUnhandledException_Handler;
            _RaiseOnUnhandledException_ = value;
         }
   }
}

public static bool AvoidRaiseOnUnhandledExceptionUnderDebugger
{
   get
   {
      lock (_SyncRoot_)
         return _AvoidRaiseOnUnhandledExceptionUnderDebugger_;
   }
   set
   {
      lock (_SyncRoot_)
         _AvoidRaiseOnUnhandledExceptionUnderDebugger_ = value;
   }
}

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

public static event HFatalErrorEmergencyCallback EmergencyHandler
{
   add    { lock (_SyncRoot_) _EmergencyHandler_ += value; }
   remove { lock (_SyncRoot_) _EmergencyHandler_ -= value; }
}

public static event HFatalErrorEmergencyCallback EmergencyFinalHandler
{
   add    { lock (_SyncRoot_) _EmergencyFinalHandler_ += value; }
   remove { lock (_SyncRoot_) _EmergencyFinalHandler_ -= value; }
}

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

public static event Action RaiseTrigger
{
   add    { lock (_SyncRoot_) _RaiseTrigger_ += value; }
   remove { lock (_SyncRoot_) _RaiseTrigger_ -= value; }
}

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

/// <exception cref="HOperationFailedException" />
public static string ReportFolderPath
{
   get { lock (_SyncRoot_) return GetReportFolderPath(); }
}

public static string ReportFolderPathSpecification
{
   get { lock (_SyncRoot_) return _ReportFolderPath_; }
}

/// <exception cref="HOperationFailedException" />
public static void SpecifyReportFolderPath([HOptional] string value)
{
   lock (_SyncRoot_)
   {
      _ReportFolderFullPath_ = value!=null ? GetReportFolderFullPath( value ) : null;
      _ReportFolderPath_ = value;
   }
}

/// <exception cref="HOperationFailedException" />
public static void PrepareReportFolder()
{
   PrepareReportFolder( ReportFolderPath );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public static void PrepareReportFolder(string path)
{
   lock (_SyncRoot_)
   {
      HArgChecking.VerifyNotNull( path );
      _ReportFolderFullPath_ = CreateReportDirectory( GetReportFolderFullPath( path ) );
      _ReportFolderPath_ = path;
   }
}

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

public static bool IsInTerminationThread
{
   get { return _IsInTerminationThread_; }
}

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

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(string message)
{
   return RaiseEx(Assembly.GetCallingAssembly(),null,message,null,new StackTrace(1,true));
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise([HOptional] string cause,string message)
{
   return RaiseEx(Assembly.GetCallingAssembly(),cause,message,null,new StackTrace(1,true));
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise( [HOptional] string cause,
                                    string messageFmt, params object[] parameters )
{
   return RaiseEx( Assembly.GetCallingAssembly(),
      cause, string.Format(messageFmt,parameters), null,
      new StackTrace(1,true) );
}

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

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(int skipFrames,string message)
{
   return RaiseEx( Assembly.GetCallingAssembly(), null, message,
                   null, new StackTrace( skipFrames+1, true ) );
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(int skipFrames,[HOptional] string cause,string message)
{
   return RaiseEx( Assembly.GetCallingAssembly(), cause, message,
                   null, new StackTrace( skipFrames+1, true ) );
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise( int skipFrames, [HOptional] string cause,
                                    string messageFmt, params object[] parameters )
{
   return RaiseEx( Assembly.GetCallingAssembly(),
      cause, string.Format(messageFmt,parameters), null,
      new StackTrace(skipFrames+1,true) );
}

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

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(string cause,Exception e)
{
   return RaiseEx( Assembly.GetCallingAssembly(), cause, null, e, null );
}

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

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(Assembly assembly,string message)
{
   return RaiseEx( assembly, null, message, null, new StackTrace(1,true) );
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(Assembly assembly,[HOptional] string cause,string message)
{
   return RaiseEx( assembly, cause, message, null, new StackTrace(1,true) );
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise( Assembly assembly,
                                    [HOptional] string cause,
                                    string messageFmt,
                                    params object[] parameters )
{
   return RaiseEx( assembly, cause, string.Format(messageFmt,parameters),
                   null, new StackTrace(1,true) );
}

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

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(Assembly assembly,int skipFrames,string message)
{
   return RaiseEx( assembly, null, message, null, new StackTrace( skipFrames+1, true ) );
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise( Assembly assembly, int skipFrames,
                                    [HOptional] string cause, string message )
{
   return RaiseEx( assembly, cause, message, null, new StackTrace( skipFrames+1, true ) );
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise( Assembly assembly, int skipFrames, 
                                    [HOptional] string cause, string messageFmt,
                                    params object[] parameters )
{
   return RaiseEx( assembly,
      cause, string.Format(messageFmt,parameters), null,
      new StackTrace(skipFrames+1,true) );
}

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

[MethodImpl(MethodImplOptions.NoInlining)]
public static HFakeException Raise(Assembly assembly,string cause,Exception e)
{
   return RaiseEx( assembly, cause, null, e, null );
}

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

public static bool IsExceptionFatal([HOptional] Exception e)
{
   return e != null && (e is HFatalErrorException || e.Data[_FatalErrorExceptionKey_] != null);
}

public static bool IsFatalErrorExceptionChain([HOptional] Exception e)
{
   if ( e == null )
      return false;
   for (; e!=null; e=e.InnerException)
      if ( IsExceptionFatal(e) )
         return true;
   return false;
}

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

public static void ProcessUnhandledException(Exception e)
{
   if ( _AvoidRaiseOnUnhandledExceptionUnderDebugger_ && Debugger.IsAttached )
      return;
   if ( !IsFatalErrorExceptionChain(e) )
   {
      Thread mainThread =
         HProgram.MainThreadSpecification;
      Raise
      (
         mainThread!=null ?
            Thread.CurrentThread==mainThread ?
               "Unhandled exception has occured in main program thread." :
               "Unhandled exception has occured in secondary program thread." :
            "Unhandled exception has occured in the program.",
         e
      );
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HFatalError
//**************************************************************************************************

} // CommonHelpers (.WASM)
