// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.


#include "AnimInstances/OLSBaseLinkedLayerAnimInstance.h"

#include "AnimInstances/OLSBaseLayerAnimInstance.h"

void UOLSBaseLinkedLayerAnimInstance::NativeInitializeAnimation()
{
	// Super::NativeInitializeAnimation();
	// Don't call Super since it is empty.
	BaseMainAnimInstance = Cast<UOLSBaseLayerAnimInstance>(GetOwningComponent()->GetAnimInstance());
}

void UOLSBaseLinkedLayerAnimInstance::NativeUpdateAnimation(float deltaSeconds)
{
	// Super::NativeUpdateAnimation(deltaSeconds);
	// Don't call Super since it is empty.

	NativeUpdateEssentialValues(deltaSeconds);
}

void UOLSBaseLinkedLayerAnimInstance::NativeThreadSafeUpdateAnimation(float deltaSeconds)
{
	// Super::NativeThreadSafeUpdateAnimation(deltaSeconds);
	// Don't call Super since it is empty.
	NativeThreadSafeUpdateLocomotionStateData(deltaSeconds);
	NativeUpdateSkeletonControlData(deltaSeconds);
}

void UOLSBaseLinkedLayerAnimInstance::NativePostEvaluateAnimation()
{
	// Don't call Super since it's empty.
}

void UOLSBaseLinkedLayerAnimInstance::NativeUpdateEssentialValues(const float deltaSeconds)
{
	if (BaseMainAnimInstance)
	{
		WorldVelocity = BaseMainAnimInstance->GetWorldVelocity();
		LocalVelocityDirectionAngle = BaseMainAnimInstance->GetLocalVelocityDirectionAngle();
		DisplacementSpeed = BaseMainAnimInstance->GetDisplacementSpeed();
		LocalAcceleration2D = BaseMainAnimInstance->GetLocalAcceleration2D();

		bIsLookingOrAimingDirection = BaseMainAnimInstance->IsLookingOrAimingDirection();
		bIsVelocityOrAimingDirection = BaseMainAnimInstance->IsVelocityOrAimingDirection();
		bIsCrouching = BaseMainAnimInstance->IsCrouching();
		bHasAcceleration = BaseMainAnimInstance->HasAcceleration();
		bHasVelocity = BaseMainAnimInstance->HasVelocity();

		RotationMode = BaseMainAnimInstance->GetRotationMode();

		OwningPawnMaxSpeed = BaseMainAnimInstance->GetOwningPawnMaxSpeed();
	}
}

void UOLSBaseLinkedLayerAnimInstance::NativeUpdateSkeletonControlData(const float deltaSeconds)
{
	OrientationWarpingAlpha = GetCurveValue(EnableOrientationWarpingName);
}

void UOLSBaseLinkedLayerAnimInstance::NativeThreadSafeUpdateLocomotionStateData(const float deltaSeconds)
{
	HipDirection = (bUseHipDirection ? GetHipDirection() : EOLSHipDirection::EForward);

	// Call custom logic on blueprint.
	BlueprintThreadSafeUpdateLocomotionStateData(deltaSeconds);
}

EOLSHipDirection UOLSBaseLinkedLayerAnimInstance::GetHipDirection() const
{
	if (!bIsLookingOrAimingDirection)
	{
		return EOLSHipDirection::EForward;
	}

	const bool isHipForward = UKismetMathLibrary::InRange_FloatFloat(FMath::Abs(LocalVelocityDirectionAngle),
	                                                                 HipDirectionAngleClamp.X + HipDirectionAngleClamp.
	                                                                 Y,
	                                                                 HipDirectionAngleClamp.Y + HipDirectionBuffer) &&
		UKismetMathLibrary::InRange_FloatFloat(FMath::Abs(LocalVelocityDirectionAngle),
		                                       HipDirectionAngleClamp.X - HipDirectionAngleClamp.Y,
		                                       HipDirectionAngleClamp.Y + HipDirectionBuffer);

	return (isHipForward) ? EOLSHipDirection::EForward : EOLSHipDirection::EBackward;
}

FOLSStanceAnimSets UOLSBaseLinkedLayerAnimInstance::GetStanceAnimSets(const bool isCrouching) const
{
	return (isCrouching ? CrouchingAnimSets : StandingAnimSets);
}

