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

#pragma once

#include "CoreMinimal.h"
#include "Components/WidgetComponent.h"
#include "PlayerNameplateComponent.generated.h"

class APlayerState;

/**
 * L'etiquette de pseudo posee au-dessus d'un joueur.
 *
 * Herite de UWidgetComponent et le configure en espace ECRAN : c'est ce qui la
 * fait toujours faire face a la camera, gratuitement et exactement, sans le
 * moindre calcul d'orientation par frame. En espace Monde il faudrait la
 * reorienter soi-meme a chaque image, et elle serait occultee par le decor --
 * un pseudo qui disparait derriere un caillou n'aide personne.
 *
 * L'espace ecran n'applique AUCUNE echelle de distance -- verifie dans
 * SWorldWidgetScreenLayer::Tick, qui ne fait que projeter la position. Le
 * pseudo garderait donc une taille en pixels constante pendant que le
 * personnage, lui, retrecit : l'oeil lit ca comme une etiquette qui GROSSIT en
 * s'eloignant. C'est pourquoi l'echelle est calculee ici, a la main, en 1/d
 * comme le ferait une vraie perspective.
 *
 * L'opacite suit le meme chemin : une etiquette qui s'eteint d'un coup a la
 * distance limite se remarque bien plus qu'un fondu, alors qu'elle est censee
 * ne pas se faire remarquer du tout.
 *
 * Le composant ne replique RIEN : le pseudo vit deja sur le PlayerState, qui
 * est replique par le moteur. Ajouter une propriete ici enverrait deux fois la
 * meme chaine sur le reseau.
 */
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
class EMBERWILD_API UPlayerNameplateComponent : public UWidgetComponent
{
	GENERATED_BODY()

public:
	UPlayerNameplateComponent();

	virtual void BeginPlay() override;

	/**
	 * Un Tick ici, contrairement au reste du projet.
	 *
	 * La regle "jamais de Tick pour lire un etat" vise les widgets qui
	 * interrogent une valeur au lieu de s'abonner a un delegue. Ce n'est pas le
	 * cas : on interpole en continu deux grandeurs geometriques -- echelle et
	 * opacite -- qui changent a chaque pas du joueur. Sur un timer a 0,25 s
	 * l'etiquette grossirait par paliers visibles.
	 *
	 * Le cout est nul en pratique : UWidgetComponent tick deja en espace ecran
	 * pour se projeter, on ajoute une distance et deux setters.
	 */
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

protected:
	/**
	 * Au-dela de cette distance, l'etiquette disparait. 2500 cm = 25 m, soit un
	 * peu plus que la portee a laquelle on distingue un coequipier dans un
	 * decor dense.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate", meta = (ClampMin = "100", Units = "cm"))
	float MaxDrawDistance = 2500.f;

	/**
	 * Distance a laquelle l'etiquette est a sa taille de reference, c'est-a-dire
	 * exactement celle dessinee dans le WBP. Plus pres elle grossit, plus loin
	 * elle retrecit -- en 1/d, comme une vraie perspective.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Echelle", meta = (ClampMin = "50", Units = "cm"))
	float ReferenceDistance = 600.f;

	/**
	 * Bornes de l'echelle. Le minimum evite un pseudo reduit a une bouillie de
	 * deux pixels ; le maximum evite qu'un coequipier colle a la camera ne
	 * remplisse l'ecran de son nom.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Echelle", meta = (ClampMin = "0.05"))
	float MinScale = 0.4f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Echelle", meta = (ClampMin = "0.1"))
	float MaxScale = 1.4f;

	/**
	 * Longueur du fondu, comptee EN DECA de MaxDrawDistance : l'opacite passe de
	 * 1 a 0 sur cette bande, puis l'etiquette est carrement eteinte.
	 *
	 * Exprimee comme une longueur et non comme une distance de depart : une
	 * distance de depart superieure a MaxDrawDistance serait un reglage invalide
	 * silencieux, alors qu'une longueur se borne toute seule.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Fondu", meta = (ClampMin = "0", Units = "cm"))
	float FadeLength = 700.f;

	/**
	 * Cacher l'etiquette de son PROPRE personnage.
	 *
	 * Vrai par defaut : en vue premiere personne on ne voit jamais son propre
	 * corps, mais une etiquette en espace ecran, elle, se projetterait en plein
	 * milieu du viseur.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate")
	bool bHideOnOwningPlayer = true;

private:
	/** Le PlayerState du pawn porteur, ou nullptr tant qu'il n'est pas arrive. */
	APlayerState* GetOwnerPlayerState() const;

	/** Recopie le pseudo dans le widget, uniquement s'il a change. */
	void RefreshName();

	/**
	 * Dernier pseudo reellement ecrit dans le widget.
	 *
	 * Le PlayerState arrive APRES le pawn chez un client : la premiere passe
	 * trouve donc un nom vide, et il faut continuer a regarder. Comparer plutot
	 * que reecrire evite de reconstruire la mise en page du Text Block quatre
	 * fois par seconde pour une chaine identique.
	 */
	FString LastShownName;
};
