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

#include "DrawDebugHelpers.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Grabber.h"

#define OUT //mark for outparameters (only for reading purposes)
// Sets default values for this component's properties
UGrabber::UGrabber()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;
}

// Called when the game starts
void UGrabber::BeginPlay()
{
	Super::BeginPlay();

	FindPhysicsHandle(); //Check for physics Handle Component

	SetupInputComponent();
}

void UGrabber::FindPhysicsHandle()
{
	PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>(); //<> for function templates
	if(!PhysicsHandle) // same as if(PhysicsHandle == nullptr)
	{
	UE_LOG(LogTemp, Error, TEXT("No Physics handle component found on: %s !"), *GetOwner()->GetName());
	}
}

void UGrabber::SetupInputComponent()
{
	InputComponent = GetOwner()->FindComponentByClass<UInputComponent>();
	if(InputComponent)
	{
		//UE_LOG(LogTemp, Warning, TEXT("Input Component found on: %s !"), *GetOwner()->GetName());
		InputComponent->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab); // User Input zu einem funktionsaufruf binden  // Name der funktion beim keypress gleich name vom action in projectsettings  ,  IE = InputEvent
		InputComponent->BindAction("Grab", IE_Released, this, &UGrabber::GrabRelease);
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Input Component MISSING on: %s !"), *GetOwner()->GetName());
	}	
}

void UGrabber::Grab()
{
	//only Raycast, when key is pressed and Try and reach any actor with physics body collision set.
	FHitResult HitResult = GetFirstPhysicsBodyInReach();
	UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
	AActor* ActorHit = HitResult.GetActor();

	//if we hit something, attach physics handle
	if(ActorHit) //if an actor is hit, then attach PhysicsHandle
	{
		if(!PhysicsHandle){return;}
		PhysicsHandle->GrabComponentAtLocation
		(
			ComponentToGrab,
			NAME_None,
			GetLineTraceEnd()
		);
	}
		
}

void UGrabber::GrabRelease()
{
	//Remove, release physicshandle
	if(!PhysicsHandle){return;}
	PhysicsHandle->ReleaseComponent();
}

// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if(!PhysicsHandle){return;}
	//If PhysicsBody attached
	if(PhysicsHandle->GrabbedComponent)
	{
		//move object we are holding
		PhysicsHandle->SetTargetLocation(GetLineTraceEnd());
	}
}

FHitResult UGrabber::GetFirstPhysicsBodyInReach() const
{
	FHitResult Hit;
	//Raycast out to a certain distance (Reach)
	FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner()); //fname, ComplexCollision=false, ignore hit on owner(origin point)

	GetWorld()->LineTraceSingleByObjectType(
		OUT Hit,
		GetPlayerWorldPosition(), //PlayerViewPointLocation
		GetLineTraceEnd(),
		FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody), //:: to accsess an enum
		TraceParams
	);
	return Hit;
}

FVector UGrabber::GetPlayerWorldPosition() const
{
	//Get Player Viewport
	FVector PlayerViewPointLocation;
	FRotator PlayerViewPointRotation;
	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
		OUT PlayerViewPointLocation, 
		OUT PlayerViewPointRotation
	);

	return PlayerViewPointLocation;
}

FVector UGrabber::GetLineTraceEnd() const //Players Reach
{
	//Get Player Viewport
	FVector PlayerViewPointLocation;
	FRotator PlayerViewPointRotation;
	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
		OUT PlayerViewPointLocation, 
		OUT PlayerViewPointRotation
	);

	return PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach;
}

//comments
//Logging out to test
	// UE_LOG(LogTemp, Warning, TEXT("Location: %s, Rotation= %s"),
	// 	*PlayerViewPointLocation.ToString(), 
	// 	*PlayerViewPointRotation.ToString()
	// );

		/*
	//Get Player Viewport
	FVector PlayerViewPointLocation;
	FRotator PlayerViewPointRotation;
	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
		OUT PlayerViewPointLocation, 
		OUT PlayerViewPointRotation
	);

	//GetPhysicsbodyInReach
	//Draw a Line from the Player showing the Reach 
	FVector LineTraceEnd = PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach;
	// DEBUG
	// DrawDebugLine(
	// 	GetWorld(),
	// 	PlayerViewPointLocation,
	// 	LineTraceEnd,
	// 	FColor(0, 255 , 0) //red, green, blue,
	// 	false,
	// 	0.f,
	// 	0,
	// 	5.f
	// );
	*/

	//return name of hit actor
	//See what it hits
	// if(Hit.GetActor())
	// UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *Hit.GetActor()->GetName());
	//or
	/*
	AActor* ActorHit = Hit.GetActor();

	//Log out to Test
	if(Hit.GetActor())
	{
		UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *ActorHit->GetName());
	}	
	*/