// Copyright Epic Games, Inc. All Rights Reserved.

#include "GameFramework/GameUserSettings.h"
#include "HAL/FileManager.h"
#include "Misc/ConfigCacheIni.h"
#include "HAL/IConsoleManager.h"
#include "GenericPlatform/GenericApplication.h"
#include "Misc/App.h"
#include "EngineGlobals.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Engine/GameViewportClient.h"
#include "UnrealEngine.h"
#include "Framework/Application/SlateApplication.h"
#include "Engine/GameEngine.h"
#include "Sound/AudioSettings.h"
#include "Sound/SoundCue.h"
#include "AudioDevice.h"
#include "DynamicResolutionState.h"
#include "HAL/PlatformFramePacer.h"

extern EWindowMode::Type GetWindowModeType(EWindowMode::Type WindowMode);

enum EGameUserSettingsVersion
{
  /** Version for user game settings. All settings will be wiped if the serialized version differs. */
  UE_GAMEUSERSETTINGS_VERSION = 5
};


UGameUserSettings::UGameUserSettings(const FObjectInitializer& ObjectInitializer)
  : Super(ObjectInitializer)
{
  // this will only call the base version of SetToDefaults but some constructors may rely on it being set
  SetToDefaults();
}

FIntPoint UGameUserSettings::GetScreenResolution() const
{
  return FIntPoint(ResolutionSizeX, ResolutionSizeY);
}

FIntPoint UGameUserSettings::GetLastConfirmedScreenResolution() const
{
  return FIntPoint(LastUserConfirmedResolutionSizeX, LastUserConfirmedResolutionSizeY);
}

FIntPoint UGameUserSettings::GetDesktopResolution() const
{
  FDisplayMetrics DisplayMetrics;
  if (FSlateApplication::IsInitialized())
  {
    FSlateApplication::Get().GetInitialDisplayMetrics(DisplayMetrics);
  }
  else
  {
    if (FApp::CanEverRender())
    {
      FDisplayMetrics::RebuildDisplayMetrics(DisplayMetrics);
    }
    else
    {
      // assume headless
      DisplayMetrics.PrimaryDisplayWidth = 0;
      DisplayMetrics.PrimaryDisplayHeight = 0;
    }
  }
  return FIntPoint(DisplayMetrics.PrimaryDisplayWidth, DisplayMetrics.PrimaryDisplayHeight);
}

void UGameUserSettings::SetScreenResolution(FIntPoint Resolution)
{
  if (ResolutionSizeX != Resolution.X || ResolutionSizeY != Resolution.Y)
  {
    ResolutionSizeX = Resolution.X;
    ResolutionSizeY = Resolution.Y;
    UpdateResolutionQuality();
  }
}

static EWindowMode::Type GetPlatformFullscreenMode(int InFullscreenMode)
{
  EWindowMode::Type Mode = EWindowMode::ConvertIntToWindowMode(InFullscreenMode);

  return (!FPlatformProperties::SupportsWindowedMode()) ? EWindowMode::Fullscreen : Mode;
}

EWindowMode::Type UGameUserSettings::GetFullscreenMode() const
{
  return GetPlatformFullscreenMode(FullscreenMode);
}

EWindowMode::Type UGameUserSettings::GetLastConfirmedFullscreenMode() const
{
  return GetPlatformFullscreenMode(LastConfirmedFullscreenMode);
}

void UGameUserSettings::SetFullscreenMode(EWindowMode::Type InFullscreenMode)
{
  if (FullscreenMode != InFullscreenMode)
  {
    switch (InFullscreenMode)
    {
    case EWindowMode::Fullscreen:
      FullscreenMode = 0;
      break;
    case EWindowMode::WindowedFullscreen:
      FullscreenMode = 1;
      break;
    case EWindowMode::Windowed:
    default:
      FullscreenMode = 2;
      break;
    }

    UpdateResolutionQuality();
  }
}

EWindowMode::Type UGameUserSettings::GetPreferredFullscreenMode() const
{
  return PreferredFullscreenMode == 0 ? EWindowMode::Fullscreen : EWindowMode::WindowedFullscreen;
}

void UGameUserSettings::SetVSyncEnabled(bool bEnable)
{
  bUseVSync = bEnable;
}

bool UGameUserSettings::IsVSyncEnabled() const
{
  return bUseVSync;
}

void UGameUserSettings::SetDynamicResolutionEnabled(bool bEnable)
{
  bUseDynamicResolution = bEnable;
}

