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

#pragma once

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

class AFpsPlayer;

/**
 * AnimInstance du corps vu a la TROISIEME personne -- celui que les autres
 * joueurs voient, et dont on ne percoit soi-meme que l'ombre.
 *
 * Meme partage des roles que UFpsArmsAnimInstance : elle ne DECIDE de rien, elle
 * lit l'etat du pawn une fois par frame et le republie en variables. C'est la
 * machine a etats du Blueprint enfant qui choisit l'animation, parce qu'une
 * transition se regle a l'oeil avec des durees de fondu et n'a rien a faire dans
 * du C++ qu'il faut recompiler.
 *
 * La difference avec les bras est reseau, et elle est essentielle : les bras ne
 * sont vus que par leur proprietaire, ce corps-ci est vu par TOUT LE MONDE.
 * Chaque machine evalue donc les quatre corps de la partie. Et pourtant rien
 * n'est replique ici -- tout est DERIVE d'un etat que le moteur ou le projet
 * synchronisent deja :
 *
 *   Velocity      -> replique par le mouvement du Character
 *   bIsCrouched   -> UPROPERTY repliquee d'ACharacter
 *   bIsSprinting  -> repliquee par AFpsPlayer (c'est a ca qu'elle sert)
 *   AimPitch      -> RemoteViewPitch, replique par APawn sans rien demander
 *
 * Repliquer une seule de ces variables serait envoyer deux fois la meme
 * information -- exactement le reproche fait a UPlayerNameplateComponent.
 */
UCLASS()
class EMBERWILD_API UPlayerBodyAnimInstance : 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 : l'axe du BlendSpace de deplacement. */
	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	float GroundSpeed = 0.f;

	/**
	 * Direction du deplacement par rapport a l'orientation du corps, en degres :
	 * 0 = on avance, 90 = on va vers la droite, 180 = on recule.
	 *
	 * Elle est non nulle en permanence dans ce jeu et ce n'est pas un detail :
	 * bUseControllerRotationYaw garde le corps aligne sur la camera, donc un pas
	 * de cote est un VRAI pas de cote, jamais un demi-tour. Le pack ne livre que
	 * des cycles vers l'avant, donc personne ne la lit encore ; elle est publiee
	 * parce que le jour ou les cycles lateraux arriveront, il n'y aura qu'un
	 * BlendSpace a brancher et pas une classe a rouvrir.
	 */
	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	float LocomotionDirection = 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;

	/**
	 * Vitesse verticale en cm/s : positif = on monte, negatif = on tombe.
	 *
	 * C'est elle, et elle seule, qui distingue un SAUT d'une CHUTE. bIsFalling
	 * vaut vrai dans les deux cas : le moteur appelle "falling" tout ce qui n'est
	 * pas au sol, detente comprise. Sans ce signe, sortir d'une falaise jouerait
	 * l'animation de detente -- un personnage qui prend son elan pour tomber.
	 *
	 * Gratuite sur le reseau comme le reste : le CharacterMovement replique deja
	 * la velocite des pawns distants, on ne fait que lire sa composante Z.
	 */
	UPROPERTY(BlueprintReadOnly, Category = "Movement")
	float VerticalSpeed = 0.f;

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

	/**
	 * Tangage du regard, en degres, borne a -90..90. Positif = le joueur regarde
	 * vers le haut.
	 *
	 * Gratuit sur le reseau : APawn compresse et replique deja RemoteViewPitch
	 * pour ses pawns distants, et GetBaseAimRotation() rend la bonne valeur des
	 * deux cotes sans qu'on ait une seule propriete a declarer. Personne ne la
	 * lit tant qu'il n'y a pas d'AimOffset -- mais le jour ou un ami doit voir
	 * qu'on regarde le ciel, c'est cette variable qu'on branchera.
	 */
	UPROPERTY(BlueprintReadOnly, Category = "Aim")
	float AimPitch = 0.f;

	/**
	 * Vitesse de lecture du cycle de deplacement.
	 *
	 * A NE BRANCHER QUE si l'on joue une animation unique. Avec le BlendSpace du
	 * pack, qui interpole deja marche et course sur l'axe de la vitesse, la
	 * brancher etirerait une seconde fois un cycle deja etire et donnerait une
	 * cadence qui accelere deux fois trop vite. Elle reste ici parce que le
	 * BlendSpace est calibre sur les vitesses du pack et pas sur les notres
	 * (200 / 400 / 650) : si la foulee patine, c'est le reglage a sortir.
	 */
	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. */
	UPROPERTY(EditDefaultsOnly, Category = "Movement", meta = (ClampMin = "1"))
	float ReferenceLocomotionSpeed = 400.f;

	/** Bornes du Play Rate, memes garde-fous que sur les bras. */
	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 QUATRE corps en partie a quatre.
	 *
	 * 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;
};
