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

#if ENABLE_ANIM_DEBUG && ENABLE_VISUAL_LOG
#define ENABLE_LOCOMOTION_DEBUG 1
#else
#define  ENABLE_LOCOMOTION_DEBUG 0
#endif

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

protected:
	
	//~ Begin UAnimInstance overrides
	virtual void NativeInitializeAnimation() override;
	virtual void NativeUninitializeAnimation() override;
	virtual void NativeBeginPlay() 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 NativeUpdateRotationData(class APawn* owningPawn, const float deltaSeconds);
	virtual void NativeUpdateLocationData(class APawn* owningPawn, const float deltaSeconds);
	virtual void NativeUpdateVelocityData(const TScriptInterface<class IOLSMoveableInterface>& moveableInterface, class APawn* owningPawn, const float deltaSeconds);
	virtual void NativeUpdateAccelerationData(class APawn* owningPawn, const float deltaSeconds);
	virtual void NativeUpdateCharacterStateData(const TScriptInterface<class IOLSMoveableInterface>& moveableInterface,
												class UPawnMovementComponent* movementComponent,
												const float deltaSeconds);
	virtual void NativeUpdateAimingData(class APawn* owningPawn, const float deltaSeconds);

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

	virtual void NativeThreadSafeUpdateRotationData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateLocationData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateVelocityData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateAccelerationData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateWallDetectionHeuristic(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateCharacterStateData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateAimingData(const float deltaSeconds);
	virtual void NativeThreadSafeUpdateRootYawOffset(const float deltaSeconds);

protected: //~ Blueprint thread-safe implementable functions.
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateRotationData(float deltaSeconds);
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateLocationData(float deltaSeconds);
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateVelocityData(float deltaSeconds);
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateAccelerationData(float deltaSeconds);
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateWallDetectionHeuristic(const float deltaSeconds);
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateCharacterData(float detalSeconds);
	
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, meta = (BlueprintThreadSafe))
	void BlueprintThreadSafeUpdateAimingData(float deltaSeconds);

public:

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	class UPawnMovementComponent* GetMovementComponent() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	const FVector& GetWorldVelocity() const;

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	const float& GetLocalVelocityDirectionAngle() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	const float& GetDisplacementSpeed() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	const FVector& GetLocalAcceleration2D() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsLookingOrAimingDirection() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsVelocityOrLookingDirection() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsVelocityOrAimingDirection() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsAimingDirection() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsLookingDirection() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsVelocityDirection() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsCrouching() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool HasVelocity() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool HasAcceleration() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool ShouldDistanceMatchStop() const;

	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	bool IsMovingPerpendicularToInitialPivot() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	const EOLSRotationMode& GetRotationMode() const;
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	const float& GetOwningPawnMaxSpeed() const;

protected:

	UFUNCTION(BlueprintCallable)
	void SetRootYawOffset(const float rootYawOffset);
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	void ProcessTurnYawCurve();
	
	UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe))
	void ProcessTurnYawCurve_ForwardFacing();
	
protected:
	
	UFUNCTION(BlueprintCallable, Category = "Masking", meta = (BlueprintThreadSafe))
	float CalculateMeshSpaceBlendWeight(const FName localSpaceLayerCurveName) const;

protected:

	UFUNCTION(BlueprintCallable, Category = "Components", meta = (BlueprintThreadSafe))
	class UOLSLocomotionComponent* ThreadSafeGetLocomotionComponent() const;
	
protected:

	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|LocationData")
	FVector OwningPawnLocation = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|RotationData")
	FRotator OwningPawnRotation = FRotator::ZeroRotator;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|VelocityData")
	FVector OwningPawnVelocity = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|AccelerationData")
	FVector OwningPawnAcceleration = FVector::ZeroVector;

	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|CharacterStateData")
	float OwningPawnGravityZ = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|CharacterStateData")
	float OwningPawnMaxSpeed = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|CharacterStateData")
	TEnumAsByte<EMovementMode> OwningPawnMovementMode = MOVE_None;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|CharacterStateData")
	uint8 bIsOwningPawnOnGround : 1 = false;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|CharacterStateData")
	uint8 bIsOwningPawnCrouching : 1 = false;
	
	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|CharacterStateData")
	uint8 bIsOwningOrientRotationToMovement : 1 = false;

	UPROPERTY(BlueprintReadOnly, Category = "PawnInfo|AimingData")
	FRotator OwningPawnAimRotation = FRotator::ZeroRotator;

protected:

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|RotationData")
	FRotator WorldRotation = FRotator::ZeroRotator;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|RotationData")
	float YawDeltaSinceLastUpdate = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|RotationData")
	float AdditiveLeanAngle = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|RotationData")
	float YawDeltaSpeed = 0.f;

protected:

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|LocationData")
	FVector WorldLocation = FVector::ZeroVector;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|LocationData")
	float DisplacementSinceLastUpdate = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|LocationData")
	float DisplacementSpeed = 0.f;

protected:

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	FVector WorldVelocity = FVector::ZeroVector;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	FVector LocalVelocity2D = FVector::ZeroVector;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	float LocalVelocityDirectionAngle = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	float LocalVelocityDirectionAngleWithOffset = 0.f;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	EOLSCardinalDirection LocalVelocityDirection = EOLSCardinalDirection::EForward;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	EOLSCardinalDirection LocalVelocityDirectionNoOffset = EOLSCardinalDirection::EForward;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|VelocityData")
	uint8 bHasVelocity : 1;

