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

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

/// <summary>
/// System-error exception class (Win32, COM etc.)
/// </summary>
public class ESystemError: Exception {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public SystemErrorKind ErrorKind { get; }
public Exception? OriginalException { get; }
public UInt64? ErrorCode64 { get; }

public bool HasErrorKind =>
   ErrorKind != 0;
public bool HasErrorCode =>
   ErrorCode64.HasValue;

public bool IsWin32Error =>
   ErrorKind == SystemErrorKind.Win32;
public bool IsCOMError =>
   ErrorKind == SystemErrorKind.COM;

public UInt32? Win32ErrorCode =>
   IsWin32Error ? (UInt32?)VerifyErrorCodeWin32COM() : null;
public Int32? COMErrorCode =>
   IsCOMError ? unchecked( (Int32?)VerifyErrorCodeWin32COM() ) : null;

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

public ESystemError()
   :base() {}

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

public ESystemError( string message )
   :base( message.TXT() ) {}

public ESystemError( string message, Exception innerException )
   :base( message.TXT(), innerException ) {}

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

public ESystemError( SystemErrorKind errorKind, string message )
: this( message )
{
   ErrorKind = errorKind;
}

public ESystemError( UInt64 errorCode64, string message )
: this( message )
{
   ErrorCode64 = errorCode64;
}

public ESystemError( SystemErrorKind errorKind, UInt64 errorCode64, string message )
: this( message )
{
   ErrorKind = errorKind;
   ErrorCode64 = VerifyErrorArgsWin32COM( errorKind, errorCode64 );
}

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

public ESystemError( Exception originalException )
: this( originalException.Message )
{}

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

public ESystemError( SystemErrorKind errorKind )
: this()
{
   ErrorKind = errorKind;
}

public ESystemError( SystemErrorKind errorKind, Exception originalException )
: this( errorKind, originalException.Message )
{}

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

public ESystemError( UInt64 errorCode64 )
: this()
{
   ErrorCode64 = errorCode64;
}

public ESystemError( UInt64 errorCode64, Exception originalException )
: this( errorCode64, originalException.Message )
{}

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

/// <exception cref="ArgumentException" />
public ESystemError( SystemErrorKind errorKind, UInt64 errorCode64 )
: this()
{
   ErrorKind = errorKind;
   ErrorCode64 = VerifyErrorArgsWin32COM( errorKind, errorCode64 );
}

/// <exception cref="ArgumentException" />
public ESystemError( SystemErrorKind errorKind, UInt64 errorCode64, Exception originalException )
: this( errorKind, VerifyErrorArgsWin32COM( errorKind, errorCode64 ), originalException.Message )
{}

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

/// <exception cref="InvalidOperationException" />
public virtual UInt64? VerifyErrorCode() =>
   VerifyErrorCodeWin32COM();

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

/// <exception cref="InvalidOperationException" />
public UInt64? VerifyErrorCodeWin32COM()
{
   if ( u.ops.IsOneOf( ErrorKind, u.SystemErrorKind.Win32, u.SystemErrorKind.COM ) &&
        ErrorCode64.HasValue && u.uint64.HighPart( ErrorCode64.Value ) != 0 )
      throw e_invalid_operation.New(
         "System error has incorrect code value (more than 32 bits) for {0} error kind.",
         ErrorKind == u.SystemErrorKind.Win32 ? "Win32" : "COM" );
   return ErrorCode64;
}

/// <exception cref="ArgumentException" />
public static UInt64 VerifyErrorArgsWin32COM( SystemErrorKind errorKind, UInt64 errorCode64 )
{
   if ( u.ops.IsOneOf( errorKind, u.SystemErrorKind.Win32, u.SystemErrorKind.COM ) &&
        u.uint64.HighPart( errorCode64 ) != 0 )
      throw e_argument.NewWith( nameof(errorCode64),
         "Incorrect error code value (more than 32 bits) for {0} error kind.",
         errorKind == u.SystemErrorKind.Win32 ? "Win32" : "COM" );
   return errorCode64;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // ESystemError

} // user_rtx (.wasm)
