// RuntimeForStatic base library | C+- (SysRoutine) | Author: Sergei Y.Kitáev
#include "Precompiled.h"
#include "SharedIncludes.h"
namespace RuntimeForStatic {
//==================================================================================================
$RuntimeParameters $RT::_RuntimeParameters_;
volatile PCWSTR $RT::_DefaultModuleName_;
DWORD $RT::_IsInitialized_;
bool $RT::_WasInitialized_, $RT::_WasInitializationAttempt_, $RT::_IsInitializing_;
//==================================================================================================

bool $RT::IsInitialized()
{
   return $BOOL( $Interlocked::GetAsBool($, &_IsInitialized_ ) );
}
bool $RT::IsInitialized(bool treatInitializingAsInitialized)
{
   return treatInitializingAsInitialized && _IsInitializing_ || IsInitialized();
}
bool $RT::WasInitialized()
{
   ::MemoryBarrier(); return _WasInitialized_;
}
bool $RT::WasInitializationAttempt()
{
   ::MemoryBarrier(); return _WasInitializationAttempt_;
}

bool $RT::IsInitializing()
{
   return _IsInitializing_;
}

void $RT::VerifyInitialized($O) $requires_rt_init
{
   if ( !IsInitialized() )
      Fail_NotInitialized($ci);
}
void $RT::Fail_NotInitialized($O)
{
   $INVALID_OPERATION_WITH_MESSAGE( $TEXT(
      L"“RuntimeForStatic” library has not been initialized.",
      L"Библиотека “RuntimeForStatic” не была проинициализирована." ));
}

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

bool $RT::Initialize($OA, const $RuntimeParameters& runtimeParameters,
                          $DynText& errorMessage $out )
{
   if ( IsInitialized() )
      $INVALID_OPERATION_WITH_MESSAGE( $TEXT(
         L"Attepmt of repeatedly initialize “RuntimeForStatic” library.",
         L"Попытка повторной инициализации библиотеки “RuntimeForStatic”." ));
   $RuntimeParameters &rtParams = _RuntimeParameters_ = runtimeParameters;
   $Ops::NormalizeBool( rtParams.AllowDesktopInteraction );
   _WasInitializationAttempt_ = true;
   ::MemoryBarrier();
   errorMessage.SetMessage_UnknownError($ci);
   PCWSTR errorText = nullptr;
   DWORD dwSysError = 0;
   $ModuleParametersRT &moduleParams = rtParams.ModuleParameters;
   if ( rtParams.InitializerThreadId )
   {
      errorText = $TEXT(
         L"Parameter must not be specified: “InitializerThreadId”.",
         L"Параметр не должен задаваться: “InitializerThreadId”." );
      goto SIMPLE_FAILURE;
   }
   if ( !moduleParams.Name )
   {
      errorText = $TEXT(
         L"Parameter was not specified: “ModuleParameters.Name”.",
         L"Параметр не задан: “ModuleParameters.Name”." );
      goto SIMPLE_FAILURE;
   }
   $ExitCodesRT &excodes = rtParams.ExitCodes;
   $subst::ReplaceNull( excodes.Failure,   $FAILURE_EXIT_CODE   );
   $subst::ReplaceNull( excodes.Breakdown, $BREAKDOWN_EXIT_CODE );
   auto pThreadSettings = &rtParams.ThreadSettingsPattern;
   if ( !$Thread::AcceptSettings($ci, pThreadSettings, errorMessage ) )
      return false;
   ::GetLastError();
   if (!( ::SetThreadLocale( pThreadSettings->LocaleId ) &&
          $Thread::InitializeThreading( rtParams.ThreadSettingsPattern, errorText ) &&
          $LE::InitializeThreading( errorText ) ))
      goto FAILURE_SYS;
   _IsInitializing_ = true;
   if ( !$Thread::InitializeThread($$, pThreadSettings, false, errorMessage ) )
   {
      $LE::FinalizeThreading();
      $Thread::FinalizeThreading();
      _IsInitializing_ = false;
      return false;
   }
   rtParams.InitializerThreadId = ::GetCurrentThreadId();
   if ( !rtParams.MainThreadId )
      rtParams.MainThreadId = rtParams.InitializerThreadId;
   _WasInitialized_ = true; ::MemoryBarrier();
   $Interlocked::SetAsBool($, &_IsInitialized_ );
   _IsInitializing_ = false;
   return errorMessage.SetMessage_NoError($ci);
FAILURE_SYS:
   dwSysError = ::GetLastError();
   if ( !dwSysError )
      goto SIMPLE_FAILURE;
   $LE::FinalizeThreading();
   $Thread::FinalizeThreading();
   ::MemoryBarrier();
   PWSTR pSysErrorText = HelpersRT::GetSystemMessage($, dwSysError );
   if ( pSysErrorText )
   {
      int capacity = $SZ::Length($, pSysErrorText ) + 1;
      PWSTR pMessageCopy = (PWSTR)HelpersRT::Allocate($, capacity * $CHAR_SIZE );
      if ( pMessageCopy )
         errorMessage.SetPointer($, $SZ::Copy($, pSysErrorText, pMessageCopy, capacity ) );
      $Memory::SysLocalFree($, pSysErrorText );
      if ( pMessageCopy )
      {
         errorMessage.SetMessage_MemoryAllocationFailure($ci);
         return false;
      }
   }
SIMPLE_FAILURE:
   PCWSTR generalMessage = $TEXT(
      L"“RuntimeForStatic” library initialization has failed.",
      L"Инициализация библиотеки “RuntimeForStatic” потерпела неудачу." );
   PWSTR pErrorText = errorText ?
      HelpersRT::FormatAlloc($, L"%s\r\n%s", generalMessage, errorText ) :
      HelpersRT::FormatAlloc($, $TEXT(
         L"%s\r\nSystem error code: %d (0x%.8X).",
         L"%s\r\nКод системной ошибки: %d (0x%.8X)." ),
         generalMessage, dwSysError, dwSysError );
   if ( pErrorText )
      errorMessage.SetPointer($, pErrorText );
   else
      errorMessage.SetStaticText($, generalMessage );
   return false;
}

void $RT::Finalize() $not_requires_rt_init
{
   if ( !::InterlockedExchange( &_IsInitialized_, FALSE ) )
      return;
   if ( IsInInitializerThread() )
      $Thread::FinalizeThread($$);
   $LE::FinalizeThreading();
   $Thread::FinalizeThreading();
   ::MemoryBarrier();
}

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

void $RT::PrepareForRequiredPropertyGetting($O)
{
   if ( !WasInitialized() ) Fail_NotInitialized($ci);
}

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

PCWSTR $RT::ModuleName($O) $strict_result $requires_rt_init
{
   PCWSTR rslt = GetModuleName();
   if ( !rslt ) Fail_NotInitialized($ci);
   return rslt;
}
PCWSTR $RT::GetModuleName() $or_null
{
   ::MemoryBarrier();
   auto value = _RuntimeParameters_.ModuleParameters.Name;
   return value ? value : GetDefaultModuleName();
}

PCWSTR $RT::GetMainModuleName() $or_null
{
   ::MemoryBarrier();
   return _RuntimeParameters_.ModuleParameters.MainModuleName;
}

DWORD $RT::MainThreadId($O) $strict_result $requires_rt_init
{
   DWORD rslt = GetMainThreadId();
   if ( !rslt ) Fail_NotInitialized($ci);
   return rslt;
}
DWORD $RT::GetMainThreadId() $or_zero
{
   ::MemoryBarrier();
   return _RuntimeParameters_.MainThreadId;
}

DWORD $RT::InitializerThreadId($O) $strict_result $requires_rt_init
{
   DWORD rslt = GetInitializerThreadId();
   if ( !rslt ) Fail_NotInitialized($ci);
   return rslt;
}
DWORD $RT::GetInitializerThreadId() $or_zero
{
   ::MemoryBarrier();
   return _RuntimeParameters_.InitializerThreadId;
}

bool $RT::AllowDesktopInteractionValue($O) $strict_result $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.AllowDesktopInteraction;
}
bool $RT::GetAllowDesktopInteraction() $or_false
{
   ::MemoryBarrier();
   return _RuntimeParameters_.AllowDesktopInteraction;
}
bool $RT::GetAllowDesktopInteraction(bool defaultValue)
{
   return IsInitialized() ? GetAllowDesktopInteraction() : defaultValue;
}

