// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Engine/EngineTypes.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MovementComponentState.generated.h"

//////////////////////////////////////////////////////////////
//
// Very often, the animation blueprint of a character will
// want to know the state of the character movement
// component, including such things as Velocity,
// Acceleration, IsFalling, etc.  However, the movement
// component cannot be accessed directly from the
// animation graph, since it runs on a worker thread.
// By copying the data into this plain struct, the
// animation graph can safely read it.
//
//////////////////////////////////////////////////////////////

USTRUCT(BlueprintType)
struct INTEGRATION_API FlxMovementComponentState
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
	FVector Velocity = FVector::ZeroVector;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
	bool bIsAccelerating = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
	float MaxWalkSpeed = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
	TEnumAsByte<EMovementMode> MovementMode = MOVE_None;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
	bool bIsFalling = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
	bool bIsCrouching = false;

	FlxMovementComponentState() = default;
	explicit FlxMovementComponentState(class UCharacterMovementComponent *CMC);
};

UCLASS()
class INTEGRATION_API UlxMovementComponentStateLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

public:
	///////////////////////////////////////////////////////////
	//
	// True field getters.
	//
	///////////////////////////////////////////////////////////
	
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static FVector GetVelocity(const FlxMovementComponentState &State) { return State.Velocity; }

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static bool GetIsAccelerating(const FlxMovementComponentState &State) { return State.bIsAccelerating; }

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static float GetMaxWalkSpeed(const FlxMovementComponentState &State) { return State.MaxWalkSpeed; }

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static EMovementMode GetMovementMode(const FlxMovementComponentState &State) { return State.MovementMode; }

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static bool GetIsFalling(const FlxMovementComponentState &State) { return State.bIsFalling; }

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static bool GetIsCrouching(const FlxMovementComponentState &State) { return State.bIsCrouching; }

	///////////////////////////////////////////////////////////
	//
	// These are not true getters, but actually calculate
	// simple values from the existing fields.
	//
	///////////////////////////////////////////////////////////
	 
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static float GetGroundSpeed(const FlxMovementComponentState &State) { return State.Velocity.Size2D(); }

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
	static bool GetShouldMove(const FlxMovementComponentState &State);

	///////////////////////////////////////////////////////////
	//
	// Debugging.
	//
	///////////////////////////////////////////////////////////

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State")
	static FString DebugString(const FlxMovementComponentState &State);

	// Allows you to pass a MovementComponentState to Format Message.
	//
	UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Movement Component State")
	static FFormatArgumentData FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name);

	///////////////////////////////////////////////////////////
	//
	// Other operations.
	//
	///////////////////////////////////////////////////////////

	// Get a snapshot the Movement Component State.
	//
	// Normally, this just pulls data directly from the
	// character movement component. However, we sometimes
	// need to disable the movement component, especially
	// during cutscenes.  We provide an alternative 'Fake
	// Movement Component State' stored on the actor's
	// UlxTangible. When the movement component's mode is
	// MOVE_None, this function returns the fake movement
	// component state instead.  The fake movement component
	// state can be updated manually in blueprints.
	//
	UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (DefaultToSelf = "AnimInstance"))
	static FlxMovementComponentState GetMovementComponentState(UAnimInstance *AnimInstance);

	// Update the fake movement component state stored on
	// the actor's tangible.  The fake movement component
	// state is usually read by the animation blueprint
	// when the real movement component is disabled.
	//
	UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (DefaultToSelf = "Actor", AutoCreateRefTerm = "State"))
	static FlxMovementComponentState SetFakeMovementComponentState(AActor *Actor, const FlxMovementComponentState &State);
};
