// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "PatrolSpawn.h"
#include "NPCBase.h"
#include "DefaultGameMode.h"
#include "NetworkPlayer.h"

APatrolSpawn::APatrolSpawn()
{
	respawnTime = 5;
	m_respawnTimer = 0;

	spawnContinuous = true;
}

void APatrolSpawn::BeginPlay()
{
	Super::BeginPlay();
	if (Role != ROLE_Authority)
		return;
	FName name;
	
	if (aggroRadius < 0) aggroRadius = 0;
	if (respawnTime < 0) respawnTime = 0;

	if (spawns.Num() == 0)
	{
		JWARNING("Empty spawner in scene");
		return;
	}

	for (int i = 0; i < controlPoints.Num(); i++)
		m_controlPoints.Add(FVector(controlPoints[i].X + this->GetActorLocation().X, controlPoints[i].Y + this->GetActorLocation().Y, this->GetActorLocation().Z));

	m_controlPointSign = 1;
	m_isPulled = false;

	m_currentControlPoint = 0;
	m_lastPosition = GetActorLocation();

	//SpawnMobs();
}

void APatrolSpawn::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);

}

void APatrolSpawn::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);



	if (Role != ROLE_Authority)
		return;

	UWorld* const world = GetWorld();
	if (!world) return;

	check(m_mobs.Num() == spawns.Num());

	m_respawnTimer = m_respawnTimer > 0 ? (m_respawnTimer - DeltaTime) : 0;
	if (m_respawnTimer <= 0 && m_mobCount != m_mobs.Num() && spawnContinuous)
		m_RespawnMobs();
	if (m_mobs.Num() == 0)
		return;
	// Respawn the mobs
	/*	if (spawnContinuous)
			m_RespawnMobs();*/
	if (m_controlPoints.Num() <= 0)
	{
		RWARNING("There are no control points");
		return;
	}

	bool anyIdle = false;
	for (int32 i = 0; i < spawns.Num(); i++)
	{
		if (!spawns[i])
			continue;

		if (i == 0 && m_mobs[i] != nullptr)
		{
			if (m_isPulled == false)
			{
				RPRINT("testing");
				m_lastPosition = m_mobs[i]->GetActorLocation();
			}
			else if (!m_mobs[i]->target)
			{
				const FVector2D mobPos = FVector2D(m_mobs[i]->GetActorLocation().X, m_mobs[i]->GetActorLocation().Y);
				const float distSqr = FVector2D::DistSquared(mobPos, FVector2D(m_lastPosition.X, m_lastPosition.Y));
				if (distSqr < (m_mobs[i]->collisionRadius*m_mobs[i]->collisionRadius)*collisionScaler)
					m_isPulled = false;
			}


			// Any of the mobs not doing anything?
			const FVector2D mobPos = FVector2D(m_mobs[i]->GetActorLocation().X, m_mobs[i]->GetActorLocation().Y);
			const float distSqr = FVector2D::DistSquared(mobPos, FVector2D(m_lastPosition.X, m_lastPosition.Y));

			////// Regeneration (hardcoded, remove in future)
			if (!m_mobs[i]->target && distSqr < aggroRadius*aggroRadius)
			{
				anyIdle = true;
			}
		}
	}

	if (m_mobs[0] != nullptr && m_isPulled)
	{
		m_mobs[0]->SetControlPoint(m_lastPosition);
		for (int i = 1; i < m_mobs.Num(); i++)
		{
			m_mobs[i]->SetControlPoint(m_lastPosition);
		}
	}
	ADefaultGameMode* const mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
	if (mode)
	{
		FVector controlLocation = GetActorLocation();
		// check if there are some controll points
		if (m_mobs[0] != nullptr && !m_isPulled)
		{
			if (FVector2D::DistSquared(FVector2D(m_mobs[0]->GetActorLocation().X, m_mobs[0]->GetActorLocation().Y), FVector2D(m_controlPoints[m_currentControlPoint].X, m_controlPoints[m_currentControlPoint].Y)) < 10000)
			{
				m_currentControlPoint += m_controlPointSign;
				if (m_currentControlPoint >= m_controlPoints.Num() || m_currentControlPoint < 0)
				{
					m_controlPointSign *= -1;
					m_currentControlPoint += m_controlPointSign;
					if (isConnected)
					{
						m_currentControlPoint = 0;
						m_controlPointSign *= -1;
					}
				}
			}
			// set new control location
			controlLocation = m_controlPoints[m_currentControlPoint];

			m_mobs[0]->SetControlPoint(controlLocation);
			if (m_mobs.Num() > 1)
			{

				for (int i = 1; i < m_mobs.Num(); i++)
				{
					m_mobs[i]->SetControlPoint(m_lastPosition);
				}
			}
		}

		if (anyIdle && !m_isPulled)
		{
			/*// Find the closest player (in aggroRadius)
			ANetworkCharacter* closest = nullptr;
			float closestDist = BIG_NUMBER;
			TArray<ANetworkCharacter*> players = mode->GetPlayers();
			const float aggroSqr = aggroRadius * aggroRadius;
			for (int32 i = 0; i < players.Num(); i++)
			{
			const float distSqr = FVector::DistSquared(m_mobs[0]->GetActorLocation(), players[i]->GetActorLocation());
			if (distSqr < closestDist && distSqr <= aggroSqr)
			{
			closest = players[i];
			closestDist = distSqr;
			}
			}*/
			ANetworkPlayer* closest = m_GetClosestPlayer();
			if (closest)
			{
				// Attack the closest player
				for (int32 i = 0; i < spawns.Num(); i++)
				{
					if (m_mobs[i] == nullptr)
						continue;

					m_mobs[i]->target = (closest);
					m_isPulled = true;
				}
			}
		}

	}
}