const FOLSGaitAnimSets& UOLSBaseLinkedLayerAnimInstance::GetGaitAnimSets(
	const bool isCrouching, const EOLSGait& locomotionState) const
{
	//@TODO: add nullptr log.
	return *GetStanceAnimSets(isCrouching).SelectGaitAnimSetByGait(locomotionState);
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectIdleAnimation(const bool isCrouching, const bool isAiming) const
{
	return GetStanceAnimSets(isCrouching).GetIdleAnimation(isAiming);
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectIdleEntryAnimation(const bool isCrouching) const
{
	return GetStanceAnimSets(isCrouching).GetIdleEntryAnimation();
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectIdleBreakAnimation(const bool isCrouching) const
{
	return GetStanceAnimSets(isCrouching).GetRandomIdleBreakAnimation();
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectTurnInPlaceAnimation(
	const bool isCrouching, const float direction) const
{
	return GetStanceAnimSets(isCrouching).GetTurnInPlaceAnimation(direction);
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectStartCycleAnimation(const bool isCrouching,
                                                                          const bool isAiming,
                                                                          const EOLSGait gait,
                                                                          const EOLSCardinalDirection velocityDirection,
                                                                          const EOLSHipDirection hipDirection,
                                                                          const float angle) const
{
	if (isAiming)
	{
		return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_CameraFacing.GetStartCycleFromHipDirection(hipDirection).
		                                          GetMovementAnimationByCardinalDirection(velocityDirection);
	}

	return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_ForwardFacing.StartCycle.
	                                          GetMovementAnimationByAngle(velocityDirection, angle);
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectCycleAnimation(const bool isCrouching,
                                                                     const bool isAiming,
                                                                     const EOLSGait gait,
                                                                     const EOLSCardinalDirection velocityDirection,
                                                                     const EOLSHipDirection hipDirection) const
{
	if (isAiming)
	{
		return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_CameraFacing.GetCycleFromHipDirection(hipDirection).
		                                          GetMovementAnimationByCardinalDirection(velocityDirection);
	}

	return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_ForwardFacing.Cycle;
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectPivotAnimation(const bool isCrouching,
                                                                     const bool isAiming,
                                                                     const EOLSGait gait,
                                                                     const EOLSCardinalDirection velocityDirection,
                                                                     const EOLSHipDirection hipDirection,
                                                                     const float angle) const
{
	if (isAiming)
	{
		return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_CameraFacing.GetPivotFromHipDirection(hipDirection).
		                                          GetMovementAnimationByCardinalDirection(velocityDirection);
	}

	return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_ForwardFacing.Pivot.GetBackwardLeftRightByAngle(angle);
}

UAnimSequence* UOLSBaseLinkedLayerAnimInstance::SelectStopCycleAnimation(const bool isCrouching,
                                                                         const bool isAiming,
                                                                         const EOLSGait gait,
                                                                         const EOLSCardinalDirection velocityDirection,
                                                                         const EOLSHipDirection hipDirection) const
{
	if (isAiming)
	{
		return GetGaitAnimSets(isCrouching, gait).
		       GaitAnimSet_CameraFacing.GetStopCycleFromHipDirection(hipDirection).
		       GetMovementAnimationByCardinalDirection(velocityDirection);
	}

	return GetGaitAnimSets(isCrouching, gait).GaitAnimSet_ForwardFacing.StopCycle;
}

FVector2D UOLSBaseLinkedLayerAnimInstance::SelectPlayRateByLocomotionState(
	const bool isCrouching, const bool isAiming, const EOLSGait gait,
	const EOLSLocomotionStatePlayRate locomotionState) const
{
	FVector2D result = FVector2D::ZeroVector;
	if (isAiming)
	{
		const FOLSGaitAnimSet_CameraFacing& gaitAnimSet = GetGaitAnimSets(isCrouching, gait).GaitAnimSet_CameraFacing;
		gaitAnimSet.GetPlayRateByLocomotionState(locomotionState, result);
		return result;
	}

	const FOLSGaitAnimSet_ForwardFacing& gaitAnimSet = GetGaitAnimSets(isCrouching, gait).GaitAnimSet_ForwardFacing;
	gaitAnimSet.GetPlayRateByLocomotionState(locomotionState, result);
	return result;
}

bool UOLSBaseLinkedLayerAnimInstance::CanPlayIdleBreakAnimation(const bool isCrouching) const
{
	return GetStanceAnimSets(isCrouching).CanPlayIdleBreakAnimation();
}

void UOLSBaseLinkedLayerAnimInstance::ProcessIdleBreakTransitionLogic(const bool isCrouching, const float deltaSeconds)
{
	if (CanPlayIdleBreakAnimation(isCrouching))
	{
		TimeUntilNextIdleBreak -= deltaSeconds;
		return;
	}

	ResetIdleBreakTransitionLogic();
}

void UOLSBaseLinkedLayerAnimInstance::ResetIdleBreakTransitionLogic()
{
	TimeUntilNextIdleBreak = IdleBreakDelayTime;
}

void UOLSBaseLinkedLayerAnimInstance::DetermineIdleBreakDelayTime()
{
	IdleBreakDelayTime = 6 + FMath::TruncToInt32(FMath::Abs(WorldVelocity.X + WorldVelocity.Y)) & 10;
}

void UOLSBaseLinkedLayerAnimInstance::ProcessTurnInPlaceTransitionLogic(const float angle, const float turnRate,
                                                                        const float deltaSeconds)
{
	switch (RotationMode)
	{
	case EOLSRotationMode::EVelocityDirection:
		{
			bCanRotateInPlace = false;
			bCanTurnInPlace = false;
			TurnInPlaceDelayTime = 0.f;
			break;
		}

	case EOLSRotationMode::ELookingDirection:
		{
			bCanRotateInPlace = false;

			const float absAngle = FMath::Abs(angle);
			if (absAngle > 45.f && turnRate < 50.f)
			{
				TurnInPlaceDelayTime += deltaSeconds;
				bCanTurnInPlace = TurnInPlaceDelayTime > UKismetMathLibrary::MapRangeClamped(
					absAngle, 45.f, 180.f, 1.f, 0.f);
				break;
			}

			bCanTurnInPlace = false;
			break;
		}
	case EOLSRotationMode::EAiming:
		{
			bCanTurnInPlace = false;
			TurnInPlaceDelayTime = 0.f;

			const float absAngle = FMath::Abs(angle);
			bCanRotateInPlace = (absAngle > 50.f);
			break;
		}
	}
}

UOLSBaseLayerAnimInstance* UOLSBaseLinkedLayerAnimInstance::GetBaseMainAnimInstance() const
{
	return BaseMainAnimInstance;
}