bool UGameUserSettings::IsDynamicResolutionEnabled() const
{
  return bUseDynamicResolution;
}

bool UGameUserSettings::IsScreenResolutionDirty() const
{
  bool bIsDirty = false;
  if (GEngine && GEngine->GameViewport && GEngine->GameViewport->ViewportFrame)
  {
    bIsDirty = (ResolutionSizeX != GSystemResolution.ResX || ResolutionSizeY != GSystemResolution.ResY) ? true : false;
  }
  return bIsDirty;
}

bool UGameUserSettings::IsFullscreenModeDirty() const
{
  bool bIsDirty = false;
  if (GEngine && GEngine->GameViewport && GEngine->GameViewport->ViewportFrame)
  {
    EWindowMode::Type CurrentFullscreenMode = GEngine->GameViewport->Viewport->GetWindowMode();
    EWindowMode::Type NewFullscreenMode = GetFullscreenMode();
    bIsDirty = (CurrentFullscreenMode != NewFullscreenMode);
  }
  return bIsDirty;
}

bool UGameUserSettings::IsVSyncDirty() const
{
  bool bIsDirty = false;
  if (GEngine && GEngine->GameViewport && GEngine->GameViewport->ViewportFrame)
  {
    static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VSync"));
    bIsDirty = (bUseVSync != (CVar->GetValueOnGameThread() != 0));
  }
  return bIsDirty;
}

bool UGameUserSettings::IsDynamicResolutionDirty() const
{
  bool bIsDirty = false;
  if (GEngine && GEngine->GameViewport && GEngine->GameViewport->ViewportFrame)
  {
    bIsDirty = (bUseDynamicResolution != GEngine->GetDynamicResolutionUserSetting());
  }
  return bIsDirty;
}

bool UGameUserSettings::IsDirty() const
{
  return IsScreenResolutionDirty() || IsFullscreenModeDirty() || IsVSyncDirty() || IsDynamicResolutionDirty();
}

void UGameUserSettings::ConfirmVideoMode()
{
  LastConfirmedFullscreenMode = FullscreenMode;
  LastUserConfirmedResolutionSizeX = ResolutionSizeX;
  LastUserConfirmedResolutionSizeY = ResolutionSizeY;
}

void UGameUserSettings::RevertVideoMode()
{
  FullscreenMode = LastConfirmedFullscreenMode;
  ResolutionSizeX = LastUserConfirmedResolutionSizeX;
  ResolutionSizeY = LastUserConfirmedResolutionSizeY;
}

void UGameUserSettings::SetToDefaults()
{
  ResolutionSizeX = LastUserConfirmedResolutionSizeX = GetDefaultResolution().X;
  ResolutionSizeY = LastUserConfirmedResolutionSizeY = GetDefaultResolution().Y;
  WindowPosX = GetDefaultWindowPosition().X;
  WindowPosY = GetDefaultWindowPosition().Y;
  FullscreenMode = GetDefaultWindowMode();
  FrameRateLimit = 0.0f;
  MinResolutionScale = Scalability::MinResolutionScale;
  DesiredScreenWidth = 1280;
  DesiredScreenHeight = 720;
  LastUserConfirmedDesiredScreenWidth = DesiredScreenWidth;
  LastUserConfirmedDesiredScreenHeight = DesiredScreenHeight;
  LastCPUBenchmarkResult = -1.0f;
  LastGPUBenchmarkResult = -1.0f;
  LastCPUBenchmarkSteps.Empty();
  LastGPUBenchmarkSteps.Empty();
  LastGPUBenchmarkMultiplier = 1.0f;
  LastRecommendedScreenWidth = -1.0f;
  LastRecommendedScreenHeight = -1.0f;

  static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.FullScreenMode"));
  PreferredFullscreenMode = CVar->GetValueOnGameThread();

  ScalabilityQuality.SetDefaults();

  if (FApp::CanEverRender())
  {
    UpdateResolutionQuality();
  }

  bUseDynamicResolution = false;
  bUseHDRDisplayOutput = FPlatformMisc::UseHDRByDefault();
  HDRDisplayOutputNits = 1000;
}

bool UGameUserSettings::IsVersionValid()
{
  return (Version == UE_GAMEUSERSETTINGS_VERSION);
}

void UGameUserSettings::UpdateVersion()
{
  Version = UE_GAMEUSERSETTINGS_VERSION;
}

