#pragma once

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

#include "HeadMountedDisplay.h"
#include "PXR_InputFunctionLibrary.h"
#include "CameraAnchorComponent.h"

//#include "XRVisualizationFunctionLibrary.h"
#include "Components/PoseableMeshComponent.h"

#include "IHandsAdapter.h"

#include "PicoHandsAdapter.generated.h"

// These are used as pointers only in this header. Forward-declare them so the header parses on
// every UE version regardless of which transitive includes it happens to get - UE 5.4's chain
// doesn't pull in UMaterial, which caused C2061 here and a cascade of errors in the .cpp. Full
// definitions are included in PicoHandsAdapter.cpp where the types are actually dereferenced.
class UMaterial;
class USkinnedAsset;

class CPicoHand
{
public:
	CPicoHand();
	CPicoHand(EPICOXRHandType eHandType, AActor* pOwner);
	virtual	~CPicoHand();
	void	OnBeginPlay(USkinnedAsset* skinnedAsset, UMaterial* material);
	void	UpdateHandData();
	bool	UpdateVisibility(bool showHands, bool bApplyLocationToBones, bool hideByConfidence, EPICOXRHandTrackingConfidence confidenceThreshold);
	void	SetVisibility(bool bNewVisiblity);
	TArray<HandJointInfo> GetJointInfos(float validHandMinimumPalmSize);

private:
	bool	Init(EPICOXRHandType eHandType, AActor* pOwner);
	FName	FormatHandJointWithPrefix(const FString& Prefix, EPICOXRHandJoint Joint);
	void	GetHandTrackingDataFromPicoSDK();
	void	UpdateBonePose(bool bApplyLocationToBones);
	void	UpdateHandTransform();
	void	ApplyMotionDataToMesh();
	static const TArray<EPICOXRHandJoint>& GetBoneTypes();

	EPICOXRHandType					_handType;
	TArray<FVector>					_jointPositions;
	TArray<FQuat>					_jointRotations;
	AActor*							_pOwner;
	UPoseableMeshComponent*			_handMesh;
	USkinnedAsset*					_handSkinnedAsset;
	UMaterial*						_handMaterial;
	TMap<EPICOXRHandJoint, FName>	_boneNameMappings;
	TArray<EPICOXRHandJoint>		_boneTypes;
	EPICOXRHandTrackingConfidence	_trackingConfidence;
	bool							_bVisible;
};

UCLASS()
class AR51SDK_PICO_API APicoHandsAdapter : public APawn, public IHandsAdapter
{
	GENERATED_BODY()

private:

	UCameraAnchorComponent* _cameraAnchorComponent;

	static const TArray<FName>& GetBoneNames();

	TArray<HandJointInfo> GetJointInfos(EHandType handType);
	void ApplyMotionDataToMesh(UPoseableMeshComponent* MeshComponent, TArray<FVector>* handJointPositions, TArray<FQuat>* handJointRotations);
	void UpdateBonePose(bool bLeftHand);
	void UpdateHandTransform(bool bLeftHand);

	TMap<EPICOXRHandJoint, FName> BoneNameMappings;

	CPicoHand* _pLeftHand;
	CPicoHand* _pRightHand;

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 = "General", meta = (ToolTip = "Right Hand Skinned Asset."))
	USkinnedAsset* RightHandSkinnedAsset;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General", meta = (ToolTip = "Right Hand Skinned Asset."))
	USkinnedAsset* LeftHandSkinnedAsset;

	/** Whether or not Every Bone's Location should be updated*/
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "General")
	bool bApplyLocationToBones;

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "General")
	UPoseableMeshComponent* _leftHand;

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "General")
	UPoseableMeshComponent* _rightHand;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General", meta = (ToolTip = "Hands Material"))
	UMaterial* _handsMaterial;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General", meta = (ToolTip = "Hide hands if confidence in detection is too low"))
	bool _bHideByConfidence;


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

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;

};
