#pragma once

#include "CoreMinimal.h"
#include "Camera/CameraActor.h"
#include "CameraSplineActor.h"  // Include the new CameraSplineActor class
#include "Components/SplineComponent.h"
#include "UnleashedCamera.generated.h"

UENUM(BlueprintType)
enum class EUnleashedCameraType : uint8
{
    SplineCamera,
    FixedCamera,
    FollowCamera
};

UCLASS()
class SONICUNLEASHEDREMAKE_API AUnleashedCamera : public ACameraActor
{
    GENERATED_BODY()

public:
    AUnleashedCamera();

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Settings")
    EUnleashedCameraType CameraType;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Settings")
    ACameraSplineActor* SplinePathActor; 

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Settings")
    bool bLookAtSonic;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera Settings")
    class ASonicCharacterComponent* SonicCharacter;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Settings")
    float FollowCameraInterpSpeed = 5.0f;

protected:
    virtual void BeginPlay() override;
    virtual void Tick(float DeltaTime) override;

private:
    USplineComponent* CachedSplineComponent = nullptr;

    void UpdateSplineCamera(float DeltaTime);
    void UpdateFollowCamera(float DeltaTime);
    void LookAtTarget(AActor* Target);
    void CacheSplineComponent();
    void LogNullWarnings();
};


#include "UnleashedCamera.h"
#include "Sonic/SonicCharacterComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"

AUnleashedCamera::AUnleashedCamera()
{
    PrimaryActorTick.bCanEverTick = true;
    bLookAtSonic = false;
}

void AUnleashedCamera::BeginPlay()
{
    Super::BeginPlay();
    SonicCharacter = Cast<ASonicCharacterComponent>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
    LogNullWarnings();
    CacheSplineComponent();
}

void AUnleashedCamera::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    if (!SonicCharacter)
    {
        return;
    }

    if (bLookAtSonic)
    {
        LookAtTarget(SonicCharacter);
    }

    switch (CameraType)
    {
    case EUnleashedCameraType::SplineCamera:
        UpdateSplineCamera(DeltaTime);
        break;
    case EUnleashedCameraType::FollowCamera:
        UpdateFollowCamera(DeltaTime);
        break;
    case EUnleashedCameraType::FixedCamera:
        break;
    default:
        UE_LOG(LogTemp, Error, TEXT("Invalid CameraType in UnleashedCamera"));
        break;
    }
}

void AUnleashedCamera::UpdateSplineCamera(float DeltaTime)
{
    if (!CachedSplineComponent || !SonicCharacter)
    {
        LogNullWarnings();
        return;
    }

    FVector SonicLocation = SonicCharacter->GetOwner()->GetActorLocation();
    float DistanceAlongSpline = CachedSplineComponent->FindInputKeyClosestToWorldLocation(SonicLocation);

    FVector TargetCameraPosition = CachedSplineComponent->GetLocationAtSplineInputKey(DistanceAlongSpline, ESplineCoordinateSpace::World);
    SetActorLocation(TargetCameraPosition);
}

void AUnleashedCamera::UpdateFollowCamera(float DeltaTime)
{
    if (!SonicCharacter)
    {
        return;
    }

    FVector SonicLocation = SonicCharacter->GetOwner()->GetActorLocation();
    FVector SonicForwardVector = SonicCharacter->GetOwner()->GetActorForwardVector();
    FVector DesiredCameraPosition = SonicLocation - SonicForwardVector * 300.0f + FVector(0.0f, 0.0f, 100.0f);

    FVector CurrentCameraPosition = GetActorLocation();
    FVector NewCameraPosition = FMath::VInterpTo(CurrentCameraPosition, DesiredCameraPosition, DeltaTime, FollowCameraInterpSpeed);
    SetActorLocation(NewCameraPosition);
}

void AUnleashedCamera::LookAtTarget(AActor* Target)
{
    if (Target)
    {
        FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), Target->GetActorLocation());
        SetActorRotation(LookAtRotation);
    }
}

void AUnleashedCamera::CacheSplineComponent()
{
    if (SplinePathActor)
    {
        CachedSplineComponent = SplinePathActor->FindComponentByClass<USplineComponent>();
        if (!CachedSplineComponent)
        {
            UE_LOG(LogTemp, Error, TEXT("CameraSplineActor does not have a valid SplineComponent!"));
        }
    }
}

void AUnleashedCamera::LogNullWarnings()
{
    if (!SonicCharacter)
    {
        UE_LOG(LogTemp, Error, TEXT("SonicCharacter not found!"));
    }
    if (!SplinePathActor)
    {
        UE_LOG(LogTemp, Error, TEXT("CameraSplineActor is null!"));
    }
}


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SplineComponent.h"  // Include SplineComponent
#include "CameraSplineActor.generated.h"

UCLASS()
class SONICUNLEASHEDREMAKE_API ACameraSplineActor : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    ACameraSplineActor();

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

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

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Spline")
    USplineComponent* SplineComponent;  // Add the SplineComponent
};


#include "CameraSplineActor.h"

// Sets default values
ACameraSplineActor::ACameraSplineActor()
{
    PrimaryActorTick.bCanEverTick = true;

    // Initialize the SplineComponent
    SplineComponent = CreateDefaultSubobject<USplineComponent>(TEXT("SplineComponent"));
    RootComponent = SplineComponent;  // Set it as the root component if desired
    SplineComponent->SetIsVisualizationComponent(true);  // Enable visualization
    SplineComponent->bShouldVisualizeScale = true;  // Enable scale visualization
}

// Called when the game starts or when spawned
void ACameraSplineActor::BeginPlay()
{
    Super::BeginPlay();
    
}

// Called every frame
void ACameraSplineActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}


In the actual editor i put the camera in the level, select a camerasplineactor. but in the console i still get  LogTemp: Error: CameraSplineActor is null!