void UGameUserSettings::UpdateResolutionQuality()
{
  const int32 MinHeight = UKismetSystemLibrary::GetMinYResolutionFor3DView();
  const int32 ScreenWidth = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().X : ResolutionSizeX;
  const int32 ScreenHeight = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().Y : ResolutionSizeY;
  MinResolutionScale = FMath::Max<float>(Scalability::MinResolutionScale, ((float)MinHeight / (float)ScreenHeight) * 100.0f);

  if (bUseDesiredScreenHeight)
  {
    ScalabilityQuality.ResolutionQuality = GetDefaultResolutionScale();
  }
  else
  {
    ScalabilityQuality.ResolutionQuality = FMath::Max(ScalabilityQuality.ResolutionQuality, MinResolutionScale);
  }
}

float UGameUserSettings::GetDefaultResolutionScale()
{
  const int32 ScreenWidth = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().X : ResolutionSizeX;
  const int32 ScreenHeight = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().Y : ResolutionSizeY;
  const int32 ClampedWidth = (ScreenWidth > 0 && DesiredScreenWidth > ScreenWidth) ? ScreenWidth : DesiredScreenWidth;
  const int32 ClampedHeight = (ScreenHeight > 0 && DesiredScreenHeight > ScreenHeight) ? ScreenHeight : DesiredScreenHeight;

  const float DesiredResQuality = FindResolutionQualityForScreenSize(ClampedWidth, ClampedHeight);
  return FMath::Max(DesiredResQuality, MinResolutionScale);
}

float UGameUserSettings::GetRecommendedResolutionScale()
{
  const float RecommendedResQuality = FindResolutionQualityForScreenSize(LastRecommendedScreenWidth, LastRecommendedScreenHeight);

  return FMath::Max(RecommendedResQuality, MinResolutionScale);
}

float UGameUserSettings::FindResolutionQualityForScreenSize(float Width, float Height)
{
  float ResolutionQuality = 100.0f;

  const FIntPoint ScreenSize = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution() : FIntPoint(ResolutionSizeX, ResolutionSizeY);
  const float ScreenAspectRatio = (float)ScreenSize.X / (float)ScreenSize.Y;
  const float AspectRatio = Width / Height;

  // If the screen aspect ratio is different than the target resolution's aspect ratio, we need to adjust the target width and height
  if (!FMath::IsNearlyEqual(ScreenAspectRatio, AspectRatio))
  {
    if (ScreenAspectRatio < AspectRatio)
    {
      // For smaller aspect ratios we allow more vertical space so that the screen width matches the width of the original mode
      Height = (Height * AspectRatio) / ScreenAspectRatio;
    }
    else
    {
      // For wider screens we try to choose a screen size that'd have similar total number of pixels as the original mode
      Height = FMath::Sqrt((Height * Height * AspectRatio) / ScreenAspectRatio);
    }
    Width = Height * AspectRatio;
  }

  if (Height < ScreenSize.Y)
  {
    ResolutionQuality = ((float)Height / (float)ScreenSize.Y) * 100.0f;
  }

  return ResolutionQuality;
}

void UGameUserSettings::SetFrameRateLimitCVar(float InLimit)
{
  GEngine->SetMaxFPS(FMath::Max(InLimit, 0.0f));
}

void UGameUserSettings::SetSyncIntervalCVar(int32 InInterval)
{
  static IConsoleVariable* SyncIntervalCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("rhi.syncinterval"));
  if (ensure(SyncIntervalCVar))
  {
    SyncIntervalCVar->Set(InInterval, ECVF_SetByCode);
  }
}

void UGameUserSettings::SetSyncTypeCVar(int32 InType)
{
  static IConsoleVariable* SyncIntervalCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.GTSyncType"));
  if (ensure(SyncIntervalCVar))
  {
    SyncIntervalCVar->Set(InType, ECVF_SetByCode);
  }
}

float UGameUserSettings::GetEffectiveFrameRateLimit()
{
  return FrameRateLimit;
}

void UGameUserSettings::SetPreferredFullscreenMode(int32 Mode)
{
  PreferredFullscreenMode = Mode;

  auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FullScreenMode"));
  if (CVar)
  {
    CVar->Set(Mode, ECVF_SetByGameSetting);
  }
}