protected:
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|AccelerationData")
	FVector LocalAcceleration2D = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|AccelerationData")
	uint8 bHasAcceleration : 1;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|AccelerationData")
	FVector PivotDirection2D = FVector::ZeroVector;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|AccelerationData")
	EOLSCardinalDirection CardinalDirectionFromAcceleration = EOLSCardinalDirection::EForward;

protected:
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bIsCrouching : 1 = false;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bHasCrouchStateChanged : 1 = false;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bHasADSStateChanged : 1 = false;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bWasADSLastUpdate : 1 = false;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	float TimeSinceFiredWeapon = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bIsJumping : 1 = false;
		
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bIsFalling : 1 = false;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	float TimeToJumpApex = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|CharacterStateData")
	uint8 bIsRunningIntoWall : 1 = false;

protected:

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|TurnInPlaceData")
	float RootYawOffset = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|TurnInPlaceData")
	FFloatSpringState RootYawOffsetSpringState;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlaceData")
	EOLSRootYawOffsetMode RootYawOffsetMode = EOLSRootYawOffsetMode::EBlendOut;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlaceData")
	float TurnYawCurveValue = 0.f;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|TurnInPlaceData")
	float TurnInPlaceRotationDirection = 0.f;

	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|TurnInPlaceData")
	float TurnInPlaceRecoveryDirection = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlaceData")
	float MaxTurnYawValue = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|TurnInPlaceData")
	float CachedTurnYawCurveValue = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|TurnInPlaceData")
	uint8 bHasReachedEndTurn : 1 = false;
	
protected:
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ThreadSafe|GameplayTagBindings")
	bool bGameplayTag_IsADS = false;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ThreadSafe|GameplayTagBindings")
	bool bGameplayTag_IsFiring = false;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ThreadSafe|GameplayTagBindings")
	bool bGameplayTag_IsDodging = false;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ThreadSafe|GameplayTagBindings")
	bool bGameplayTag_IsMeleeAttack = false;

protected:
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|AimingData")
	float AimPitch = 0.f;
	
	UPROPERTY(BlueprintReadOnly, Category = "ThreadSafe|AimingData")
	float AimYaw = 0.f;

protected:
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	float PivotInitialVelocityDirection = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	EOLSCardinalDirection PivotInitialDirection = EOLSCardinalDirection::EForward;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	float LastPivotTime = 0.f;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	EOLSCardinalDirection StartDirection = EOLSCardinalDirection::EForward;

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	EOLSGait StopInitialGait = EOLSGait::EWalk;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	EOLSCardinalDirection StopLocalVelocityCardinalDirection = EOLSCardinalDirection::EForward;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	uint8 bHasGaitChanged : 1 = false;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	uint8 bHasRotationModeChanged : 1 = false;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	uint8 bHasLinkedLayerChanged : 1 = false;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	EOLSGait LastGait = EOLSGait::EWalk;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	EOLSRotationMode LastRotationMode = EOLSRotationMode::EVelocityDirection;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionSMData")
	TObjectPtr<class UAnimInstance> LastLinkedLayer = nullptr;

protected:

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionComponentData")
	EOLSGait DesiredGait = EOLSGait::EWalk;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionComponentData")
	EOLSGait Gait = EOLSGait::EWalk;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionComponentData")
	EOLSGait PrevGait = EOLSGait::EWalk;

	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionComponentData")
	EOLSStance Stance = EOLSStance::EStanding;
	
	UPROPERTY(BlueprintReadWrite, Category = "ThreadSafe|LocomotionComponentData")
	EOLSRotationMode RotationMode = EOLSRotationMode::EVelocityDirection;
	
protected:
	
#if WITH_EDITORONLY_DATA
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Debug")
	uint8 bEnableDebug : 1;
#endif
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|TurnInPlaceData")
	uint8 bEnableRootYawOffset : 1 = true;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|TurnInPlaceData")
	FName TurnYawWeightCurveName = NAME_None;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|TurnInPlaceData")
	FName RemainingTurnYawCurveName = NAME_None;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|VelocityData")
	float CardinalDirectionDeadZone = 10.f;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|GameplayTagBindings")
	FGameplayTag ADSGameplayTag = FGameplayTag::EmptyTag;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|GameplayTagBindings")
	FGameplayTag FiringGameplayTag = FGameplayTag::EmptyTag;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|GameplayTagBindings")
	FGameplayTag DodgingGameplayTag = FGameplayTag::EmptyTag;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|GameplayTagBindings")
	FGameplayTag MeleeGameplayTag = FGameplayTag::EmptyTag;

protected:

	UPROPERTY(BlueprintReadOnly)
	uint8 bIsFirstUpdate : 1 = true;
	
protected: // References
	
	UPROPERTY(Transient, DuplicateTransient)
	TObjectPtr<class APawn> OwningPawn = nullptr;

	UPROPERTY(Transient, DuplicateTransient)
	TObjectPtr<class UPawnMovementComponent> MovementComponent = nullptr;
	
	UPROPERTY(Transient, DuplicateTransient)
	TObjectPtr<class UOLSLocomotionComponent> LocomotionComponent = nullptr;

	TScriptInterface<class IOLSMoveableInterface> MoveableInterface = nullptr;

protected:

	FVector2D RootYawOffsetAngleClamp = FVector2D(-180.f, 180.f);
};
