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


#include "CoffeeMachineController.h"

#include "HoverableBoxComponent.h"
#include "Faye/Base/FayePlayerController.h"
#include "Kismet/GameplayStatics.h"


UCoffeeMachineController::UCoffeeMachineController()
{
	PrimaryComponentTick.bCanEverTick = true;
}

void UCoffeeMachineController::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (!bCoffeeMakingActive) return;
	
	if (InputType == EInputControllerType::Mouse)
	{
		PerformMouseHover();
		
		if (CurrentSelectedMeshComponent)
		{
			MoveHeldObject();
		}
	}
}

void UCoffeeMachineController::ToggleCoffeeMaking(bool bActive)
{
	bCoffeeMakingActive = bActive;
	PlayerController->ToggleCoffeeMaking(bActive);

	if (bCoffeeMakingActive)
	{
		PlayerController->OnCoffeeMakingAction_GrabStarted.AddUniqueDynamic(this, &UCoffeeMachineController::OnInputGrab);
		PlayerController->OnCoffeeMakingAction_GrabRelease.AddUniqueDynamic(this, &UCoffeeMachineController::OnInputRelease);
	}
	else
	{
		PlayerController->OnCoffeeMakingAction_GrabStarted.RemoveDynamic(this, &UCoffeeMachineController::OnInputGrab);
		PlayerController->OnCoffeeMakingAction_GrabRelease.RemoveDynamic(this, &UCoffeeMachineController::OnInputRelease);
	}
}

void UCoffeeMachineController::SetCoffeeMakingStage(ECoffeeMachineMakingState NewStage)
{
	CurrentCoffeeMakingStage = NewStage;
}

void UCoffeeMachineController::BeginPlay()
{
	Super::BeginPlay();
	
	PlayerController = Cast<AFayePlayerController>(UGameplayStatics::GetPlayerController(GetWorld(), 0));
}

void UCoffeeMachineController::PerformMouseHover()
{
	float MouseX, MouseY;
	if (!PlayerController->GetMousePosition(MouseX, MouseY)) return;
	
	FVector WorldOrigin, WorldDirection;
	if (!PlayerController->DeprojectScreenPositionToWorld(MouseX, MouseY, WorldOrigin, WorldDirection)) return;
	
	FVector TraceEnd = WorldOrigin + (WorldDirection * 300.f);
	
	TArray<FHitResult> HitResults;
	FCollisionQueryParams Params;
	Params.AddIgnoredComponent(CurrentSelectedBoxComponent);
	const bool bHit = GetWorld()->LineTraceMultiByChannel(HitResults, WorldOrigin, TraceEnd, ECC_GameTraceChannel1, Params);
	
	

	HoverBox(bHit? Cast<UHoverableBoxComponent>(HitResults[0].Component) : nullptr);
}

void UCoffeeMachineController::MoveHeldObject()
{
	float MouseX, MouseY;
	if (!PlayerController->GetMousePosition(MouseX, MouseY)) return;
	
	FVector WorldOrigin, WorldDirection;
	if (!PlayerController->DeprojectScreenPositionToWorld(MouseX, MouseY, WorldOrigin, WorldDirection)) return;
	
	APlayerCameraManager* CameraManager = PlayerController->PlayerCameraManager;
	FVector CameraForward = CameraManager->GetActorForwardVector();
	FPlane MovementPlane(CurrentSelectedMeshComponent->GetComponentLocation(), CameraForward);
	
	FVector IntersectPoint = FMath::RayPlaneIntersection(WorldOrigin, WorldDirection, MovementPlane);
	
	switch (CurrentGrabbedObjectMovementMode)
	{
	case ECoffeeMachinePropMovementMode::PlaneYZ:
		break;
	case ECoffeeMachinePropMovementMode::ZOnly:
		{
			IntersectPoint.X = CurrentGrabbedObjectStartPos.X;
			IntersectPoint.Y = CurrentGrabbedObjectStartPos.Y;
			
			IntersectPoint.Z = FMath::Clamp(IntersectPoint.Z, CurrentGrabbedObjectStartPos.Z - 14.f, CurrentGrabbedObjectStartPos.Z);
			
			// if (GEngine)
			// {
			// 	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("Intersection point: %s"), *IntersectPoint.ToString()));
			// }
			break;
		}
	}
	
	CurrentSelectedMeshComponent->SetWorldLocation(IntersectPoint);
}

