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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "FpsPlayer.generated.h"

class APickupItem;
class UCameraComponent;
class UInputAction;
class UInputMappingContext;
class UInteractionComponent;
class UInventoryComponent;
class USurvivalStatsComponent;
struct FInputActionValue;

/**
 * Personnage joueur en vue première personne.
 * Les assets d'input (Input Mapping Context + Input Actions) se branchent
 * dans le Blueprint enfant (BP_FpsPlayer), catégorie "Input".
 */
UCLASS()
class SURVIVAL_PROJET_API AFpsPlayer : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AFpsPlayer();

	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	/** Vrai tant que le joueur sprinte (utile pour l'anim ou la stamina plus tard). */
	UFUNCTION(BlueprintPure, Category = "Mouvement")
	bool IsSprinting() const { return bIsSprinting; }

	UFUNCTION(BlueprintPure, Category = "Camera")
	UCameraComponent* GetFirstPersonCamera() const { return FirstPersonCamera; }

	UFUNCTION(BlueprintPure, Category = "Interaction")
	UInteractionComponent* GetInteractionComponent() const { return InteractionComponent; }

	UFUNCTION(BlueprintPure, Category = "Inventaire")
	UInventoryComponent* GetInventoryComponent() const { return InventoryComponent; }

	UFUNCTION(BlueprintPure, Category = "Survie")
	USurvivalStatsComponent* GetSurvivalStats() const { return SurvivalStats; }

	/** Sert a l'UI pour afficher la vraie touche mappee, plutot qu'un "E" ecrit en dur. */
	UFUNCTION(BlueprintPure, Category = "Input")
	const UInputAction* GetInteractAction() const { return InteractAction; }

	/**
	 * Coupe les etats maintenus avant de desactiver les inputs du pawn.
	 *
	 * Sprint et saut sont declenches par Started et arretes par Completed.
	 * Si on desactive l'input pendant que la touche est enfoncee, le Completed
	 * n'a plus personne pour l'ecouter et l'etat reste bloque : on rouvrirait
	 * le jeu en sprintant sans toucher a Maj.
	 */
	UFUNCTION(BlueprintCallable, Category = "Input")
	void ClearTransientInputStates();

	/**
	 * Retire des objets d'un slot et les fait apparaitre au sol devant le joueur.
	 * @param Quantity nombre d'exemplaires ; passe un grand nombre pour tout jeter.
	 * @return true si quelque chose est reellement sorti de l'inventaire.
	 */
	UFUNCTION(BlueprintCallable, Category = "Inventaire")
	bool DropSlot(int32 SlotIndex, int32 Quantity);

	/** Vide l'inventaire entier en un tas autour du personnage. */
	UFUNCTION(BlueprintCallable, Category = "Inventaire")
	void DropAllItems();

	UFUNCTION(BlueprintPure, Category = "Survie")
	bool IsDead() const { return bIsDead; }

	/**
	 * Secoue la caméra verticalement. Strength est la vitesse initiale en cm/s
	 * donnée à la caméra ; positif = vers le haut, négatif = vers le bas.
	 * Réutilisable pour une explosion, un recul d'arme, un coup reçu.
	 */
	UFUNCTION(BlueprintCallable, Category = "Camera")
	void AddCameraPunch(float Strength);

	/**
	 * Recopie les reglages du joueur dans le pawn.
	 *
	 * Appelee au BeginPlay ET a chaque fois que USurvivalUserSettings diffuse :
	 * apres un respawn le pawn est un NOUVEL acteur, une valeur poussee une
	 * seule fois serait perdue avec le cadavre. UFUNCTION obligatoire, c'est ce
	 * qui permet le AddDynamic sur le delegue.
	 */
	UFUNCTION()
	void ApplyUserSettings();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	// Appelés par le CharacterMovement quand la capsule change de taille (accroupi)
	virtual void OnStartCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;
	virtual void OnEndCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;

	// Réception au sol et décollage
	virtual void Landed(const FHitResult& Hit) override;
	virtual void OnJumped_Implementation() override;

	// ------------------------------------------------------------------
	// Composants
	// ------------------------------------------------------------------

	/** Caméra placée à hauteur des yeux, suit la rotation du controller. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	TObjectPtr<UCameraComponent> FirstPersonCamera;

	/** Détecte et déclenche les interactions avec le monde. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Interaction")
	TObjectPtr<UInteractionComponent> InteractionComponent;

	/**
	 * Inventaire du joueur. Sur le pawn et non sur le controller :
	 * un cadavre qui garde son butin fait partie du gameplay de survie.
	 */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventaire")
	TObjectPtr<UInventoryComponent> InventoryComponent;

	/** Vie, faim, soif. Sur le pawn : c'est ce corps qui a faim. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Survie")
	TObjectPtr<USurvivalStatsComponent> SurvivalStats;

	// ------------------------------------------------------------------
	// Enhanced Input : assets à assigner dans le Blueprint
	// ------------------------------------------------------------------

	/** Contexte ajouté au joueur au BeginPlay (IMC_Default). */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputMappingContext> DefaultMappingContext;

	/** Déplacement, valeur Axis2D (X = droite/gauche, Y = avant/arrière). */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> MoveAction;

	/** Regard souris / stick droit, valeur Axis2D. */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> LookAction;

	/** Saut, valeur Digital (bool). */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> JumpAction;

	/** Sprint, valeur Digital (bool) — maintenu. */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> SprintAction;

	/** Accroupi, valeur Digital (bool) — maintenu. */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> CrouchAction;

	/** Interaction, valeur Digital (bool). */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> InteractAction;

	/**
	 * Utilise l'objet actif de la barre rapide, valeur Digital (bool).
	 * A mapper sur le clic gauche.
	 *
	 * Bindee sur le PAWN volontairement : quand l'inventaire est ouvert, les
	 * inputs du pawn sont coupes, donc le clic gauche sert au glisser-deposer
	 * de l'UI sans qu'on ait le moindre test a ecrire.
	 */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> UseItemAction;

	// ------------------------------------------------------------------
	// Réglages : mouvement
	// ------------------------------------------------------------------

	/** Vitesse de marche normale (cm/s). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mouvement", meta = (ClampMin = "0"))
	float WalkSpeed = 400.f;

	/** Vitesse en sprint (cm/s). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mouvement", meta = (ClampMin = "0"))
	float SprintSpeed = 650.f;

	/** Vitesse accroupi (cm/s). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mouvement", meta = (ClampMin = "0"))
	float CrouchSpeed = 200.f;

	// ------------------------------------------------------------------
	// Réglages : objets jetés au sol
	// ------------------------------------------------------------------

	/**
	 * Classe engendrée quand on jette un objet. Assigne ton BP_Pickup_Test :
	 * son ItemData est écrasé au spawn, un seul Blueprint suffit pour tout.
	 */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Inventaire|Drop")
	TSubclassOf<APickupItem> DroppedPickupClass;

	/** Distance devant les yeux où l'objet est déposé (cm). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire|Drop", meta = (ClampMin = "10"))
	float DropDistance = 120.f;

	/** Hauteur au-dessus du sol trouvé, pour éviter que l'objet s'y encastre. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire|Drop")
	float DropGroundOffset = 5.f;

	// ------------------------------------------------------------------
	// Réglages : regard
	// ------------------------------------------------------------------

	/**
	 * Sensibilité horizontale : degrés de rotation par unité de souris.
	 * 1.0 = brut (beaucoup trop rapide), ~0.4 est un bon point de départ.
	 *
	 * ATTENTION : ces trois valeurs, comme BaseFOV, sont ECRASEES au BeginPlay
	 * par USurvivalUserSettings. Elles ne servent plus que de repli si l'objet
	 * de réglages est absent ; pour changer la sensibilité, passe par le menu.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.01"))
	float LookSensitivityX = 0.4f;

	/** Sensibilité verticale. Souvent réglée un peu plus basse que l'horizontale. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.01"))
	float LookSensitivityY = 0.4f;

	/** Inverse l'axe vertical de la souris. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
	bool bInvertLookY = false;

	/**
	 * Hauteur de l'oeil AU-DESSUS DES PIEDS, debout (cm).
	 * 160 cm correspond a un personnage d'environ 1m76.
	 * Mesure depuis le sol et non depuis la capsule : c'est ce qui permet
	 * d'annuler le saut instantane de la capsule quand elle retrecit.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
	float EyeHeightStanding = 160.f;

	/** Hauteur de l'oeil au-dessus des pieds, accroupi (cm). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
	float EyeHeightCrouched = 104.f;

	// ------------------------------------------------------------------
	// Réglages : animation d'accroupissement
	// ------------------------------------------------------------------

	/** Durée en secondes de la transition debout <-> accroupi. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Accroupi", meta = (ClampMin = "0.01"))
	float CrouchTransitionDuration = 0.4f;

	/** Tangage (degrés) au pic de la transition. Positif = le regard plonge. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Accroupi")
	float CrouchTiltPitch = 3.f;

	/**
	 * Roulis (degrés) au pic de la transition : bascule sur le côté.
	 * A 0 par defaut, le mouvement reste purement vertical.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Accroupi")
	float CrouchTiltRoll = 0.f;

	// ------------------------------------------------------------------
	// Réglages : impacts caméra (saut / atterrissage)
	// ------------------------------------------------------------------

	/**
	 * Fréquence du ressort de caméra en Hz : à quelle vitesse elle revient
	 * en place après un choc. Plus haut = plus sec.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0.1"))
	float PunchFrequency = 3.f;

	/**
	 * Amortissement du ressort. 1.0 = retour net sans rebond,
	 * 0.5 = un rebond visible, en dessous ça oscille longtemps.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0.05", ClampMax = "2.0"))
	float PunchDamping = 0.85f;

	/**
	 * Durée (s) sur laquelle une impulsion est injectée dans le ressort.
	 * A 0 elle est appliquée d'un coup : la caméra part à pleine vitesse en
	 * une frame, ce qui se lit comme un a-coup. Quelques centiemes suffisent
	 * a rendre l'entree en mouvement progressive.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
	float PunchImpulseRampTime = 0.06f;

	/** Degrés de tangage ajoutés par cm de déplacement vertical de la caméra. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact")
	float PunchPitchPerCm = 0.35f;

	/** Amplitude maximale du déplacement vertical (cm), garde-fou. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
	float PunchMaxOffset = 50.f;

	/**
	 * Impulsion (cm/s) à l'atterrissage, quand l'impact est au maximum.
	 * Ordre de grandeur : le deplacement en cm de la camera vaut environ
	 * cette valeur x 0.018 (avec les reglages de ressort par defaut).
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
	float LandPunchStrength = 2000.f;

	/** En dessous de cette vitesse de chute (cm/s), aucun effet à l'atterrissage. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
	float LandPunchMinFallSpeed = 150.f;

	/**
	 * Vitesse de chute (cm/s) à partir de laquelle l'effet est au maximum.
	 * Un saut sur place retombe a peu pres a JumpZVelocity (450), donc cette
	 * valeur doit rester proche pour qu'un saut normal se sente deja bien.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "1"))
	float LandPunchMaxFallSpeed = 900.f;

	/**
	 * Impulsion (cm/s) au décollage du saut. Positif = la caméra s'enfonce
	 * un instant (compression des jambes) ; mets une valeur négative pour
	 * qu'elle parte vers le haut à la place.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact")
	float JumpPunchStrength = 550.f;

	// ------------------------------------------------------------------
	// Réglages : champ de vision
	// ------------------------------------------------------------------

	/** FOV au repos, en degrés. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|FOV", meta = (ClampMin = "30", ClampMax = "150"))
	float BaseFOV = 90.f;

	/** Degrés de FOV ajoutés en sprint. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|FOV")
	float SprintFOVOffset = 8.f;

	/** Vitesse de transition du FOV. Plus haut = plus brusque. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|FOV", meta = (ClampMin = "0.1"))
	float FOVInterpSpeed = 8.f;

	// ------------------------------------------------------------------
	// Handlers d'input
	// ------------------------------------------------------------------

	void Move(const FInputActionValue& Value);
	void Look(const FInputActionValue& Value);
	void StartSprint();
	void StopSprint();
	void StartCrouch();
	void StopCrouch();
	void Interact();

	/**
	 * Relachement de la touche de sprint / d'accroupissement.
	 *
	 * Separes de StopSprint et StopCrouch, qui restent des ordres inconditionnels :
	 * en mode bascule le relachement ne doit rien faire, alors que la mort ou
	 * l'ouverture de l'inventaire doivent bel et bien couper le sprint.
	 */
	void HandleSprintReleased();
	void HandleCrouchReleased();

	/**
	 * Clic gauche : agit avec l'objet actif de la barre rapide.
	 * Aujourd'hui seuls les consommables font quelque chose ; c'est ici que
	 * viendront les outils et les armes.
	 */
	void UseSelectedItem();