bool $RT::IsNativeThreadStartForbiddenVCRT($O) $strict_result $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.IsNativeThreadStartForbiddenVCRT;
}
bool $RT::GetNativeThreadStartForbiddenVCRT() $or_false
{
   ::MemoryBarrier();
   return _RuntimeParameters_.IsNativeThreadStartForbiddenVCRT;
}

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

int $RT::SuccessfulExitCode($O) $strict_result $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.ExitCodes.Successful;
}
int $RT::GetSuccessfulExitCode() $default_value( 0 )
{
   ::MemoryBarrier();
   return _RuntimeParameters_.ExitCodes.Successful;
}

int $RT::FailureExitCode($O) $strict_result $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.ExitCodes.Failure;
}
int $RT::GetFailureExitCode() $default_value( 1 )
{
   ::MemoryBarrier();
   return $_( _RuntimeParameters_.ExitCodes.Failure, $ExitCodesRT::FailureDefault );
}

int $RT::BreakdownExitCode($O) $strict_result $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.ExitCodes.Breakdown;
}
int $RT::GetBreakdownExitCode() $default_value( -1 )
{
   ::MemoryBarrier();
   return $_( _RuntimeParameters_.ExitCodes.Breakdown, $ExitCodesRT::BreakdownDefault );
}

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

