// © 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 "GameplayTagContainer.h"
#include "Animation/AnimInstance.h"
#include "Data/OLSAnimationData.h"
#include "Data/OLSEnumLibrary.h"
#include "Data/OLSLocomotionData.h"
#include "OLSBaseLinkedLayerAnimInstance.generated.h"

/**
 * 
 */
UCLASS()
class OLSANIMATION_API UOLSBaseLinkedLayerAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

protected:

	//~ Begin UAnimInstance overrides
	virtual void NativeInitializeAnimation() override;
	virtual void NativeUpdateAnimation(float deltaSeconds) override;
	virtual void NativeThreadSafeUpdateAnimation(float deltaSeconds) override;
	virtual void NativePostEvaluateAnimation() override;
	//~ End UAnimInstance overrides

protected: //~ Native update functions.

	virtual void NativeUpdateEssentialValues(const float deltaSeconds);

protected: //~ Native thread-safe update functions.

	virtual void NativeUpdateSkeletonControlData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateLocomotionStateData(const float deltaSeconds);

protected: //~ Blueprint thread-safe update functions.

	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateLocomotionStateData(const float deltaSeconds);

protected: 

	virtual EOLSHipDirection GetHipDirection() const;
	FOLSStanceAnimSets GetStanceAnimSets(const bool isCrouching) const;
	const FOLSGaitAnimSets& GetGaitAnimSets(const bool isCrouching, const EOLSGait& locomotionState) const;
	
protected:

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectIdleAnimation(const bool isCrouching, const bool isAiming) const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectIdleEntryAnimation(const bool isCrouching) const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectIdleBreakAnimation(const bool isCrouching) const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectTurnInPlaceAnimation(const bool isCrouching, const float direction = 0.f) const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectStartCycleAnimation(const bool isCrouching,
												   const bool isAiming,
												   const EOLSGait gait,
												   const EOLSCardinalDirection velocityDirection,
												   const EOLSHipDirection hipDirection,
												   const float angle) const;

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectCycleAnimation(const bool isCrouching,
	                                          const bool isAiming,
	                                          const EOLSGait gait,
	                                          const EOLSCardinalDirection velocityDirection,
	                                          const EOLSHipDirection hipDirection) const;

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectPivotAnimation(const bool isCrouching,
											  const bool isAiming,
											  const EOLSGait gait,
											  const EOLSCardinalDirection velocityDirection,
											  const EOLSHipDirection hipDirection,
											  const float angle) const;

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	class UAnimSequence* SelectStopCycleAnimation(const bool isCrouching,
	                                              const bool isAiming,
	                                              const EOLSGait gait,
	                                              const EOLSCardinalDirection velocityDirection,
	                                              const EOLSHipDirection hipDirection) const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|Selectors")
	FVector2D SelectPlayRateByLocomotionState(const bool isCrouching, const bool isAiming, const EOLSGait gait, const EOLSLocomotionStatePlayRate locomotionState) const;

protected:
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|IdleBreaks")
	bool CanPlayIdleBreakAnimation(const bool isCrouching) const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|IdleBreaks")
	void ProcessIdleBreakTransitionLogic(const bool isCrouching, const float deltaSeconds);
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|IdleBreaks")
	void ResetIdleBreakTransitionLogic();
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|IdleBreaks")
	void DetermineIdleBreakDelayTime();

protected:
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe), Category = "ThreadSafe|IdleBreaks")
	void ProcessTurnInPlaceTransitionLogic(const float angle, const float turnRate, const float deltaSeconds);
	
protected:
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	class UOLSBaseLayerAnimInstance* GetBaseMainAnimInstance() const;

protected:

	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	FVector WorldVelocity = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	float DisplacementSpeed = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	float LocalVelocityDirectionAngle = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	FVector LocalAcceleration2D = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	uint8 bIsLookingOrAimingDirection : 1;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	uint8 bIsVelocityOrAimingDirection : 1;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	EOLSRotationMode RotationMode = EOLSRotationMode::EVelocityDirection;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	uint8 bIsCrouching : 1;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	uint8 bHasAcceleration : 1;
	
	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	uint8 bHasVelocity : 1;

	UPROPERTY(BlueprintReadOnly, Category = "BaseMainAnimInstance|Cache")
	float OwningPawnMaxSpeed = 0.f;
	
protected:

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlace")
	float Angle = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlace")
	float TurnInPlaceAnimTime = 0.f;

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlace")
	float TurnInPlaceRotationDirection = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlace")
	float TurnInPlaceRecoveryDirection = 0.f;
	
	UPROPERTY(BlueprintReadOnly , Category = "ThreadSafe|TurnInPlace")
	float TurnInPlaceDelayTime = 0.f;
	
	UPROPERTY(BlueprintReadWrite , Category = "ThreadSafe|TurnInPlace")
	uint8 bCanTurnInPlace : 1 = false;
	
	UPROPERTY(BlueprintReadWrite , Category = "ThreadSafe|TurnInPlace")
	uint8 bCanRotateInPlace : 1 = false;
	
protected:

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|StrideWarping")
	float StrideWarpingStartAlpha = 0.f;

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|StrideWarping")
	float StrideWarpingPivotAlpha = 0.f;

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|StrideWarping")
	float StrideWarpingCycleAlpha = 0.f;

protected:

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|OrientationWarping")
	float OrientationWarpingAlpha = 0.f;

protected:
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|Pivots")
	FVector PivotStartAcceleration = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|Pivots")
	float PivotStartAngle = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|Pivots")
	float TimeAtPivotStop = 0.f;

protected:

	UPROPERTY(BlueprintReadOnly , Category = "ThreadSafe|IdleBreaks")
	float TimeUntilNextIdleBreak = 0.f;
	
	UPROPERTY(BlueprintReadOnly , Category = "ThreadSafe|IdleBreaks")
	float IdleBreakDelayTime = 0.f;
	
protected:

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionStateData")
	EOLSHipDirection HipDirection = EOLSHipDirection::EForward;

protected:

#if WITH_EDITORONLY_DATA
	UPROPERTY(BlueprintReadOnly, Category = "Settings|Debug")
	uint8 bEnableDebug : 1;
#endif
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|LocomotionStateData")
	uint8 bUseHipDirection : 1;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|LocomotionStateData")
	FVector2D HipDirectionAngleClamp = FVector2D::ZeroVector;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|LocomotionStateData")
	float HipDirectionBuffer = 5.f;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|AnimSet")
	FOLSStanceAnimSets StandingAnimSets;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|AnimSet")
	FOLSStanceAnimSets CrouchingAnimSets;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|AnimSet|DistanceMatching")
	FName GroundDistanceCurveName = NAME_None;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|AnimSet|DistanceMatching")
	FName JumpDistanceCurveName = NAME_None;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|StrideWarping")
	float StrideWarpingBlendInDurationScaled = .2f;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings|StrideWarping")
	float StrideWarpingBlendInStartOffset = .15f;

protected:

	UPROPERTY(Transient)
	TObjectPtr<class UOLSBaseLayerAnimInstance> BaseMainAnimInstance = nullptr;
};