void UGameUserSettings::ValidateSettings()
{
  // Should we wipe all user settings?
  if (!IsVersionValid())
  {
    // First try loading the settings, if they haven't been loaded before.
    LoadSettings(true);

    // If it still an old version, delete the user settings file and reload defaults.
    if (!IsVersionValid())
    {
      // Force reset if there aren't any default .ini settings.
      SetToDefaults();
      static const auto CVarVSync = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VSync"));
      SetVSyncEnabled(CVarVSync->GetValueOnGameThread() != 0);

      if (GEngine)
      {
        SetDynamicResolutionEnabled(GEngine->GetDynamicResolutionUserSetting());
      }

      IFileManager::Get().Delete(*GGameUserSettingsIni);
      LoadSettings(true);
    }
  }

  if (ResolutionSizeX <= 0 || ResolutionSizeY <= 0)
  {
    SetScreenResolution(FIntPoint(GSystemResolution.ResX, GSystemResolution.ResY));

    // Set last confirmed video settings
    LastConfirmedFullscreenMode = FullscreenMode;
    LastUserConfirmedResolutionSizeX = ResolutionSizeX;
    LastUserConfirmedResolutionSizeY = ResolutionSizeY;
  }

  const int32 ScreenWidth = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().X : ResolutionSizeX;
  const int32 ScreenHeight = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().Y : ResolutionSizeY;
  const int32 ClampedWidth = (ScreenWidth > 0 && DesiredScreenWidth > ScreenWidth) ? ScreenWidth : DesiredScreenWidth;
  const int32 ClampedHeight = (ScreenHeight > 0 && DesiredScreenHeight > ScreenHeight) ? ScreenHeight : DesiredScreenHeight;

  LastUserConfirmedDesiredScreenWidth = DesiredScreenWidth;
  LastUserConfirmedDesiredScreenHeight = DesiredScreenHeight;


  // We do not modify the user setting on console if HDR is not supported
  if (!FPlatformMisc::UseHDRByDefault())
  {
    if (bUseHDRDisplayOutput && !SupportsHDRDisplayOutput())
    {
      bUseHDRDisplayOutput = false;
    }
  }

  LastConfirmedAudioQualityLevel = AudioQualityLevel;

  // The user settings have now been validated for the current version.
  UpdateVersion();
}

void UGameUserSettings::ApplyNonResolutionSettings()
{
  QUICK_SCOPE_CYCLE_COUNTER(GameUserSettings_ApplyNonResolutionSettings);

  ValidateSettings();

  // Update vsync cvar
  {
    FString ConfigSection = TEXT("SystemSettings");
#if WITH_EDITOR
    if (GIsEditor)
    {
      ConfigSection = TEXT("SystemSettingsEditor");
    }
#endif
    int32 VSyncValue = 0;
    if (GConfig->GetInt(*ConfigSection, TEXT("r.Vsync"), VSyncValue, GEngineIni))
    {
      // VSync was already set by system settings. We are capable of setting it here.
    }
    else
    {
      static auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync"));
      CVar->Set(IsVSyncEnabled(), ECVF_SetByGameSetting);
    }
  }

  GEngine->SetDynamicResolutionUserSetting(IsDynamicResolutionEnabled());

  if (!IsRunningDedicatedServer())
  {
    SetFrameRateLimitCVar(GetEffectiveFrameRateLimit());
  }

  // in init those are loaded earlier, after that we apply consolevariables.ini
  if (GEngine->IsInitialized())
  {
    Scalability::SetQualityLevels(ScalabilityQuality);
  }

  FAudioDeviceHandle AudioDevice = GEngine->GetMainAudioDevice();
  if (AudioDevice)
  {
    FAudioQualitySettings AudioSettings = AudioDevice->GetQualityLevelSettings();
    AudioDevice->SetMaxChannels(AudioSettings.MaxChannels);
  }

  IConsoleManager::Get().CallAllConsoleVariableSinks();

  bool bWithEditor = false;
#if WITH_EDITOR
  if (GIsEditor)
  {
    bWithEditor = true;
  }
#endif

  bool bEnableHDR = (IsHDRAllowed() && bUseHDRDisplayOutput && !bWithEditor);

  EnableHDRDisplayOutput(bEnableHDR, HDRDisplayOutputNits);

}

