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

#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "FpsArmsAnimInstance.generated.h"

class AFpsPlayer;

/**
 * AnimInstance des bras en vue premiere personne.
 *
 * Elle ne DECIDE de rien : elle lit l'etat du pawn une fois par frame et le
 * republie sous forme de variables. C'est la machine a etats du Blueprint enfant
 * (ABP_FpsArms) qui choisit l'animation. La separation est volontaire -- une
 * transition d'animation se regle a l'oeil, avec des durees de fondu, et ca n'a
 * rien a faire dans du C++ qu'il faut recompiler.
 *
 * Corollaire : ces variables ne sont JAMAIS repliquees. Les bras ne sont vus que
 * par leur proprietaire, donc chaque machine calcule les siens a partir d'un etat
 * de pawn que la replication a deja synchronise (vitesse, sprint, accroupi).
 */
UCLASS()
class SURVIVAL_PROJET_API UFpsArmsAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:
	virtual void NativeInitializeAnimation() override;
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

protected:
	// ------------------------------------------------------------------
	// Etat publie vers le Blueprint d'animation
	// ------------------------------------------------------------------

	/** Vitesse horizontale en cm/s. Sert aussi a moduler le PlayRate de la course. */
	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	float GroundSpeed = 0.f;

	/** Vrai quand le joueur se deplace REELLEMENT, freinage exclu. */
	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	bool bIsMoving = false;

	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	bool bIsSprinting = false;

	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	bool bIsFalling = false;

	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	bool bIsCrouching = false;

	/**
	 * Vitesse de lecture du cycle de deplacement, a brancher sur la broche
	 * Play Rate du noeud d'animation.
	 *
	 * Le projet a trois vitesses de jeu (accroupi 200, marche 400, sprint 650)
	 * pour un seul cycle d'animation. Plutot qu'un etat par vitesse -- trois
	 * seuils, donc trois occasions de claquer quand on les franchit -- on etire
	 * le meme cycle. La cadence des bras suit alors la vitesse en continu, y
	 * compris pendant l'acceleration.
	 *
	 * Un vrai etat Sprint n'aura de sens que le jour ou l'animateur livrera une
	 * animation de sprint a la POSTURE differente. Le meme cycle joue plus vite
	 * n'est pas une animation de sprint.
	 */
	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	float LocomotionPlayRate = 1.f;

	/**
	 * En dessous de cette vitesse (cm/s) on considere le joueur immobile.
	 *
	 * Un seuil strictement a zero ferait clignoter la transition course/repos
	 * pendant le freinage, ou la vitesse decroit en passant par des valeurs
	 * minuscules mais non nulles.
	 */
	UPROPERTY(EditDefaultsOnly, Category = "Movement", meta = (ClampMin = "0"))
	float MovingSpeedThreshold = 10.f;

	/**
	 * Vitesse (cm/s) a laquelle le cycle tourne a sa cadence d'origine.
	 *
	 * A regler a l'oeil dans ABP_FpsArms : c'est la vitesse ou les bras ont l'air
	 * naturels sans etirement. Par defaut la vitesse de marche du pawn, donc le
	 * sprint accelere d'environ 60 %.
	 */
	UPROPERTY(EditDefaultsOnly, Category = "Movement", meta = (ClampMin = "1"))
	float ReferenceLocomotionSpeed = 400.f;

	/**
	 * Bornes du Play Rate. Sans le plancher, un joueur qui rampe a 20 cm/s
	 * regarderait des bras quasi immobiles ; sans le plafond, une future vitesse
	 * bonus donnerait des bras en accelere qui sortent du registre credible.
	 */
	UPROPERTY(EditDefaultsOnly, Category = "Movement", meta = (ClampMin = "0.1"))
	float MinLocomotionPlayRate = 0.6f;

	UPROPERTY(EditDefaultsOnly, Category = "Movement", meta = (ClampMin = "0.1"))
	float MaxLocomotionPlayRate = 1.8f;

private:
	/**
	 * Cache du pawn. TryGetPawnOwner() fait un Cast a chaque appel, et cette
	 * fonction tourne a chaque frame sur un mesh anime.
	 *
	 * TWeakObjectPtr et pas un pointeur nu : apres un respawn le pawn est detruit,
	 * et cet objet peut lui survivre d'une frame.
	 */
	TWeakObjectPtr<AFpsPlayer> OwningPlayer;
};
