#include "HyperTwistXR/HyperTwistXrTrainingPawn.h"

#include "Camera/CameraComponent.h"
#include "Components/SceneComponent.h"
#include "GameFramework/PlayerController.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "InputCoreTypes.h"
#include "MotionControllerComponent.h"

namespace HyperTwistXrTrainingPawnInternal
{
	constexpr float SeatedHeightOffset = -42.0f;
	constexpr float HybridHeightOffset = -20.0f;
	constexpr float ManualVerticalAdjustmentMin = -160.0f;
	constexpr float ManualVerticalAdjustmentMax = 160.0f;
	constexpr float DesktopFallbackPitchMin = -65.0f;
	constexpr float DesktopFallbackPitchMax = 65.0f;
	constexpr float MoveSpeedMin = 40.0f;
	constexpr float MoveSpeedMax = 600.0f;
	constexpr float VerticalAdjustmentSpeedMin = 40.0f;
	constexpr float VerticalAdjustmentSpeedMax = 320.0f;
	constexpr float SnapTurnDegreesMin = 5.0f;
	constexpr float SnapTurnDegreesMax = 90.0f;
	constexpr float SnapTurnAxisThresholdMin = 0.2f;
	constexpr float SnapTurnAxisThresholdMax = 0.95f;
	constexpr float SmoothTurnDegreesPerSecondMin = 45.0f;
	constexpr float SmoothTurnDegreesPerSecondMax = 240.0f;
	constexpr float PointerLengthMin = 32.0f;
	constexpr float PointerLengthMax = 320.0f;
	constexpr float WorldToMetersScaleMin = 25.0f;
	constexpr float WorldToMetersScaleMax = 400.0f;
	constexpr float ControllerDeadZoneMin = 0.0f;
	constexpr float ControllerDeadZoneMax = 0.95f;

	FVector BuildPlanarDirection(const FVector& RawDirection)
	{
		FVector Result = FVector::VectorPlaneProject(RawDirection, FVector::UpVector);
		Result.Normalize();
		return Result;
	}

	float ApplyDeadZone(const float Value, const float DeadZone)
	{
		return FMath::Abs(Value) >= DeadZone ? Value : 0.0f;
	}

	const TCHAR* DescribeDominantHand(const EHyperTwistXrDominantHand DominantHand)
	{
		return DominantHand == EHyperTwistXrDominantHand::Left
			? TEXT("left")
			: TEXT("right");
	}

	const TCHAR* DescribePostureMode(const EHyperTwistXrPostureMode PostureMode)
	{
		switch (PostureMode)
		{
		case EHyperTwistXrPostureMode::Seated:
			return TEXT("seated");
		case EHyperTwistXrPostureMode::Hybrid:
			return TEXT("hybrid");
		default:
			return TEXT("standing");
		}
	}