private:
	/** Fait avancer toutes les animations de caméra et les applique. */
	void UpdateCameraEffects(float DeltaTime);

	/** Branche sur OnDied du composant de survie. */
	UFUNCTION()
	void HandleDeath();

	/** Fait apparaitre un pickup deja configure a un endroit donne. */
	APickupItem* SpawnPickup(const struct FInventorySlot& Slot, const FVector& Location);

	/** Descend une position jusqu'au sol, pour ne pas laisser d'objet en l'air. */
	FVector PlaceOnGround(const FVector& Start) const;

	bool bIsDead = false;

	/** Progression de l'animation d'accroupissement : 0 = debout, 1 = accroupi. */
	float CrouchAlpha = 0.f;

	/** Valeur visée par CrouchAlpha. */
	float CrouchTargetAlpha = 0.f;

	/** +1 quand on s'accroupit, -1 quand on se relève : inverse le sens du tilt. */
	float CrouchTiltDirection = 1.f;

	/** Décalage vertical courant de la caméra (cm), état du ressort d'impact. */
	float PunchOffset = 0.f;

	/** Vitesse du ressort d'impact (cm/s). */
	float PunchVelocity = 0.f;

	/** Impulsion restant à injecter dans le ressort, étalée sur PunchImpulseRampTime. */
	float PendingPunch = 0.f;

	/** Vitesse verticale mémorisée pendant la chute, pour calibrer l'atterrissage. */
	float LastFallSpeed = 0.f;

	bool bIsSprinting = false;

	/** Évite de spammer le log si IA_Look est mal configurée. */
	bool bLoggedLookTypeWarning = false;

	// ------------------------------------------------------------------
	// Copies locales des réglages joueur, rafraîchies par ApplyUserSettings
	// ------------------------------------------------------------------

	/** 0 = caméra parfaitement fixe, 1 = amplitude d'origine. */
	float CameraShakeMultiplier = 1.f;

	bool bSprintToggleMode = false;

	bool bCrouchToggleMode = false;
};