const $ModuleParametersRT& $RT::ModuleParameters($O) $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.ModuleParameters;
}
$PCModuleParametersRT $RT::GetModuleParameters() $or_null
{
   if ( !IsInitialized() ) return nullptr;
   ::MemoryBarrier();
   return &_RuntimeParameters_.ModuleParameters;
}

const $ExitCodesRT& $RT::ExitCodes($O) $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.ExitCodes;
}
$PCExitCodesRT $RT::GetExitCodes() $or_null
{
   if ( !IsInitialized() ) return nullptr;
   ::MemoryBarrier();
   return &_RuntimeParameters_.ExitCodes;
}

const $ThreadSettings& $RT::ThreadSettingsPattern($O) $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.ThreadSettingsPattern;
}
$PCThreadSettings $RT::GetThreadSettingsPattern() $or_null
{
   if ( !IsInitialized() ) return nullptr;
   ::MemoryBarrier();
   return &_RuntimeParameters_.ThreadSettingsPattern;
}

const $FatalErrorSettingsRT& $RT::FatalErrorSettings($O) $requires_rt_init
{
   PrepareForRequiredPropertyGetting($ci);
   return _RuntimeParameters_.FatalErrorSettings;
}
$PCFatalErrorSettingsRT $RT::GetFatalErrorSettings() $or_null
{
   if ( !IsInitialized() ) return nullptr;
   ::MemoryBarrier();
   return &_RuntimeParameters_.FatalErrorSettings;
}

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

bool $RT::IsInInitializerThread()
{
   DWORD initializerThreadId = GetInitializerThreadId();
   return initializerThreadId && initializerThreadId == ::GetCurrentThreadId();
}

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

void $RT::SpecifyMainThreadId($O,DWORD value) $requires_rt_init
{
   VerifyInitialized($ci);
   _RuntimeParameters_.MainThreadId = value;
   ::MemoryBarrier();
}

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

PCWSTR $RT::GetDefaultModuleName() $or_null
{
   return _DefaultModuleName_;
}

void $RT::SpecifyDefaultModuleName(PCWSTR name $optional)
{
   _DefaultModuleName_ = name;
}

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