// © 2025 Naked People Team. All Rights Reserved.


#include "NPC.h"

#include "BrainComponent.h"
#include "NPCTypeDefinition.h"
#include "NPCAIController.h"
#include "NPCTargetLocation.h"
#include "NPCVisualsConfig.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/CharacterMovementComponent.h"

ANPC::ANPC()
{
	PrimaryActorTick.bCanEverTick = false;

	GetCharacterMovement()->MaxWalkSpeed = 200.0f;
	GetCharacterMovement()->bUseControllerDesiredRotation = true;
	GetCharacterMovement()->bOrientRotationToMovement = false;
	bUseControllerRotationYaw = false;
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;

	// Crowd anim hygiene (mesh-agnostic): only evaluate the pose while the NPC is on screen and
	// throttle the eval rate by distance. CharacterMovement still ticks off-screen so pooled-in
	// NPCs can keep walking; only the visible pose freezes when unrendered.
	if (USkeletalMeshComponent* MeshComp = GetMesh())
	{
		MeshComp->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
		MeshComp->bEnableUpdateRateOptimizations = true;
	}
}

ENPCType ANPC::GetNPCType() const
{
	return TypeDefinition ? TypeDefinition->Type : ENPCType::Walker;
}

float ANPC::GetObservationWeight() const
{
	return TypeDefinition ? TypeDefinition->ObservationWeight : 1.0f;
}

bool ANPC::ShouldStopToObserve() const
{
	return TypeDefinition ? TypeDefinition->bStopsToObserve : false;
}

float ANPC::GetObserveDuration() const
{
	return TypeDefinition ? TypeDefinition->ObserveDurationSeconds : 0.0f;
}

void ANPC::ActivateFromPool(const FVector& Location, const FRotator& Rotation)
{
	SetActorLocationAndRotation(Location, Rotation, false, nullptr, ETeleportType::TeleportPhysics);
	SetActorHiddenInGame(false);
	SetActorEnableCollision(true);

	if (UCharacterMovementComponent* Move = GetCharacterMovement())
	{
		Move->SetComponentTickEnabled(true);
		Move->SetMovementMode(MOVE_Walking);
	}
	
	if (ANPCAIController* NPCController = Cast<ANPCAIController>(GetController()))
	{
		NPCController->RunCustomBehaviorTree();
		NPCController->GetBlackboardComponent()->SetValueAsBool(TEXT("bHasReacted"), false);
	}
}

void ANPC::DeactivateToPool()
{
	// Drop any active observation so a pooled-out NPC stops contributing to the player's embarrassment.
	if (ANPCAIController* NPCController = Cast<ANPCAIController>(GetController()))
	{
		NPCController->ClearObservation();
		if (UBrainComponent* Brain = NPCController->GetBrainComponent())
		{
			Brain->StopLogic(TEXT("Moved to pool"));
		}
		NPCController->ClearFocus(EAIFocusPriority::Default);
	}

	if (UCharacterMovementComponent* Move = GetCharacterMovement())
	{
		Move->StopMovementImmediately();
		Move->DisableMovement();
		Move->SetComponentTickEnabled(false);
	}
	
	if (TargetLocationActor)
	{
		TargetLocationActor->Release(this);
	}

	SetActorHiddenInGame(true);
	SetActorEnableCollision(false);
}

void ANPC::Init(ENPCType InType)
{
	if (InType == ENPCType::None)
		return;
	
	Type = InType;
	
	if (!NPCTypeDefinitions.Contains(Type))
		return;
	
	UNPCTypeDefinition* Definition = NPCTypeDefinitions[Type];
	if (!Definition)
		return;
	
	TypeDefinition = Definition;
}
