// Fill out your copyright notice in the Description page of Project Settings.


#include "customCharacterMovementComponent.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "Engine/World.h"

UCustomCharacterMovementComponent::UCustomCharacterMovementComponent()
{
    // Set default values here if needed
}

// Custom movement physics
// Use custom movement 0 for climbing
void UCustomCharacterMovementComponent::PhysCustom(float DeltaTime, int32 Iterations)
{
    // Handle custom movement mode physics here
    switch (CustomMovementMode)
    {
		case 0:
		{
			if (DeltaTime < MIN_TICK_TIME)
			{
				return;
			}

			RestorePreAdditiveRootMotionVelocity();

			if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
			{
				if (bCheatFlying && Acceleration.IsZero())
				{
					Velocity = FVector::ZeroVector;
				}
				//const float Friction = 0.5f * GetPhysicsVolume()->FluidFriction;
				constexpr float Friction = 5.f;
				CalcVelocity(DeltaTime, Friction, true, GetMaxBrakingDeceleration());
			}

			ApplyRootMotionToVelocity(DeltaTime);
				
			bJustTeleported = false;

			FVector OldLocation = UpdatedComponent->GetComponentLocation();
			const FVector Adjusted = Velocity * DeltaTime;
			FHitResult Hit(1.f);
			SafeMoveUpdatedComponent(Adjusted, UpdatedComponent->GetComponentQuat(), true, Hit);

			if (Hit.Time < 1.f)
			{
				SlideAlongSurface(Velocity * DeltaTime, 1.f - Hit.Time, Hit.Normal, Hit, true);
			}
				
			const ACharacter* Character = Cast<ACharacter>(GetOwner());
			FHitResult WallHit;
			const bool bWallFound = SweepForwardForWall(Character, WallHit);
			if (bWallFound)
			{
				const FVector SurfaceNormal = WallHit.Normal;
				
				// Update Location
				const FVector TargetLocation = WallHit.Location + (SurfaceNormal * 0.1f);
				const FVector NewLocation = FMath::VInterpTo(UpdatedComponent->GetComponentLocation(), TargetLocation, DeltaTime, 2.f);
				UpdatedComponent->SetWorldLocation(NewLocation);
				
				// Update Rotation
				const FRotator TargetRotation = FRotationMatrix::MakeFromXZ(-SurfaceNormal, FVector::UpVector).Rotator();
				const FRotator NewRotation = FMath::RInterpTo(UpdatedComponent->GetComponentRotation(), TargetRotation, DeltaTime, 2.f);
				UpdatedComponent->SetWorldRotation(NewRotation);
			}
			else
			{
				// Reset Rotation and set MovementMode to Falling
				const FRotator NewRotation = FRotator(0.0f, UpdatedComponent->GetComponentRotation().Yaw, 0.0f);
				UpdatedComponent->SetWorldRotation(NewRotation);
				SetMovementMode(MOVE_Falling);
			}

			if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
			{
				Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / DeltaTime;
			}

			break;
		}

		default:
			// For any other custom mode, fallback to base implementation
			Super::PhysCustom(DeltaTime, Iterations);
    		GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT("Movement mode not implemented!"));
			break;
    }
}

void UCustomCharacterMovementComponent::OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode)
{
	Super::OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode);
	
	if (CharacterOwner)
	{
		if (MovementMode == MOVE_Custom && CustomMovementMode == 0)
		{
			CharacterOwner->bUseControllerRotationYaw = false;
		}
		else if (PreviousMovementMode == MOVE_Custom && PreviousCustomMode == 0)
		{
			CharacterOwner->bUseControllerRotationYaw = true;
		}
	}
}

// Sweeps forward from the character's capsule to detect a wall in front.
// Returns true if a blocking hit is found; OutHit contains hit information.
// SweepDistance is how far ahead to check.
bool UCustomCharacterMovementComponent::SweepForwardForWall(const ACharacter* Character, FHitResult& OutHit, const float SweepDistance)
{
	if (!Character)
	{
		return false;
	}

	const UCapsuleComponent* Capsule = Character->GetCapsuleComponent();
	if (!Capsule)
	{
		return false;
	}

	const UWorld* World = Character->GetWorld();
	if (!World)
	{
		return false;
	}

	const FVector Start = Capsule->GetComponentLocation();
	const FVector Forward = Character->GetActorForwardVector();
	const FVector End = Start + Forward * SweepDistance;

	const float CapsuleRadius = Capsule->GetScaledCapsuleRadius();
	const float CapsuleHalfHeight = Capsule->GetScaledCapsuleHalfHeight();
	const FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(CapsuleRadius, CapsuleHalfHeight);

	const bool bHit = World->SweepSingleByChannel(
		OutHit,
		Start,
		End,
		FQuat::Identity,
		ECC_Visibility,  // Adjust collision channel as needed
		CapsuleShape
	);

	return bHit;
}