// Fill out your copyright notice in the Description page of Project Settings.


#include "SurvivalUserSettings.h"

#include "Engine/Engine.h"

namespace
{
	/** Arrondit sur le palier le plus proche, puis borne. */
	float SnapToStep(float Value, float Min, float Max, float Step)
	{
		const float Snapped = Min + FMath::RoundToFloat((Value - Min) / Step) * Step;
		return FMath::Clamp(Snapped, Min, Max);
	}
}

USurvivalUserSettings::USurvivalUserSettings()
{
	// Le constructeur tourne sur le CDO, avant toute lecture du .ini : ces
	// valeurs sont donc bien les defauts d'usine, ecrasees ensuite par le
	// fichier s'il existe.
	SetSurvivalGameplayToDefaults();
	SetSurvivalDisplayToDefaults();
}

USurvivalUserSettings* USurvivalUserSettings::Get()
{
	return GEngine ? Cast<USurvivalUserSettings>(GEngine->GetGameUserSettings()) : nullptr;
}

void USurvivalUserSettings::SetSurvivalGameplayToDefaults()
{
	LookSensitivity = 35.f;
	bSeparateVerticalSensitivity = false;
	LookSensitivityVertical = 35.f;
	bInvertLookY = false;
	FieldOfView = 90.f;
	CameraShakeScale = 100.f;
	bSprintToggle = false;
	bCrouchToggle = false;
}

void USurvivalUserSettings::SetSurvivalDisplayToDefaults()
{
	Brightness = 100.f;
}

void USurvivalUserSettings::SetToDefaults()
{
	// Le "Reinitialiser" global du moteur doit aussi remettre NOS valeurs :
	// sans cette surcharge, elles resteraient seules inchangees.
	Super::SetToDefaults();

	SetSurvivalGameplayToDefaults();
	SetSurvivalDisplayToDefaults();
}

void USurvivalUserSettings::SetBrightness(float Value)
{
	using namespace SurvivalSettingsRange;
	Brightness = SnapToStep(Value, BrightnessMin, BrightnessMax, BrightnessStep);
}

void USurvivalUserSettings::ApplySurvivalSettings()
{
	// Le gamma n'appartient pas a UGameUserSettings : il vit sur GEngine, donc
	// sur un objet GLOBAL au processus. Deux consequences :
	//
	// 1. DisplayGamma est un EXPOSANT dont la valeur moteur est 2.2, pas un
	//    multiplicateur neutre a 1. On module la reference, on ne la remplace
	//    pas.
	// 2. En editeur, cet objet est celui de l'editeur : y toucher assombrit
	//    toute l'interface, y compris apres avoir quitte le PIE. On ne l'applique
	//    donc qu'en jeu. La luminosite ne se teste pas en PIE, mais en
	//    Standalone -- qui est un processus separe.
	if (GEngine && !GIsEditor)
	{
		using namespace SurvivalSettingsRange;

		const float Normalized = FMath::Clamp(Brightness, BrightnessMin, BrightnessMax) / 100.f;
		GEngine->DisplayGamma = BaseDisplayGamma * Normalized;
	}

	OnSurvivalSettingsApplied.Broadcast();
}

void USurvivalUserSettings::ApplyNonResolutionSettings()
{
	Super::ApplyNonResolutionSettings();

	// ApplySettings() passe forcement par ici : brancher la diffusion a ce
	// niveau garantit que nos reglages suivent, quel que soit le chemin.
	ApplySurvivalSettings();
}

float USurvivalUserSettings::GetLookMultiplierX() const
{
	using namespace SurvivalSettingsRange;

	const float Normalized = FMath::GetRangePct(SensitivityMin, SensitivityMax, LookSensitivity);
	return FMath::Lerp(LookMultiplierAtMin, LookMultiplierAtMax, Normalized);
}

float USurvivalUserSettings::GetLookMultiplierY() const
{
	using namespace SurvivalSettingsRange;

	// Sans axe separe, le vertical suit l'horizontal : c'est ce que veulent
	// 95 % des joueurs, et deux curseurs desynchronises par inadvertance
	// donnent une visee bancale sans qu'on comprenne pourquoi.
	if (!bSeparateVerticalSensitivity)
	{
		return GetLookMultiplierX();
	}

	const float Normalized = FMath::GetRangePct(SensitivityMin, SensitivityMax, LookSensitivityVertical);
	return FMath::Lerp(LookMultiplierAtMin, LookMultiplierAtMax, Normalized);
}

float USurvivalUserSettings::GetCameraShakeMultiplier() const
{
	return FMath::Clamp(CameraShakeScale / 100.f, 0.f, 1.f);
}

void USurvivalUserSettings::SetLookSensitivity(float Value)
{
	using namespace SurvivalSettingsRange;
	LookSensitivity = SnapToStep(Value, SensitivityMin, SensitivityMax, SensitivityStep);
}

void USurvivalUserSettings::SetSeparateVerticalSensitivity(bool bValue)
{
	// En repassant sur "non separe" on recale le vertical sur l'horizontal :
	// sinon la valeur oubliee ressortirait telle quelle a la reactivation.
	if (!bValue)
	{
		LookSensitivityVertical = LookSensitivity;
	}

	bSeparateVerticalSensitivity = bValue;
}

void USurvivalUserSettings::SetLookSensitivityVertical(float Value)
{
	using namespace SurvivalSettingsRange;
	LookSensitivityVertical = SnapToStep(Value, SensitivityMin, SensitivityMax, SensitivityStep);
}

void USurvivalUserSettings::SetInvertLookY(bool bValue)
{
	bInvertLookY = bValue;
}

void USurvivalUserSettings::SetFieldOfView(float Value)
{
	using namespace SurvivalSettingsRange;
	FieldOfView = SnapToStep(Value, FieldOfViewMin, FieldOfViewMax, FieldOfViewStep);
}

void USurvivalUserSettings::SetCameraShakeScale(float Value)
{
	using namespace SurvivalSettingsRange;
	CameraShakeScale = SnapToStep(Value, CameraShakeMin, CameraShakeMax, CameraShakeStep);
}

void USurvivalUserSettings::SetSprintToggle(bool bValue)
{
	bSprintToggle = bValue;
}

void USurvivalUserSettings::SetCrouchToggle(bool bValue)
{
	bCrouchToggle = bValue;
}
