// RuntimeForStatic base library | C+- (SysRoutine) | Author: Sergei Y.Kitáev
#include "Precompiled.h"
#include "SharedIncludes.h"
namespace RuntimeForStatic {
//==================================================================================================

void $FatalError::ReportError( const TerminationParams& terminationParams,
                               bool isCompletionReport )
{
   ReportError( ReportOutputs::All, terminationParams, isCompletionReport );
}
void $FatalError::ReportError( ReportOutputs outputs,
                               const TerminationParams& terminationParams,
                               bool isCompletionReport )
{
   $$VERIFY_POINTER_ARG_( &terminationParams );
   PCWSTR message = terminationParams.Message;
   DWORD sourceThreadId = terminationParams.SourceThreadId;
   $$VERIFY_POINTER( message );
   const $CallerInfo
      &ci = *terminationParams.CallerInfoPtr;
   bool
      isCIProvided = $IS_CI_PROVIDED( ci ),
      isFromMainThread = sourceThreadId && sourceThreadId == $RT::GetMainThreadId(),
      uiReport = (outputs & ReportOutputs::UI) != 0 && UseDesktopInteraction(),
      traceReport = $BOOL( outputs & ReportOutputs::Trace );
   if ( !uiReport && !traceReport )
      return;
   PWSTR pTagExpression = nullptr;
   PCWSTR tagExpression = L"";
   if ( ci.Tag )
   {
      pTagExpression = HelpersRT::FormatAlloc($, $TEXT(
         L"* Marker: {%s}\r\n",
         L"* Маркер: {%s}\r\n" ), ci.Tag );
      tagExpression = $_( (PCWSTR)pTagExpression, $TEXT(
         L"* Marker: ???\r\n",
         L"* Маркер: ???\r\n" ));
   }
   PWSTR pMainModuleExpression = nullptr;
   PCWSTR mainModuleExpression = L"", mainModuleName = $RT::GetMainModuleName();
   if ( mainModuleName )
   {
      pMainModuleExpression = HelpersRT::FormatAlloc($, $TEXT(
         L"* Main module: %s\r\n",
         L"* Основной модуль: %s\r\n" ), mainModuleName );
      mainModuleExpression = $_( (PCWSTR)pMainModuleExpression, $TEXT(
         L"* Main module: ???\r\n",
         L"* Основной модуль: ???\r\n" ));
   }
   PWSTR
      pExeName = HelpersRT::GetExeName($),
      pExeBaseName = HelpersRT::GetExeBaseName($),
      pLineNoStr = isCIProvided ? HelpersRT::IntToStr($, ci.LineNo ) : nullptr,
      pReportText = isCompletionReport ?
         HelpersRT::FormatAlloc($, $TEXT(
            L"___ Fatal error completion error: ___\r\n"
            L"\r\n"
            L"* Exe-name: %s\r\n"
            L"* Process ID: %d\r\n"
            L"%s"
            L"\r\n"
            L"%s",
            L"___ Ошибка завершения по фатальной ошибке: ___\r\n"
            L"\r\n"
            L"* Исполняемый файл: %s\r\n"
            L"* Ид-р процесса: %d\r\n"
            L"%s"
            L"\r\n"
            L"%s" ),
            $_( (PCWSTR)pExeName, L"???" ),
            ::GetCurrentProcessId(),
            mainModuleExpression,
            message ) :
         HelpersRT::FormatAlloc($, $TEXT(
            L"___ Fatal error in [%s]: ___\r\n"
            L"\r\n"
            L"%s"
            L"* Source module: %s\r\n"
            L"* Function: %s\r\n"
            L"* File: %s\r\n"
            L"* Line: %s\r\n"
            L"%s"
            L"* Process ID: %d\r\n"
            L"* Native thread ID: %d%s\r\n"
            L"\r\n"
            L"%s",
            L"___ Фатальная ошибка в [%s]: ___\r\n"
            L"\r\n"
            L"%s"
            L"* Исходный модуль: %s\r\n"
            L"* Функция: %s\r\n"
            L"* Файл: %s\r\n"
            L"* Строка: %s\r\n"
            L"%s"
            L"* Ид-р процесса: %d\r\n"
            L"* Ид-р нативного програм.потока: %d%s\r\n"
            L"\r\n"
            L"%s" ),
            $_( (PCWSTR)pExeName, L"???" ),
            mainModuleExpression,
            $_( $RT::GetModuleName(), L"???" ),
            isCIProvided && ci.FuncSignature ? ci.FuncSignature : L"-",
            isCIProvided && ci.SourceFile ? ci.SourceFile : L"-",
            isCIProvided ? $_((PCWSTR)pLineNoStr,L"???") : L"-",
            tagExpression,
            ::GetCurrentProcessId(),
            sourceThreadId,
            isFromMainThread ? $TEXT(L" (main)",L" (главный)") :
               $RT::IsInitialized() ? $TEXT(L" (secondary)",L" (второстепенный)") : L"",
            message );
   $AUTO_FREE_6( pReportText, pExeName, pExeBaseName, pLineNoStr, pTagExpression,
                 pMainModuleExpression );
   PCWSTR
      reportText = $_( (PCWSTR)pReportText, message ),
      exeBaseName = $_( (PCWSTR)pExeBaseName, L"???" );
   bool traced = false;
   if ( traceReport )
   {
      if ( !isCompletionReport )
         HelpersRT::Trace();
      traced = $SUCCESS( HelpersRT::TraceError($, reportText ) );
   }
   if ( !uiReport )
   {
      if ( !traced )
         $Thread::Hang();
      return;
   }
   ShowErrorBoxParams params = {};
   if ( terminationParams.UseTerminationThread )
      goto SHOW_ERROR;
   params.Text = reportText;
   params.Caption = exeBaseName;
   HANDLE hThread = ::CreateThread( nullptr, 0, ShowErrorBoxRoutine, &params,
                                    CREATE_SUSPENDED, nullptr );
   if ( !hThread )
      goto SHOW_ERROR;
   ::SetThreadPriority( hThread, THREAD_PRIORITY_ABOVE_NORMAL );
   if ( ::ResumeThread( hThread ) == (DWORD)-1 )
   {
      ::CloseHandle( hThread );
      goto SHOW_ERROR;
   }
   DWORD dwExitCode;
   bool showErrorSucceeded =
      ::WaitForSingleObject( hThread, INFINITE ) == WAIT_OBJECT_0 &&
      ::GetExitCodeThread( hThread, &dwExitCode ) && !dwExitCode;
   ::CloseHandle( hThread );
   goto COMPLETION;
SHOW_ERROR:
   showErrorSucceeded = ShowErrorBox( reportText, exeBaseName );
COMPLETION:
   if ( !showErrorSucceeded && !traced )
      $Thread::Hang();
}

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

bool $FatalError::ShowErrorBox( PCWSTR text, PCWSTR caption )
{
   ::Beep( 880, 300 );
   return $BOOL( ::MessageBox( nullptr,
      $_(text,L"???"), $_(caption,L"???"),
      MB_ICONERROR | MB_SYSTEMMODAL ));
}

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

DWORD WINAPI $FatalError::ShowErrorBoxRoutine( PVOID pShowErrorBoxParams )
{
   PCWSTR text = nullptr, caption = nullptr;
   if ( pShowErrorBoxParams )
   {
      const ShowErrorBoxParams &params = *(PShowErrorBoxParams)pShowErrorBoxParams;
      text = params.Text;
      caption = params.Caption;
   }
   return ShowErrorBox( text, caption ) ? 0 : 1;
}

//==================================================================================================
} // RuntimeForStatic