FVector APatrolSpawn::SpawnResetPosition()
{
	return m_lastPosition;
}

void APatrolSpawn::SpawnMobs()
{
	m_RespawnMobs();
}
void APatrolSpawn::m_RespawnMobs()
{
	UWorld* const world = GetWorld();
	if (!world)
		return;
	for (int32 i = 0; i < m_mobs.Num(); i++)
	{
		if (!spawns[i])
			continue;
		if (m_mobs[i] == nullptr)
		{
			// Respawn!
			FTransform spawnTransform = GetTransform();
			if (hasGeneral)
			{
				spawnTransform.SetLocation(FVector(formationPoints[i].X, formationPoints[i].Y,0) + GetActorLocation());
			}
			else 
			{
				spawnTransform.SetLocation(GetActorLocation() + FVector(0, 0, 120));
			}
			spawnTransform.SetRotation(FRotator(GetActorRotation() + FRotator(0, formationRotation[i], 0)).Quaternion());
			ANPCBase* character = world->SpawnActorDeferred<ANPCBase>(spawns[i], spawnTransform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod());
			character->SetSpawn(this);
			character->SetTeam((int32)team);
			m_mobs[i] = character;
			m_mobCount++;
			m_OnMobSpawn(i);
			UGameplayStatics::FinishSpawningActor(character, spawnTransform);
			character->SpawnDefaultController();
		}
	}

	m_respawnTimer = respawnTime;
}
class ANetworkPlayer* APatrolSpawn::m_GetClosestPlayer()
{
	UWorld* const world = GetWorld();
	if (!world)
		return nullptr;
	ADefaultGameMode* const mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
	if (!mode)
		return nullptr;

	// Find the closest player (in aggroRadius)
	ANetworkPlayer* closest = nullptr;
	float closestDist = BIG_NUMBER;
	TArray<ANetworkPlayer*> players = mode->GetPlayers();
	const float aggroSqr = aggroRadius * aggroRadius;
	for (int32 i = 0; i < players.Num(); i++)
	{
		const float distSqr = FVector2D::DistSquared(FVector2D(m_mobs[0]->GetActorLocation()), FVector2D(players[i]->GetActorLocation()));
		if (distSqr < closestDist && distSqr <= aggroSqr)
		{
			closest = players[i];
			closestDist = distSqr;
		}
	}

	return closest;
}