	FHyperTwistXrPreferenceProfile SanitizePreferenceProfile(
		const FHyperTwistXrPreferenceProfile& PreferenceProfile
	)
	{
		const FHyperTwistXrPreferenceProfile DefaultProfile =
			UHyperTwistXrRuntimeLibrary::GetDefaultXrPreferenceProfile();
		if (!PreferenceProfile.IsStructurallyValid())
		{
			return DefaultProfile;
		}

		FHyperTwistXrPreferenceProfile Sanitized = PreferenceProfile;
		Sanitized.MoveSpeedUnitsPerSecond = FMath::Clamp(
			Sanitized.MoveSpeedUnitsPerSecond,
			MoveSpeedMin,
			MoveSpeedMax
		);
		Sanitized.VerticalAdjustmentUnitsPerSecond = FMath::Clamp(
			Sanitized.VerticalAdjustmentUnitsPerSecond,
			VerticalAdjustmentSpeedMin,
			VerticalAdjustmentSpeedMax
		);
		Sanitized.SnapTurnDegrees = FMath::Clamp(
			Sanitized.SnapTurnDegrees,
			SnapTurnDegreesMin,
			SnapTurnDegreesMax
		);
		Sanitized.SnapTurnAxisThreshold = FMath::Clamp(
			Sanitized.SnapTurnAxisThreshold,
			SnapTurnAxisThresholdMin,
			SnapTurnAxisThresholdMax
		);
		const float SnapTurnResetThresholdMax = FMath::Max(
			0.0f,
			Sanitized.SnapTurnAxisThreshold - 0.05f
		);
		Sanitized.SnapTurnResetThreshold = FMath::Clamp(
			Sanitized.SnapTurnResetThreshold,
			0.0f,
			SnapTurnResetThresholdMax
		);
		Sanitized.SmoothTurnDegreesPerSecond = FMath::Clamp(
			Sanitized.SmoothTurnDegreesPerSecond,
			SmoothTurnDegreesPerSecondMin,
			SmoothTurnDegreesPerSecondMax
		);
		Sanitized.PointerLengthUnits = FMath::Clamp(
			Sanitized.PointerLengthUnits,
			PointerLengthMin,
			PointerLengthMax
		);
		Sanitized.WorldToMetersScale = FMath::Clamp(
			Sanitized.WorldToMetersScale,
			WorldToMetersScaleMin,
			WorldToMetersScaleMax
		);
		Sanitized.ComfortVignetteStrength = FMath::Clamp(
			Sanitized.ComfortVignetteStrength,
			0.0f,
			1.0f
		);
		Sanitized.ControllerDeadZone = FMath::Clamp(
			Sanitized.ControllerDeadZone,
			ControllerDeadZoneMin,
			ControllerDeadZoneMax
		);
		if (Sanitized.PreferenceFieldTags.Num() <= 0)
		{
			Sanitized.PreferenceFieldTags = DefaultProfile.PreferenceFieldTags;
		}
		if (Sanitized.ComfortSurfaceTags.Num() <= 0)
		{
			Sanitized.ComfortSurfaceTags = DefaultProfile.ComfortSurfaceTags;
		}
		if (Sanitized.ExplicitExclusions.Num() <= 0)
		{
			Sanitized.ExplicitExclusions = DefaultProfile.ExplicitExclusions;
		}
		return Sanitized;
	}
}

AHyperTwistXrTrainingPawn::AHyperTwistXrTrainingPawn()
{
	PrimaryActorTick.bCanEverTick = true;
	AutoPossessPlayer = EAutoReceiveInput::Player0;

	SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	SetRootComponent(SceneRoot);

	VROrigin = CreateDefaultSubobject<USceneComponent>(TEXT("VROrigin"));
	VROrigin->SetupAttachment(SceneRoot);

	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
	CameraComponent->SetupAttachment(VROrigin);

	LeftController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("LeftController"));
	LeftController->SetupAttachment(VROrigin);
	LeftController->SetTrackingMotionSource(TEXT("Left"));

	RightController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("RightController"));
	RightController->SetupAttachment(VROrigin);
	RightController->SetTrackingMotionSource(TEXT("Right"));

	LeftPointerAnchor = CreateDefaultSubobject<USceneComponent>(TEXT("LeftPointerAnchor"));
	LeftPointerAnchor->SetupAttachment(LeftController);

	RightPointerAnchor = CreateDefaultSubobject<USceneComponent>(TEXT("RightPointerAnchor"));
	RightPointerAnchor->SetupAttachment(RightController);

	ActivePreferenceProfile = UHyperTwistXrRuntimeLibrary::GetDefaultXrPreferenceProfile();
	ActiveControllerBindingProfile =
		UHyperTwistXrRuntimeLibrary::GetDefaultXrControllerBindingProfile();
}

void AHyperTwistXrTrainingPawn::BeginPlay()
{
	Super::BeginPlay();

	ApplyPreferenceProfile(ActivePreferenceProfile);
	if (bAutoResetOrientationOnBeginPlay)
	{
		ResetXrOrigin();
	}
}

void AHyperTwistXrTrainingPawn::Tick(const float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	RefreshHeadMountedDisplayState();
	UpdateSmoothLocomotion(DeltaSeconds);
	UpdateVerticalAdjustment(DeltaSeconds);
	UpdateTurn(DeltaSeconds);
	UpdateDesktopFallbackPitch(DeltaSeconds);
}

