#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameFramework/Pawn.h"

#include "HeadMountedDisplay.h"
#include "HeadMountedDisplayTypes.h"
#include "XRVisualizationFunctionLibrary.h"
#include "Components/PoseableMeshComponent.h"
#include "Misc/EngineVersionComparison.h"

#include "IHandsAdapter.h"

// UE 5.7 removed FXRMotionControllerData (and UHeadMountedDisplayFunctionLibrary::GetMotionControllerData),
// splitting it into FXRMotionControllerState + FXRHandTrackingState (verified: the symbol exists in the
// 5.3-5.6 engine headers and is gone from 5.7). We only consume the hand-tracking part, so on 5.7+ the
// equivalent type is FXRHandTrackingState (field rename: HandKeyPositions -> HandKeyLocations, hence the
// accessor macro).
#if UE_VERSION_OLDER_THAN(5, 7, 0)
typedef FXRMotionControllerData FAR51HandTrackingData;
#define AR51_HAND_KEY_POSITIONS HandKeyPositions
#else
typedef FXRHandTrackingState FAR51HandTrackingData;
#define AR51_HAND_KEY_POSITIONS HandKeyLocations
#endif

#include "OpenXrHandsAdapter.generated.h"

UENUM(BlueprintType)
enum class EHandReferenceSpace : uint8
{
	/** Hand transforms are reported in the HMD�s tracking space (native OpenXR space). */
	HRS_HmdSpace       UMETA(DisplayName = "HMD Tracking Space"),

	/** Hand transforms are reported relative to the parent of the main camera (camera rig space). */
	HRS_CameraRigSpace UMETA(DisplayName = "Camera Rig Space"),
};

UCLASS()
class AR51SDK_OPENXR_API AOpenXrHandsAdapter : public APawn, public IHandsAdapter
{
	GENERATED_BODY()

private:

	UPoseableMeshComponent* _leftHand;
	UPoseableMeshComponent* _rightHand;
	TArray<EHandKeypoint> _boneTypes;
	FAR51HandTrackingData _LHandMotionControllerData;
	FAR51HandTrackingData _RHandMotionControllerData;

	static const TArray<FName>& GetBoneNames();
	static const TArray<EHandKeypoint>& GetBoneTypes();

	TArray<HandJointInfo> GetJointInfos(EHandType handType);
	bool RenderHands(bool isRightHand);
	void ApplyMotionDataToMesh(const FAR51HandTrackingData& MotionData, bool isRightHand);
	static bool GetNativeHmdTransform(FTransform& OutTransform);
	class UCameraComponent* GetMainCameraComponent() const;
	
public:	

	UPROPERTY()
	TArray<FTransform> HandJointTransforms;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="General")
	bool ShowHands { false };

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General", meta = (ToolTip = "Hand below this value (in cm) are assumed to be caused by some sensor error and will not be captured."))
	float ValidHandMinimumPalmSize{ 3.5f };

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking", meta = (ToolTip = "Select which coordinate space to use when reporting local hand positions and rotations."))
	EHandReferenceSpace HandReferenceSpace = EHandReferenceSpace::HRS_HmdSpace;

	// Sets default values for this actor's properties
	AOpenXrHandsAdapter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;


	// Inherited via IHandsAdapter
	virtual FString GetSourceDevice() const override;

	virtual TArray<HandJointInfo> GetRightJointInfos() override;

	virtual TArray<HandJointInfo> GetLeftJointInfos() override;

};