void UGameUserSettings::ApplyResolutionSettings(bool bCheckForCommandLineOverrides)
{
#if UE_SERVER
  return;
#endif
  QUICK_SCOPE_CYCLE_COUNTER(GameUserSettings_ApplyResolutionSettings);

  ValidateSettings();

  EWindowMode::Type NewFullscreenMode = GetFullscreenMode();

  // Request a resolution change
  RequestResolutionChange(ResolutionSizeX, ResolutionSizeY, NewFullscreenMode, bCheckForCommandLineOverrides);

  if (NewFullscreenMode == EWindowMode::Fullscreen || NewFullscreenMode == EWindowMode::WindowedFullscreen)
  {
    SetPreferredFullscreenMode(NewFullscreenMode == EWindowMode::Fullscreen ? 0 : 1);
  }

  IConsoleManager::Get().CallAllConsoleVariableSinks();
}

void UGameUserSettings::ApplySettings(bool bCheckForCommandLineOverrides)
{
  ApplyResolutionSettings(bCheckForCommandLineOverrides);
  ApplyNonResolutionSettings();
  RequestUIUpdate();

  SaveSettings();
}

void UGameUserSettings::LoadSettings(bool bForceReload/*=false*/)
{
  QUICK_SCOPE_CYCLE_COUNTER(GameUserSettings_LoadSettings);

  if (bForceReload)
  {
    LoadConfigIni(bForceReload);
  }
  LoadConfig(GetClass(), *GGameUserSettingsIni);

  // Note: Scalability::LoadState() should not be needed as we already loaded the settings earlier (needed so the engine can startup with that before the game is initialized)
  ScalabilityQuality = Scalability::GetQualityLevels();

  // Allow override using command-line settings
  bool bDetectingResolution = ResolutionSizeX == 0 || ResolutionSizeY == 0;

  if (bDetectingResolution)
  {
    ConfirmVideoMode();
  }

  // Update r.FullScreenMode CVar
  SetPreferredFullscreenMode(PreferredFullscreenMode);
}

void UGameUserSettings::RequestResolutionChange(int32 InResolutionX, int32 InResolutionY, EWindowMode::Type InWindowMode, bool bInDoOverrides /* = true */)
{
  if (bInDoOverrides)
  {
    UGameEngine::ConditionallyOverrideSettings(InResolutionX, InResolutionY, InWindowMode);
  }

  FSystemResolution::RequestResolutionChange(InResolutionX, InResolutionY, InWindowMode);
}

void UGameUserSettings::SaveSettings()
{
  QUICK_SCOPE_CYCLE_COUNTER(GameUserSettings_SaveSettings);

  // Save the Scalability state to the same ini file as it was loaded from in FEngineLoop::Preinit
  Scalability::SaveState(GIsEditor ? GEditorSettingsIni : GGameUserSettingsIni);
  SaveConfig(CPF_Config, *GGameUserSettingsIni);
}

void UGameUserSettings::LoadConfigIni(bool bForceReload/*=false*/)
{
  // Load .ini, allowing merging
  FConfigCacheIni::LoadGlobalIniFile(GGameUserSettingsIni, TEXT("GameUserSettings"), nullptr, bForceReload, false, true, true, *FPaths::GeneratedConfigDir());
}