void AHyperTwistXrTrainingPawn::SetupPlayerInputComponent(
	UInputComponent* PlayerInputComponent
)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	if (PlayerInputComponent == nullptr)
	{
		return;
	}

	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_MoveForward"),
		this,
		&AHyperTwistXrTrainingPawn::HandleMoveForward
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_MoveRight"),
		this,
		&AHyperTwistXrTrainingPawn::HandleMoveRight
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_VerticalAdjust"),
		this,
		&AHyperTwistXrTrainingPawn::HandleVerticalAdjustAxis
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_Turn"),
		this,
		&AHyperTwistXrTrainingPawn::HandleTurnAxis
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_LookPitch"),
		this,
		&AHyperTwistXrTrainingPawn::HandleLookPitchAxis
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_LeftGrip"),
		this,
		&AHyperTwistXrTrainingPawn::HandleLeftGripAxis
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_RightGrip"),
		this,
		&AHyperTwistXrTrainingPawn::HandleRightGripAxis
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_LeftTrigger"),
		this,
		&AHyperTwistXrTrainingPawn::HandleLeftTriggerAxis
	);
	PlayerInputComponent->BindAxis(
		TEXT("HyperTwistXr_RightTrigger"),
		this,
		&AHyperTwistXrTrainingPawn::HandleRightTriggerAxis
	);
	PlayerInputComponent->BindAction(
		TEXT("HyperTwistXr_Recenter"),
		IE_Pressed,
		this,
		&AHyperTwistXrTrainingPawn::ResetXrOrigin
	);
	PlayerInputComponent->BindAction(
		TEXT("HyperTwistXr_ToggleSnapTurn"),
		IE_Pressed,
		this,
		&AHyperTwistXrTrainingPawn::HandleToggleSnapTurn
	);
	PlayerInputComponent->BindAction(
		TEXT("HyperTwistXr_TogglePostureMode"),
		IE_Pressed,
		this,
		&AHyperTwistXrTrainingPawn::HandleTogglePostureMode
	);
}

void AHyperTwistXrTrainingPawn::ApplyPreferenceProfile(
	const FHyperTwistXrPreferenceProfile& PreferenceProfile
)
{
	ActivePreferenceProfile =
		HyperTwistXrTrainingPawnInternal::SanitizePreferenceProfile(
			PreferenceProfile
		);
	ApplyPostureOffset();
	ApplyPointerAnchors();

	if (GetWorld() != nullptr)
	{
		UHeadMountedDisplayFunctionLibrary::SetWorldToMetersScale(
			this,
			ActivePreferenceProfile.WorldToMetersScale
		);
	}
}

void AHyperTwistXrTrainingPawn::SetDominantHand(
	const EHyperTwistXrDominantHand DominantHand
)
{
	ActivePreferenceProfile.DominantHand = DominantHand;
	ApplyPointerAnchors();
}

void AHyperTwistXrTrainingPawn::SetPostureMode(
	const EHyperTwistXrPostureMode PostureMode
)
{
	ActivePreferenceProfile.PostureMode = PostureMode;
	ApplyPostureOffset();
}

void AHyperTwistXrTrainingPawn::SetSnapTurnEnabled(const bool bEnabled)
{
	ActivePreferenceProfile.bSnapTurnEnabled = bEnabled;
	ActivePreferenceProfile.bSmoothTurnFallbackEnabled = !bEnabled;
	bSnapTurnLatchOpen = true;
}

void AHyperTwistXrTrainingPawn::ResetXrOrigin()
{
	ManualVerticalAdjustmentUnits = 0.0f;
	DesktopFallbackPitchDegrees = 0.0f;
	ApplyDesktopFallbackRotation();
	ApplyPostureOffset();
	UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}

FHyperTwistXrRuntimeOwnershipSurface
AHyperTwistXrTrainingPawn::GetBoundedRuntimeOwnershipSurface() const
{
	return UHyperTwistXrRuntimeLibrary::BuildBundledXrRuntimeOwnershipSurface();
}