void UCoffeeMachineController::HoverBox(UHoverableBoxComponent* InBox)
{
	if (InBox == CurrentHoverableComponent)
	{
		if (CanHover(InBox))
			return;
	}
	
	if (CurrentHoverableComponent)
	{
		CurrentHoverableComponent->OnUnhovered.Broadcast();
		CurrentHoverableComponent = nullptr;
		return;
	}
	
	if (CanHover(InBox))
	{
		CurrentHoverableComponent = InBox;
		CurrentHoverableComponent->OnHovered.Broadcast();	
	}
}

bool UCoffeeMachineController::GrabObject(UHoverableBoxComponent* InBox)
{
	if (CurrentCoffeeMakingStage != ECoffeeMachineMakingState::Start &&
		CurrentCoffeeMakingStage != ECoffeeMachineMakingState::PortafilterPressing) return false;
	
	if (InBox && InBox->GetAttachParent() && CanGrab(InBox))
	{
		CurrentSelectedBoxComponent = InBox;
		CurrentSelectedMeshComponent = Cast<UStaticMeshComponent>(InBox->GetAttachParent());
		CurrentSelectedBoxComponent->OnSelected.Broadcast();
		
		CurrentGrabbedObjectStartPos = CurrentSelectedMeshComponent->GetComponentLocation();
		
		if (InBox->ComponentHasTag(OBJECT_TYPE_PORTAFILTER) && CurrentCoffeeMakingStage == ECoffeeMachineMakingState::Start)
		{
			SetCoffeeMakingStage(ECoffeeMachineMakingState::PortafilterDragging);
			CurrentGrabbedObjectMovementMode = ECoffeeMachinePropMovementMode::PlaneYZ;
		}
		else if (InBox->ComponentHasTag(OBJECT_TYPE_TAMPER) && CurrentCoffeeMakingStage == ECoffeeMachineMakingState::PortafilterPressing)
		{
			CurrentGrabbedObjectMovementMode = ECoffeeMachinePropMovementMode::ZOnly;
			CurrentGrabbedObjectStartPos = TamperLiftStartingPos;
		}
		
		return true;
	}
	
	return false;
}

void UCoffeeMachineController::ReleaseObject()
{
	OnObjectReleased.Broadcast();
	
	if (CurrentSelectedBoxComponent)
	{
		CurrentSelectedBoxComponent->OnUnselected.Broadcast();
	}
	
	CurrentSelectedMeshComponent = nullptr;
	CurrentSelectedBoxComponent = nullptr;
}

bool UCoffeeMachineController::CanGrab(const UHoverableBoxComponent* InBox) const
{
	return  InBox && 
			((InBox->ComponentHasTag(OBJECT_TYPE_PORTAFILTER) && CurrentCoffeeMakingStage == ECoffeeMachineMakingState::Start) ||
			(InBox->ComponentHasTag(OBJECT_TYPE_TAMPER) && CurrentCoffeeMakingStage == ECoffeeMachineMakingState::PortafilterPressing));
}

bool UCoffeeMachineController::CanHover(const UHoverableBoxComponent* InBox) const
{
	if (!InBox) return false;
	
	switch (CurrentCoffeeMakingStage)
	{
	case ECoffeeMachineMakingState::Start:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_PORTAFILTER) && !InBox->ComponentHasTag(OBJECT_TYPE_COFFEE_MUG)) return false;
			
			break;
		}
	case ECoffeeMachineMakingState::PortafilterDragging:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_GRINDER)) return false;
			
			break;
		}
	case ECoffeeMachineMakingState::PortafilterFilling:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_GRINDER_BUTTON)) return false;
			
			break;
		}
	case ECoffeeMachineMakingState::PortafilterPressing:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_TAMPER)) return false;
        			
			break;
		}
	case ECoffeeMachineMakingState::CoffeeLoaded:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_MAIN_MACHINE_BUTTON) && !InBox->ComponentHasTag(OBJECT_TYPE_COFFEE_MUG)) return false;
			
			// Cannot press button if cup not placed
			if (InBox->ComponentHasTag(OBJECT_TYPE_MAIN_MACHINE_BUTTON) && !bCupPlaced) return false;
			
			break;
		}
	case ECoffeeMachineMakingState::CoffeeFilling:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_MAIN_MACHINE_BUTTON)) return false;
			
			break;
		}
	case ECoffeeMachineMakingState::CoffeeReady:
		{
			if (!InBox->ComponentHasTag(OBJECT_TYPE_COFFEE_MUG)) return false;
			
			break;
		}
	case ECoffeeMachineMakingState::WaitingAction:
		return false;
	}

	return true;
}

void UCoffeeMachineController::OnInputGrab()
{
	if (!GrabObject(CurrentHoverableComponent))
	{
		OnObjectClicked.Broadcast();
	}
}

void UCoffeeMachineController::OnInputRelease()
{
	ReleaseObject();
}