void UGameUserSettings::PreloadResolutionSettings(bool bAllowCmdLineOverrides /*= true*/)
{
  // Note: This preloads resolution settings without loading the user settings object.  
  // When changing this code care must be taken to ensure the window starts at the same resolution as the in game resolution
  LoadConfigIni();

  FString ScriptEngineCategory = TEXT("/Script/Engine.Engine");
  FString GameUserSettingsCategory = TEXT("/Script/Engine.GameUserSettings");

  GConfig->GetString(*ScriptEngineCategory, TEXT("GameUserSettingsClassName"), GameUserSettingsCategory, GEngineIni);

  int32 ResolutionX = GetDefaultResolution().X;
  int32 ResolutionY = GetDefaultResolution().Y;
  EWindowMode::Type WindowMode = GetDefaultWindowMode();
  bool bUseDesktopResolution = false;
  bool bUseHDR = FPlatformMisc::UseHDRByDefault();

  int32 Version = 0;
  if (GConfig->GetInt(*GameUserSettingsCategory, TEXT("Version"), Version, GGameUserSettingsIni) && Version == UE_GAMEUSERSETTINGS_VERSION)
  {
    GConfig->GetBool(*GameUserSettingsCategory, TEXT("bUseDesktopResolution"), bUseDesktopResolution, GGameUserSettingsIni);

    int32 WindowModeInt = (int32)WindowMode;
    GConfig->GetInt(*GameUserSettingsCategory, TEXT("FullscreenMode"), WindowModeInt, GGameUserSettingsIni);
    WindowMode = EWindowMode::ConvertIntToWindowMode(WindowModeInt);

    GConfig->GetInt(*GameUserSettingsCategory, TEXT("ResolutionSizeX"), ResolutionX, GGameUserSettingsIni);
    GConfig->GetInt(*GameUserSettingsCategory, TEXT("ResolutionSizeY"), ResolutionY, GGameUserSettingsIni);

#if PLATFORM_DESKTOP
    if (bUseDesktopResolution && ResolutionX == 0 && ResolutionY == 0 && WindowMode != EWindowMode::Windowed)
    {
      // Grab display metrics so we can get the primary display output size.
      FDisplayMetrics DisplayMetrics;
      FDisplayMetrics::RebuildDisplayMetrics(DisplayMetrics);

      ResolutionX = DisplayMetrics.PrimaryDisplayWidth;
      ResolutionY = DisplayMetrics.PrimaryDisplayHeight;
    }
#endif

    GConfig->GetBool(*GameUserSettingsCategory, TEXT("bUseHDRDisplayOutput"), bUseHDR, GGameUserSettingsIni);
  }

#if !PLATFORM_MANAGES_HDR_SETTING
  if (IsHDRAllowed())
  {
    // Set the user-preference HDR switch
    static auto CVarHDROutputEnabled = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HDR.EnableHDROutput"));
    if (CVarHDROutputEnabled)
    {
      CVarHDROutputEnabled->Set(bUseHDR ? 1 : 0, ECVF_SetByGameSetting);
    }
  }
#endif

  RequestResolutionChange(ResolutionX, ResolutionY, WindowMode, bAllowCmdLineOverrides);

  IConsoleManager::Get().CallAllConsoleVariableSinks();
}

FIntPoint UGameUserSettings::GetDefaultResolution()
{
  return FIntPoint::ZeroValue;
}

FIntPoint UGameUserSettings::GetDefaultWindowPosition()
{
  return FIntPoint(-1, -1);
}

EWindowMode::Type UGameUserSettings::GetDefaultWindowMode()
{
  // WindowedFullscreen should be the general default for games
  return EWindowMode::WindowedFullscreen;
}

int32 UGameUserSettings::GetSyncInterval()
{
  static IConsoleVariable* SyncIntervalCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("rhi.syncinterval"));
  if (ensure(SyncIntervalCVar))
  {
    return SyncIntervalCVar->GetInt();
  }
  else
  {
    return 0;
  }
}

int32 UGameUserSettings::GetFramePace()
{
  return FPlatformRHIFramePacer::GetFramePace();
}

void UGameUserSettings::ResetToCurrentSettings()
{
  if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWindow().IsValid())
  {
    //handle the fullscreen setting
    SetFullscreenMode(GetWindowModeType(GEngine->GameViewport->GetWindow()->GetWindowMode()));

    //set the current resolution
    SetScreenResolution(FIntPoint(GSystemResolution.ResX, GSystemResolution.ResY));

    // Set the current VSync state
    static const auto CVarVSync = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VSync"));
    SetVSyncEnabled(CVarVSync->GetValueOnGameThread() != 0);

    // Set the current dynamic resolution state
    SetDynamicResolutionEnabled(GEngine->GetDynamicResolutionUserSetting());

    // Reset to confirmed settings
    FullscreenMode = LastConfirmedFullscreenMode;
    ResolutionSizeX = LastUserConfirmedResolutionSizeX;
    ResolutionSizeY = LastUserConfirmedResolutionSizeY;

    DesiredScreenWidth = LastUserConfirmedDesiredScreenWidth;
    DesiredScreenHeight = LastUserConfirmedDesiredScreenHeight;

    // Reset the quality settings to the current levels
    ScalabilityQuality = Scalability::GetQualityLevels();

    // Reset the audio quality level
    AudioQualityLevel = LastConfirmedAudioQualityLevel;

    UpdateResolutionQuality();
  }
}

void UGameUserSettings::SetWindowPosition(int32 WinX, int32 WinY)
{
  WindowPosX = WinX;
  WindowPosY = WinY;
}

FIntPoint UGameUserSettings::GetWindowPosition()
{
  return FIntPoint(WindowPosX, WindowPosY);
}

void UGameUserSettings::SetBenchmarkFallbackValues()
{
  ScalabilityQuality.SetBenchmarkFallback();
}