FString AHyperTwistXrTrainingPawn::BuildRuntimeStatusLine() const
{
	const FHyperTwistXrRuntimeOwnershipSurface RuntimeSurface =
		GetBoundedRuntimeOwnershipSurface();
	return FString::Printf(
		TEXT("OpenXR runtime owner %s | preferences %s | bindings %s | HMD connected %s | HMD enabled %s | posture %s | dominant hand %s | snap turn %s | smooth locomotion %s | vertical offset %.0f | desktop pitch %.0f | grip %.2f/%.2f | trigger %.2f/%.2f"),
		*RuntimeSurface.RuntimeOwnerId,
		*ActivePreferenceProfile.ProfileId,
		*ActiveControllerBindingProfile.ProfileId,
		bHeadMountedDisplayConnected ? TEXT("yes") : TEXT("no"),
		bHeadMountedDisplayEnabled ? TEXT("yes") : TEXT("no"),
		HyperTwistXrTrainingPawnInternal::DescribePostureMode(
			ActivePreferenceProfile.PostureMode),
		HyperTwistXrTrainingPawnInternal::DescribeDominantHand(
			ActivePreferenceProfile.DominantHand),
		ActivePreferenceProfile.bSnapTurnEnabled ? TEXT("on") : TEXT("off"),
		ActivePreferenceProfile.bSmoothLocomotionEnabled ? TEXT("on") : TEXT("off"),
		ManualVerticalAdjustmentUnits,
		DesktopFallbackPitchDegrees,
		LeftGripValue,
		RightGripValue,
		LeftTriggerValue,
		RightTriggerValue
	);
}

bool AHyperTwistXrTrainingPawn::HasObservedHeadMountedDisplaySession() const
{
	return bHeadMountedDisplayConnected && bHeadMountedDisplayEnabled;
}

float AHyperTwistXrTrainingPawn::GetControllerInputObservationMagnitude() const
{
	return FMath::Max(
		FMath::Abs(LeftGripValue),
		FMath::Max(
			FMath::Abs(RightGripValue),
			FMath::Max(FMath::Abs(LeftTriggerValue), FMath::Abs(RightTriggerValue))
		)
	);
}

bool AHyperTwistXrTrainingPawn::HasObservedControllerInputActivity() const
{
	const float ObservationThreshold = FMath::Max(
		0.05f,
		ActivePreferenceProfile.ControllerDeadZone
	);
	return GetControllerInputObservationMagnitude() >= ObservationThreshold;
}

void AHyperTwistXrTrainingPawn::ApplyPostureOffset()
{
	if (VROrigin == nullptr)
	{
		return;
	}

	float HeightOffset = 0.0f;
	switch (ActivePreferenceProfile.PostureMode)
	{
	case EHyperTwistXrPostureMode::Seated:
		HeightOffset = HyperTwistXrTrainingPawnInternal::SeatedHeightOffset;
		break;
	case EHyperTwistXrPostureMode::Hybrid:
		HeightOffset = HyperTwistXrTrainingPawnInternal::HybridHeightOffset;
		break;
	default:
		break;
	}

	VROrigin->SetRelativeLocation(
		FVector(0.0f, 0.0f, HeightOffset + ManualVerticalAdjustmentUnits)
	);
}

void AHyperTwistXrTrainingPawn::ApplyPointerAnchors()
{
	if (LeftPointerAnchor != nullptr)
	{
		LeftPointerAnchor->SetRelativeLocation(
			FVector(ActivePreferenceProfile.PointerLengthUnits, 0.0f, 0.0f)
		);
	}

	if (RightPointerAnchor != nullptr)
	{
		RightPointerAnchor->SetRelativeLocation(
			FVector(ActivePreferenceProfile.PointerLengthUnits, 0.0f, 0.0f)
		);
	}
}

void AHyperTwistXrTrainingPawn::ApplyDesktopFallbackRotation()
{
	if (VROrigin == nullptr || bHeadMountedDisplayEnabled)
	{
		return;
	}

	const FRotator CurrentRotation = VROrigin->GetRelativeRotation();
	VROrigin->SetRelativeRotation(
		FRotator(
			DesktopFallbackPitchDegrees,
			CurrentRotation.Yaw,
			CurrentRotation.Roll
		)
	);
}

