// Copyright Pacificao. All Rights Reserved.

#include "AgrarianWildlifeSpawnManager.h"
#include "AgrarianWildlifeBase.h"
#include "NavigationSystem.h"
#include "Net/UnrealNetwork.h"

AAgrarianWildlifeSpawnManager::AAgrarianWildlifeSpawnManager()
{
	PrimaryActorTick.bCanEverTick = true;
	bReplicates = true;
	NetCullDistanceSquared = FMath::Square(8000.0f);
	SetReplicatingMovement(false);
}

void AAgrarianWildlifeSpawnManager::BeginPlay()
{
	Super::BeginPlay();

	SpawnTimer = SpawnIntervalSeconds;
	if (HasAuthority() && bSpawnOnBeginPlay)
	{
		SpawnInitialWildlife();
	}
}

void AAgrarianWildlifeSpawnManager::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if (!HasAuthority() || !WildlifeClass)
	{
		return;
	}

	RemoveInvalidSpawnedWildlife();
	if (GetActiveWildlifeCount() >= MaxActiveWildlife)
	{
		return;
	}

	SpawnTimer -= DeltaSeconds;
	if (SpawnTimer <= 0.0f)
	{
		SpawnTimer = SpawnIntervalSeconds;
		SpawnWildlife();
	}
}

void AAgrarianWildlifeSpawnManager::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(AAgrarianWildlifeSpawnManager, SpawnedWildlife);
}

AAgrarianWildlifeBase* AAgrarianWildlifeSpawnManager::SpawnWildlife()
{
	if (!HasAuthority() || !WildlifeClass || GetActiveWildlifeCount() >= MaxActiveWildlife)
	{
		return nullptr;
	}

	FVector SpawnLocation = GetActorLocation();
	if (!ChooseSpawnLocation(SpawnLocation))
	{
		return nullptr;
	}

	FActorSpawnParameters SpawnParameters;
	SpawnParameters.Owner = this;
	SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;

	AAgrarianWildlifeBase* SpawnedActor = GetWorld()->SpawnActor<AAgrarianWildlifeBase>(
		WildlifeClass,
		SpawnLocation,
		FRotator::ZeroRotator,
		SpawnParameters);

	if (SpawnedActor)
	{
		SpawnedWildlife.Add(SpawnedActor);
	}

	return SpawnedActor;
}

int32 AAgrarianWildlifeSpawnManager::GetActiveWildlifeCount() const
{
	int32 ActiveCount = 0;
	for (AAgrarianWildlifeBase* Wildlife : SpawnedWildlife)
	{
		if (IsValid(Wildlife))
		{
			++ActiveCount;
		}
	}

	return ActiveCount;
}

void AAgrarianWildlifeSpawnManager::SpawnInitialWildlife()
{
	const int32 TargetCount = FMath::Min(InitialSpawnCount, MaxActiveWildlife);
	for (int32 SpawnIndex = GetActiveWildlifeCount(); SpawnIndex < TargetCount; ++SpawnIndex)
	{
		if (!SpawnWildlife())
		{
			break;
		}
	}
}

bool AAgrarianWildlifeSpawnManager::ChooseSpawnLocation(FVector& OutLocation) const
{
	const FVector RandomOffset(
		FMath::FRandRange(-SpawnRadius, SpawnRadius),
		FMath::FRandRange(-SpawnRadius, SpawnRadius),
		0.0f);
	const FVector CandidateLocation = GetActorLocation() + RandomOffset;

	if (!bProjectSpawnsToNavigation)
	{
		OutLocation = CandidateLocation;
		return true;
	}

	const UWorld* World = GetWorld();
	const UNavigationSystemV1* NavigationSystem = World ? FNavigationSystem::GetCurrent<UNavigationSystemV1>(World) : nullptr;
	if (!NavigationSystem)
	{
		OutLocation = CandidateLocation;
		return true;
	}

	FNavLocation ProjectedLocation;
	if (!NavigationSystem->ProjectPointToNavigation(CandidateLocation, ProjectedLocation, NavigationProjectionExtent))
	{
		return false;
	}

	OutLocation = ProjectedLocation.Location;
	return true;
}

void AAgrarianWildlifeSpawnManager::RemoveInvalidSpawnedWildlife()
{
	SpawnedWildlife.RemoveAll([](const TObjectPtr<AAgrarianWildlifeBase>& Wildlife)
	{
		return !IsValid(Wildlife);
	});
}