void UGameUserSettings::SetAudioQualityLevel(int32 QualityLevel)
{
  if (AudioQualityLevel != QualityLevel)
  {
    AudioQualityLevel = QualityLevel;

    USoundCue::StaticAudioQualityChanged(QualityLevel);
  }
}

void UGameUserSettings::SetFrameRateLimit(float NewLimit)
{
  FrameRateLimit = NewLimit;
}

float UGameUserSettings::GetFrameRateLimit() const
{
  return FrameRateLimit;
}

void UGameUserSettings::SetOverallScalabilityLevel(int32 Value)
{
  ScalabilityQuality.SetFromSingleQualityLevel(Value);
}

int32 UGameUserSettings::GetOverallScalabilityLevel() const
{
  return ScalabilityQuality.GetSingleQualityLevel();
}

void UGameUserSettings::GetResolutionScaleInformation(float& CurrentScaleNormalized, int32& CurrentScaleValue, int32& MinScaleValue, int32& MaxScaleValue) const
{
  CurrentScaleValue = ScalabilityQuality.ResolutionQuality;
  MinScaleValue = MinResolutionScale;
  MaxScaleValue = Scalability::MaxResolutionScale;
  CurrentScaleNormalized = ((float)CurrentScaleValue - (float)MinScaleValue) / (float)(MaxScaleValue - MinScaleValue);
}

void UGameUserSettings::GetResolutionScaleInformationEx(float& CurrentScaleNormalized, float& CurrentScaleValue, float& MinScaleValue, float& MaxScaleValue) const
{
  CurrentScaleValue = ScalabilityQuality.ResolutionQuality;
  MinScaleValue = MinResolutionScale;
  MaxScaleValue = Scalability::MaxResolutionScale;
  CurrentScaleNormalized = ((float)CurrentScaleValue - (float)MinScaleValue) / (float)(MaxScaleValue - MinScaleValue);
}

float UGameUserSettings::GetResolutionScaleNormalized() const
{
  float CurrentScaleNormalized, CurrentScaleValue, MinScaleValue, MaxScaleValue;
  GetResolutionScaleInformationEx(CurrentScaleNormalized, CurrentScaleValue, MinScaleValue, MaxScaleValue);

  return CurrentScaleNormalized;
}

void UGameUserSettings::SetResolutionScaleValue(int32 NewScaleValue)
{
  SetResolutionScaleValueEx((float)NewScaleValue);
}

void UGameUserSettings::SetResolutionScaleValueEx(float NewScaleValue)
{
  ScalabilityQuality.ResolutionQuality = FMath::Clamp(NewScaleValue, MinResolutionScale, Scalability::MaxResolutionScale);
  const int32 ScreenWidth = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().X : ResolutionSizeX;
  const int32 ScreenHeight = (FullscreenMode == EWindowMode::WindowedFullscreen) ? GetDesktopResolution().Y : ResolutionSizeY;
  DesiredScreenWidth = ScreenWidth * ScalabilityQuality.ResolutionQuality / 100.0f;
  DesiredScreenHeight = ScreenHeight * ScalabilityQuality.ResolutionQuality / 100.0f;
}

void UGameUserSettings::SetResolutionScaleNormalized(float NewScaleNormalized)
{
  const float RemappedValue = FMath::Lerp((float)MinResolutionScale, (float)Scalability::MaxResolutionScale, NewScaleNormalized);
  SetResolutionScaleValueEx(RemappedValue);
}

void UGameUserSettings::SetViewDistanceQuality(int32 Value)
{
  ScalabilityQuality.SetViewDistanceQuality(Value);
}

int32 UGameUserSettings::GetViewDistanceQuality() const
{
  return ScalabilityQuality.ViewDistanceQuality;
}

void UGameUserSettings::SetShadowQuality(int32 Value)
{
  ScalabilityQuality.SetShadowQuality(Value);
}

int32 UGameUserSettings::GetShadowQuality() const
{
  return ScalabilityQuality.ShadowQuality;
}

void UGameUserSettings::SetAntiAliasingQuality(int32 Value)
{
  ScalabilityQuality.SetAntiAliasingQuality(Value);
}

int32 UGameUserSettings::GetAntiAliasingQuality() const
{
  return ScalabilityQuality.AntiAliasingQuality;
}

void UGameUserSettings::SetTextureQuality(int32 Value)
{
  ScalabilityQuality.SetTextureQuality(Value);
}