void AHyperTwistXrTrainingPawn::RefreshHeadMountedDisplayState()
{
	const bool bWasHeadMountedDisplayEnabled = bHeadMountedDisplayEnabled;
	bHeadMountedDisplayConnected =
		UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayConnected();
	bHeadMountedDisplayEnabled =
		UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled();
	if (bHeadMountedDisplayEnabled && !bWasHeadMountedDisplayEnabled)
	{
		DesktopFallbackPitchDegrees = 0.0f;
		if (VROrigin != nullptr)
		{
			VROrigin->SetRelativeRotation(FRotator::ZeroRotator);
		}
		return;
	}

	if (!bHeadMountedDisplayEnabled && bWasHeadMountedDisplayEnabled)
	{
		ApplyDesktopFallbackRotation();
	}
}

void AHyperTwistXrTrainingPawn::UpdateSmoothLocomotion(const float DeltaSeconds)
{
	if (!ActivePreferenceProfile.bSmoothLocomotionEnabled)
	{
		return;
	}

	const float MoveForward = HyperTwistXrTrainingPawnInternal::ApplyDeadZone(
		PendingMoveForward,
		ActivePreferenceProfile.ControllerDeadZone
	);
	const float MoveRight = HyperTwistXrTrainingPawnInternal::ApplyDeadZone(
		PendingMoveRight,
		ActivePreferenceProfile.ControllerDeadZone
	);
	if (FMath::IsNearlyZero(MoveForward) && FMath::IsNearlyZero(MoveRight))
	{
		return;
	}

	const FVector ForwardDirection = HyperTwistXrTrainingPawnInternal::BuildPlanarDirection(
		CameraComponent != nullptr ? CameraComponent->GetForwardVector() : GetActorForwardVector()
	);
	const FVector RightDirection = HyperTwistXrTrainingPawnInternal::BuildPlanarDirection(
		CameraComponent != nullptr ? CameraComponent->GetRightVector() : GetActorRightVector()
	);
	const FVector Delta =
		(ForwardDirection * MoveForward + RightDirection * MoveRight)
		* ActivePreferenceProfile.MoveSpeedUnitsPerSecond
		* DeltaSeconds;
	AddActorWorldOffset(Delta, true);
}

void AHyperTwistXrTrainingPawn::UpdateVerticalAdjustment(const float DeltaSeconds)
{
	if (!ActivePreferenceProfile.bAllowVerticalAdjustment || VROrigin == nullptr)
	{
		return;
	}

	const float VerticalAdjustValue = HyperTwistXrTrainingPawnInternal::ApplyDeadZone(
		PendingVerticalAdjust,
		ActivePreferenceProfile.ControllerDeadZone
	);
	if (FMath::IsNearlyZero(VerticalAdjustValue))
	{
		return;
	}

	ManualVerticalAdjustmentUnits = FMath::Clamp(
		ManualVerticalAdjustmentUnits
			+ VerticalAdjustValue
				* ActivePreferenceProfile.VerticalAdjustmentUnitsPerSecond
				* DeltaSeconds,
		HyperTwistXrTrainingPawnInternal::ManualVerticalAdjustmentMin,
		HyperTwistXrTrainingPawnInternal::ManualVerticalAdjustmentMax
	);
	ApplyPostureOffset();
}

