// NativeLib DLL (for DotNetHelpers) | C+- (SysRoutine) | Author: Sergei Y.Kitáev
#include "Precompiled.h"
#include "SharedIncludes.h"
namespace NativeLib {
//==================================================================================================

bool NLFailure::HasError()
{
   return $BOOL( Category() );
}

NLErrorCat NLFailure::Category()
{
   return DataPtr()->Category;
}

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

bool NLFailure::IsGeneralError()
{
   return Category() == NLErrorCat_General;
}
bool NLFailure::IsArgumentError()
{
   return Category() == NLErrorCat_Argument;
}
bool NLFailure::IsOutOfMemoryError()
{
   return Category() == NLErrorCat_OutOfMemory;
}
bool NLFailure::IsSystemError()
{
   return Category() == NLErrorCat_System;
}
bool NLFailure::IsCOMError()
{
   return Category() == NLErrorCat_COM;
}
bool NLFailure::IsSpecialError()
{
   return Category() == NLErrorCat_Special;
}

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

bool NLFailure::ResetError() $ret_true
{
   return ResetError( true );
}

bool NLFailure::ResetError(bool threadInitVerification) $ret_true
{
   NLError *dataPtr;
   if ( threadInitVerification )
      dataPtr = DataPtr();
   else
   {
      dataPtr = $tls::GetPointer<NLError>($, &LIB::error_pointer_tls );
      $VERIFY_POINTER( dataPtr );
   }
   NLError &data = *dataPtr;
   if ( data.IsAllocatedMsg )
      $Memory::Free( data.MessagePtr );
   data = {};
   data.MessagePtr = (PWSTR)$TEXT(
      L"No error.",
      L"Нет ошибки" );
   return true;
}

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

bool NLFailure::SetArgumentError() $ret_false
{
   NLError &data = *FreeErrorData();
   data.Category = NLErrorCat_Argument;
   data.MessagePtr = (PWSTR)$TEXT(
      L"Incorrect argument(s).",
      L"Некорректные аргумент(ы)." );
   return false;
}
bool NLFailure::SetArgumentError(PCWSTR message $optional) $ret_false
{
   return SetError( NLErrorCat_Argument, message, 0, 0 );
}

bool NLFailure::SetOutOfMemoryError() $ret_false
{
   NLError &data = *FreeErrorData();
   data.Category = NLErrorCat_OutOfMemory;
   data.MessagePtr = (PWSTR)$TEXT(
      L"Out of memory for the operation.",
      L"Недостаточно памяти для выполнения операции." );
   return false;
}

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

bool NLFailure::SetSpecialError( PCWSTR message )
{
   return SetError( NLErrorCat_Special, message, 0, 0 );
}
bool NLFailure::SetSpecialError( PCWSTR message $optional, INT code $optional ) $ret_false
{
   return SetError( NLErrorCat_Special, message, code, 0 );
}
bool NLFailure::SetSpecialError( PCWSTR message $optional, UINT uCode $optional ) $ret_false
{
   return SetError( NLErrorCat_Special, message, uCode, 0 );
}
bool NLFailure::SetSpecialError( PCWSTR message $optional, INT code $optional,
                                 DWORD value $optional ) $ret_false
{
   return SetError( NLErrorCat_Special, message, code, value );
}
bool NLFailure::SetSpecialError( PCWSTR message $optional, UINT uCode $optional,
                                 DWORD value $optional ) $ret_false
{
   return SetError( NLErrorCat_Special, message, uCode, value );
}

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

bool NLFailure::SetSystemErrorEx( PCWSTR headerMessage $optional,
                                  DWORD dwError, bool isCOMError ) $ret_false
{
   $DynText errorMessage; bool isMIDNotFoundError, msgSucceeded;
   if ( dwError == $COM_UNRECOGNIZED_ERROR && isCOMError )
   {
      errorMessage = $DynText::CreateStatic($, $TEXT(
         L"Unrecognized COM-error.",
         L"Неопознанная ошибка COM." ));
      msgSucceeded = true;
   }
   else
      msgSucceeded = $Win32Error::GetErrorMessage($, dwError, errorMessage, isMIDNotFoundError );
   NLError &data = *FreeErrorData();
   data.Category = msgSucceeded ?
      isCOMError ? NLErrorCat_COM : NLErrorCat_System :
      isMIDNotFoundError ? NLErrorCat_Special : NLErrorCat_System;
   data.Code32 = msgSucceeded ? dwError : 0;
   if ( !headerMessage )
   {
      data.IsAllocatedMsg = errorMessage.NeedsFree($);
      data.MessagePtr = errorMessage.TakePointerOwnership($);
      return false;
   }
   if (!( data.MessagePtr =
          $SZ::FormatAllocNS($, L"%s\r\n%s", headerMessage, errorMessage.T($) ) ))
      return SetFormatAllocFailure();
   data.IsAllocatedMsg = true;
   return false;
}

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

bool NLFailure::SetSystemError(DWORD dwError) $ret_false
{
   return SetSystemError( nullptr, dwError );
}
bool NLFailure::SetSystemError($A, PCWSTR message $optional ) $ret_false
{
   $VERIFY_POINTER_ARG( message );
   return SetError( NLErrorCat_System, message );
}
bool NLFailure::SetSystemError(PCWSTR headerMessage $optional,DWORD dwError) $ret_false
{
   return SetSystemErrorEx( headerMessage, dwError, false );
}

bool NLFailure::SetWin32FuncFailure($A, PCWSTR functionName ) $ret_false
{
   return SetSystemErrorFmt($ci, $TEXT(
      L"Win32-function “%s” has failed.",
      L"Win32-функция “%s” потерпела неудачу." ),
      functionName );
}

bool NLFailure::SetFromLastSystemError() $ret_false
{
   return SetSystemError( ::GetLastError() );
}

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

bool NLFailure::SetFormatAllocFailure() $ret_false
{
   NLError &data = *FreeErrorData();
   data.Category = NLErrorCat_System;
   data.MessagePtr = (PWSTR)$StaticMessages::MemoryAllocationFailure();
   return false;
}

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

bool NLFailure::SetCOMError(HRESULT hResult) $ret_false
{
   return SetCOMError( nullptr, hResult );
}
bool NLFailure::SetCOMError($A, PCWSTR message ) $ret_false
{
   $VERIFY_POINTER_ARG( message );
   return SetError( NLErrorCat_COM, message );
}
bool NLFailure::SetCOMError(PCWSTR headerMessage $optional,HRESULT hResult) $ret_false
{
   return SetSystemErrorEx( headerMessage, hResult, true );
}

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

bool NLFailure::SetFromLastRTError() $ret_false
{
   return SetFromLastRTError( nullptr );
}
bool NLFailure::SetFromLastRTError(PCWSTR headerMessage $optional) $ret_false
{
   bool messageGenerationProblem;
   $DynText errorMessage = $LE::GetMessage( messageGenerationProblem );
   NLError &data = *FreeErrorData();
   data.Category = messageGenerationProblem ? NLErrorCat_Special :
      CategoryFromErrorType( $LE::ErrorType() );
   auto errorSign = $LE::ErrorSign();
   if ( !messageGenerationProblem && errorSign.IsWin32Error )
      data.Code32 = errorSign.StrictWin32Error($);
   if ( !messageGenerationProblem && errorSign.IsCOMError )
      data.Code32 = errorSign.StrictCOMError($);
   data.Value32 = errorSign.ExCode.AsDWord;
   if ( !headerMessage )
   {
      data.IsAllocatedMsg = errorMessage.NeedsFree($);
      data.MessagePtr = errorMessage.TakePointerOwnership($);
      return false;
   }
   if (!( data.MessagePtr =
          $SZ::FormatAllocNS($, L"%s\r\n%s", headerMessage, errorMessage.T($) ) ))
      return SetFormatAllocFailure();
   data.IsAllocatedMsg = true;
   return false;
}

bool NLFailure::SetFromLastSystemError( PCWSTR headerMessage $optional ) $ret_false
{
   return SetSystemError( headerMessage, ::GetLastError() );
}

bool NLFailure::SetFromErrorInfo($A,const $ErrorInfo& errorInfo) $ret_false
{
   bool messageGenerationProblem;
   $DynText errorMessage = errorInfo.Message($ci, messageGenerationProblem );
   NLError &data = *FreeErrorData();
   data.Category = messageGenerationProblem ? NLErrorCat_Special :
      CategoryFromErrorType( errorInfo.ErrorType($ci) );
   auto errorSign = errorInfo.ErrorSign($ci);
   if ( !messageGenerationProblem && errorSign.IsWin32Error )
      data.Code32 = errorSign.StrictWin32Error($ci);
   if ( !messageGenerationProblem && errorSign.IsCOMError )
      data.Code32 = errorSign.StrictCOMError($ci);
   data.IsAllocatedMsg = errorMessage.NeedsFree($);
   data.MessagePtr = errorMessage.TakePointerOwnership($);
   //data.MessageRetransQuery = data.Category == NLErrorCat_COM;
   return false;
}

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

const NLError& NLFailure::Error()
{
   return *DataPtr();
}

void NLFailure::SetMessageRetransQuery()
{
   DataPtr()->MessageRetransQuery = true;
}

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

NLErrorCat NLFailure::CategoryFromErrorType($ErrorType errorType)
{
   switch ( errorType )
   {
   case $ErrorType::None   : return NLErrorCat_None;
   case $ErrorType::General: return NLErrorCat_General;
   case $ErrorType::Win32  : return NLErrorCat_System;
   case $ErrorType::COM    : return NLErrorCat_COM;
   } // switch
   return NLErrorCat_Special;
}

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

NLError* NLFailure::FreeErrorData() $not_null
{
   NLError &data = *DataPtr();
   if ( data.IsAllocatedMsg )
      $Memory::Free( data.MessagePtr );
   data = {};
   return &data;
}

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

NLError* NLFailure::DataPtr() $not_null $fatal_error_on_failure
{
   auto pointer = GetDataPtr();
   $VERIFY_POINTER( pointer );
   return pointer;
}

NLError* NLFailure::GetDataPtr()
{
   $Thread::VerifyInitialized($);
   return $tls::GetPointer<NLError>($, &LIB::error_pointer_tls );
}

//==================================================================================================
} // NativeLib
