// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Data/OLSLocomotionData.h"
#include "OLSLocomotionComponent.generated.h"

DECLARE_MULTICAST_DELEGATE_OneParam(FOLSStanceNativeDelegate, const EOLSStance& /* = prevStance */)
DECLARE_MULTICAST_DELEGATE_OneParam(FOLSGaitNativeDelegate, const EOLSGait& /* = prevGait */)
DECLARE_MULTICAST_DELEGATE_OneParam(FOLSRotationModeStateNativeDelegate, const EOLSRotationMode& /* = prevRotationMode*/)

UCLASS(ClassGroup=(OLS), meta=(BlueprintSpawnableComponent))
class OLSANIMATION_API UOLSLocomotionComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	// Sets default values for this component's properties
	UOLSLocomotionComponent(const FObjectInitializer& objectInitializer);

protected:

	//~ UActorComponent BEGIN
	virtual void PostInitProperties() override;
	virtual void TickComponent(float deltaTime, ELevelTick tickType,
	                           FActorComponentTickFunction* thisTickFunction) override;
	//~ UActorComponent END

public:

	virtual void Initialize();
	
protected:

	EOLSGait GetAllowedGait() const;
	EOLSGait GetActualGait(const EOLSGait& allowedLocomotionState) const;

	virtual void UpdateEssentialValues(const float deltaTime);
	virtual void UpdateGait(const float deltaTime);

	FVector GetVelocity() const;
	float GetAnimCurve(const FName& curveName) const;
	
public:
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	const EOLSStance& GetStance() const;

	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	void SetStance(EOLSStance newStance, bool shouldForce = false);
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	const EOLSGait& GetGait() const;
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	const EOLSGait& GetDesiredGait() const;
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	void SetGait(EOLSGait newGait, bool shouldForce = false);
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	const EOLSRotationMode& GetRotationMode() const;
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	void SetRotationMode(EOLSRotationMode newRotationMode, bool shouldForce = false);
	
public:

	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	void SetDesiredGait(const EOLSGait newGait, const bool shouldForce = false);
	
	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	void SetDesiredStance(const EOLSStance newStance);

	UFUNCTION(BlueprintCallable, Category = "OLSLocomotionComponent")
	void SetDesiredRotationMode(const EOLSRotationMode newRotationMode);
	
protected:

	void OnStanceChanged(EOLSStance previousStance);
	void OnDesiredGaitChanged(EOLSGait previousDesiredGait);
	void OnGaitChanged(EOLSGait previousGait);
	void OnRotationModeChanged(EOLSRotationMode prevRotationMode);

protected:

	virtual bool CanSprint() const;
	virtual bool CanJog() const;

public:

	FOLSStanceNativeDelegate& GetOnStanceChangedNativeDelegate();
	FOLSGaitNativeDelegate& GetOnDesiredGaitChangedNativeDelegate();
	FOLSGaitNativeDelegate& GetOnGaitChangedNativeDelegate();
	FOLSRotationModeStateNativeDelegate& GetOnRotationModeChangedNativeDelegate();
	
protected:

	FOLSStanceNativeDelegate OnStanceChangedNativeDelegate;
	FOLSGaitNativeDelegate OnDesiredGaitChangedNativeDelegate;
	FOLSGaitNativeDelegate OnGaitChangedNativeDelegate;
	FOLSRotationModeStateNativeDelegate OnRotationModeChangedNativeDelegate;

protected:
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Setup | Stance")
	EOLSStance DesiredStance = EOLSStance::EStanding;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Setup | Rotation")
	EOLSRotationMode DesiredRotationMode = EOLSRotationMode::EVelocityDirection;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Setup | Locomotion")
	EOLSGait DesiredGait = EOLSGait::EJog;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Setup | Locomotion")
	TMap<EOLSGait, float> Gaits;

protected:
	
	UPROPERTY(BlueprintReadOnly)
	EOLSStance Stance = EOLSStance::EStanding;
	
	UPROPERTY(BlueprintReadOnly)
	EOLSGait Gait = EOLSGait::EWalk;
	
	UPROPERTY(BlueprintReadOnly)
	EOLSRotationMode RotationMode = EOLSRotationMode::ELookingDirection;

protected: /** Essential Information */
	
	UPROPERTY(BlueprintReadOnly, Category = "OLS|Essential Information")
	FVector Acceleration = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "OLS|Essential Information")
	float MovementInputAmount = 0.0f;
	
	UPROPERTY(BlueprintReadOnly, Category = "OLS|Essential Information")
	float Speed = 0.0f;

private:
	
	float MaxSpeed = 0.f;
	
private:

	UPROPERTY(Transient, DuplicateTransient)
	TObjectPtr<APawn> OwningPawn = nullptr;
	
	UPROPERTY(Transient, DuplicateTransient)
	TObjectPtr<class UAnimInstance> OwningAnimInstance = nullptr;
	
	TScriptInterface<class IOLSMoveableInterface> MoveableInterface = nullptr;
};