int32 UGameUserSettings::GetTextureQuality() const
{
  return ScalabilityQuality.TextureQuality;
}

void UGameUserSettings::SetVisualEffectQuality(int32 Value)
{
  ScalabilityQuality.SetEffectsQuality(Value);
}

int32 UGameUserSettings::GetVisualEffectQuality() const
{
  return ScalabilityQuality.EffectsQuality;
}

void UGameUserSettings::SetPostProcessingQuality(int32 Value)
{
  ScalabilityQuality.SetPostProcessQuality(Value);
}

int32 UGameUserSettings::GetPostProcessingQuality() const
{
  return ScalabilityQuality.PostProcessQuality;
}

void UGameUserSettings::SetFoliageQuality(int32 Value)
{
  ScalabilityQuality.SetFoliageQuality(Value);
}

int32 UGameUserSettings::GetFoliageQuality() const
{
  return ScalabilityQuality.FoliageQuality;
}

void UGameUserSettings::SetShadingQuality(int32 Value)
{
  ScalabilityQuality.SetShadingQuality(Value);
}

int32 UGameUserSettings::GetShadingQuality() const
{
  return ScalabilityQuality.ShadingQuality;
}

UGameUserSettings* UGameUserSettings::GetGameUserSettings()
{
  return GEngine->GetGameUserSettings();
}

void UGameUserSettings::RunHardwareBenchmark(int32 WorkScale, float CPUMultiplier, float GPUMultiplier)
{
  ScalabilityQuality = Scalability::BenchmarkQualityLevels(WorkScale, CPUMultiplier, GPUMultiplier);
  LastCPUBenchmarkResult = ScalabilityQuality.CPUBenchmarkResults;
  LastGPUBenchmarkResult = ScalabilityQuality.GPUBenchmarkResults;
  LastCPUBenchmarkSteps = ScalabilityQuality.CPUBenchmarkSteps;
  LastGPUBenchmarkSteps = ScalabilityQuality.GPUBenchmarkSteps;
  LastGPUBenchmarkMultiplier = GPUMultiplier;
}

void UGameUserSettings::ApplyHardwareBenchmarkResults()
{
  // Apply the new settings and save them
  Scalability::SetQualityLevels(ScalabilityQuality);
  Scalability::SaveState(GGameUserSettingsIni);

  SaveSettings();
}

bool UGameUserSettings::SupportsHDRDisplayOutput() const
{
  return GRHISupportsHDROutput;
}

void UGameUserSettings::EnableHDRDisplayOutput(bool bEnable, int32 DisplayNits /*= 1000*/)
{
  static IConsoleVariable* CVarHDROutputEnabled = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HDR.EnableHDROutput"));

  if (CVarHDROutputEnabled)
  {
    if (bEnable && !(GRHISupportsHDROutput && IsHDRAllowed()))
    {
      UE_LOG(LogConsoleResponse, Display, TEXT("Tried to enable HDR display output but unsupported or disallowed, forcing off."));
      bEnable = false;
    }

    // Only support 1000 and 2000 nit curves so push to closest
    int32 DisplayNitLevel = (DisplayNits < 1500) ? 1000 : 2000;

    // Apply device-specific output encoding
    if (bEnable)
    {
#if PLATFORM_WINDOWS
      if (IsRHIDeviceNVIDIA() || IsRHIDeviceAMD())
      {
        // Force exclusive fullscreen
        SetPreferredFullscreenMode(0);
        SetFullscreenMode(GetPreferredFullscreenMode());
        ApplyResolutionSettings(false);
        RequestUIUpdate();
      }
#endif
      CVarHDROutputEnabled->Set(1, ECVF_SetByGameSetting);
    }

    // Always test this branch as can be used to flush errors
    if (!bEnable)
    {
      CVarHDROutputEnabled->Set(0, ECVF_SetByGameSetting);
    }

    // Update final requested state for saved config
#if !PLATFORM_PS4 && !PLATFORM_USES_FIXED_HDR_SETTING
    // Do not override the user setting on console (we rely on the OS setting)
    bUseHDRDisplayOutput = bEnable;
#endif
    HDRDisplayOutputNits = DisplayNitLevel;
  }
}

int32 UGameUserSettings::GetCurrentHDRDisplayNits() const
{
  return bUseHDRDisplayOutput ? HDRDisplayOutputNits : 0;
}

bool UGameUserSettings::IsHDREnabled() const
{
  return bUseHDRDisplayOutput;
}