void AHyperTwistXrTrainingPawn::UpdateTurn(const float DeltaSeconds)
{
	const float TurnValue = HyperTwistXrTrainingPawnInternal::ApplyDeadZone(
		PendingTurnAxis,
		ActivePreferenceProfile.ControllerDeadZone
	);
	if (FMath::IsNearlyZero(TurnValue))
	{
		bSnapTurnLatchOpen = true;
		return;
	}

	if (ActivePreferenceProfile.bSnapTurnEnabled)
	{
		if (bSnapTurnLatchOpen
			&& FMath::Abs(TurnValue) >= ActivePreferenceProfile.SnapTurnAxisThreshold)
		{
			const float Direction = TurnValue > 0.0f ? 1.0f : -1.0f;
			AddActorWorldRotation(
				FRotator(0.0f, Direction * ActivePreferenceProfile.SnapTurnDegrees, 0.0f)
			);
			bSnapTurnLatchOpen = false;
		}

		if (FMath::Abs(TurnValue) <= ActivePreferenceProfile.SnapTurnResetThreshold)
		{
			bSnapTurnLatchOpen = true;
		}
		return;
	}

	if (ActivePreferenceProfile.bSmoothTurnFallbackEnabled)
	{
		AddActorWorldRotation(
			FRotator(
				0.0f,
				TurnValue * ActivePreferenceProfile.SmoothTurnDegreesPerSecond * DeltaSeconds,
				0.0f
			)
		);
	}
}

void AHyperTwistXrTrainingPawn::UpdateDesktopFallbackPitch(const float DeltaSeconds)
{
	if (bHeadMountedDisplayEnabled || VROrigin == nullptr)
	{
		return;
	}

	const float PitchValue = HyperTwistXrTrainingPawnInternal::ApplyDeadZone(
		PendingLookPitchAxis,
		ActivePreferenceProfile.ControllerDeadZone
	);
	if (FMath::IsNearlyZero(PitchValue))
	{
		return;
	}

	DesktopFallbackPitchDegrees = FMath::Clamp(
		DesktopFallbackPitchDegrees
			+ PitchValue * ActivePreferenceProfile.SmoothTurnDegreesPerSecond * DeltaSeconds,
		HyperTwistXrTrainingPawnInternal::DesktopFallbackPitchMin,
		HyperTwistXrTrainingPawnInternal::DesktopFallbackPitchMax
	);
	ApplyDesktopFallbackRotation();
}

void AHyperTwistXrTrainingPawn::HandleMoveForward(const float Value)
{
	PendingMoveForward = Value;
}

void AHyperTwistXrTrainingPawn::HandleMoveRight(const float Value)
{
	PendingMoveRight = Value;
}

void AHyperTwistXrTrainingPawn::HandleVerticalAdjustAxis(const float Value)
{
	PendingVerticalAdjust = Value;
}

void AHyperTwistXrTrainingPawn::HandleTurnAxis(const float Value)
{
	PendingTurnAxis = Value;
}

void AHyperTwistXrTrainingPawn::HandleLookPitchAxis(const float Value)
{
	PendingLookPitchAxis = Value;
}

void AHyperTwistXrTrainingPawn::HandleLeftGripAxis(const float Value)
{
	LeftGripValue = FMath::Clamp(Value, 0.0f, 1.0f);
}

void AHyperTwistXrTrainingPawn::HandleRightGripAxis(const float Value)
{
	RightGripValue = FMath::Clamp(Value, 0.0f, 1.0f);
}

void AHyperTwistXrTrainingPawn::HandleLeftTriggerAxis(const float Value)
{
	LeftTriggerValue = FMath::Clamp(Value, 0.0f, 1.0f);
}

void AHyperTwistXrTrainingPawn::HandleRightTriggerAxis(const float Value)
{
	RightTriggerValue = FMath::Clamp(Value, 0.0f, 1.0f);
}

void AHyperTwistXrTrainingPawn::HandleToggleSnapTurn()
{
	SetSnapTurnEnabled(!ActivePreferenceProfile.bSnapTurnEnabled);
	if (!ActivePreferenceProfile.bSnapTurnEnabled)
	{
		ActivePreferenceProfile.bSmoothTurnFallbackEnabled = true;
	}
}

void AHyperTwistXrTrainingPawn::HandleTogglePostureMode()
{
	switch (ActivePreferenceProfile.PostureMode)
	{
	case EHyperTwistXrPostureMode::Standing:
		SetPostureMode(EHyperTwistXrPostureMode::Seated);
		break;
	case EHyperTwistXrPostureMode::Seated:
		SetPostureMode(EHyperTwistXrPostureMode::Hybrid);
		break;
	default:
		SetPostureMode(EHyperTwistXrPostureMode::Standing);
		break;
	}
}
