// Copyright Epic Games, Inc. All Rights Reserved.

#include "CorruptedMemoryGameMode.h"

#include "CMGameInstance.h"
#include "CM_GameState.h"
#include "CM_PlayerController.h"
#include "CM_PlayerState.h"
#include "CorruptedMemoryCharacter.h"
#include "GameFramework/GameStateBase.h"
#include "GameFramework/PlayerStart.h"
#include "GameFramework/PlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "UObject/ConstructorHelpers.h"

ACorruptedMemoryGameMode::ACorruptedMemoryGameMode()
	: Super()
{
	// set default pawn class to our Blueprinted character
	static ConstructorHelpers::FClassFinder<APawn> PlayerPawnClassFinder(TEXT("/Game/FirstPerson/Blueprints/BP_FirstPersonCharacter"));
	DefaultPawnClass = PlayerPawnClassFinder.Class;

	static ConstructorHelpers::FClassFinder<ACharacter> PlayerCharacterClassFinder(TEXT("/Game/FirstPerson/Blueprints/BP_FirstPersonCharacter"));
	PlayerCharacterClass = PlayerCharacterClassFinder.Class;
}

void ACorruptedMemoryGameMode::PostLogin(APlayerController* NewPlayer)
{
	Super::PostLogin(NewPlayer);
	NewPlayer->GetPlayerState<ACM_PlayerState>()->ClientPlayerJoin();
	FString MaxPlayers;
	FParse::Value(FCommandLine::Get(), TEXT("maxPlayers="), MaxPlayers);
	if (GetWorld()->GetGameState()->PlayerArray.Num() == FCString::Atoi(*MaxPlayers))
	{
		FTimerHandle TimerHandle;
		GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]()
		{
			for (const auto PlayerState : GetWorld()->GetGameState()->PlayerArray)
			{
				Cast<ACorruptedMemoryCharacter>(PlayerState->GetPlayerController()->GetCharacter())->Client_GameStart();
				Cast<ACorruptedMemoryCharacter>(PlayerState->GetPlayerController()->GetCharacter())->Client_EnableUserInput();
				PlayerState->GetPlayerController()->EnableInput(PlayerState->GetPlayerController());
				PlayerState->GetPlayerController()->SetInputMode(FInputModeGameOnly());
			}

			FTimerHandle TimerHandle2;
			GetWorld()->GetTimerManager().SetTimer(TimerHandle2, [this]()
			{
				for (const auto PlayerState : GetWorld()->GetGameState()->PlayerArray)
				{
					Cast<ACorruptedMemoryCharacter>(PlayerState->GetPlayerController()->GetCharacter())->Client_GameEnd();
					Cast<ACorruptedMemoryCharacter>(PlayerState->GetPlayerController()->GetCharacter())->Client_DisableUserInput();
					PlayerState->GetPlayerController()->DisableInput(PlayerState->GetPlayerController());
					PlayerState->GetPlayerController()->SetInputMode(FInputModeUIOnly());
				}
				FTimerHandle TimerHandle3;
				GetWorld()->GetTimerManager().SetTimer(TimerHandle3, [this]()
				{
					FGenericPlatformMisc::RequestExit(false);
				}, 10, false);
			}, 125, false);
		}, 5.f, false);
	}
}

void ACorruptedMemoryGameMode::SuccessfulScan(const FString& Username) const
{
	ACM_GameState* CurrentGameState = GetWorld()->GetGameState<ACM_GameState>();
	const auto PlayerState = CurrentGameState->PlayerArray.FindByPredicate([Username](const APlayerState* PlayerState) { return PlayerState->GetPlayerName() == Username; });
	PlayerState->Get()->SetScore(PlayerState->Get()->GetScore() + SuccessfulScanScore);
}

void ACorruptedMemoryGameMode::AddGameplayTagToShelf(AActor* Shelving, const FString& GameplayTag)
{
	ACM_Shelving** FoundShelf = Shelves.FindByPredicate([Shelving](const ACM_Shelving* Shelf) { return Shelf == Shelving; });
	(*FoundShelf)->GameplayTags.Add(GameplayTag);
}

void ACorruptedMemoryGameMode::RemoveGameplayTagToShelf(AActor* Shelving, const FString& GameplayTag)
{
	ACM_Shelving** FoundShelf = Shelves.FindByPredicate([Shelving](const ACM_Shelving* Shelf) { return Shelf == Shelving; });
	(*FoundShelf)->GameplayTags.Remove(GameplayTag);
}

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

	FString MapName = GetWorld()->GetMapName();
	MapName.RemoveFromStart(GetWorld()->StreamingLevelsPrefix);
	if (MapName == "ClientEntry") return;

	//Gets all PlayerStart Actors and sorts them by PlayerStartTag
	TArray<AActor*> PlayerStartActors;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerStart::StaticClass(), PlayerStartActors);
	PlayerStartActors.Sort([](const AActor& A, const AActor& B) { return FCString::Atoi(*Cast<APlayerStart>(&A)->PlayerStartTag.ToString()) < FCString::Atoi(*Cast<APlayerStart>(&B)->PlayerStartTag.ToString()); });
	for (const AActor* PlayerStartActor : PlayerStartActors)
	{
		SpawnPoints.Add(PlayerStartActor->GetActorTransform());
	}

	TArray<AActor*> StoreShelves;
	UGameplayStatics::GetAllActorsWithTag(GetWorld(), "StoreShelf", StoreShelves);
	for (AActor* StoreShelf : StoreShelves)
	{
		Shelves.Add(Cast<ACM_Shelving>(StoreShelf));
	}

	constexpr int StartCorruptedAmount = 35;
	for (int i = 0; i < StartCorruptedAmount; i++)
	{
		ACM_Shelving* ShelfToCorrupt = Shelves[FMath::RandRange(0, Shelves.Num() - 1)];
		ShelfToCorrupt->GameplayTags.Add("Corrupted");
	}

	FTimerHandle TimerHandle;
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]()
	{
		ACM_Shelving* ShelfToCorrupt = Shelves[FMath::RandRange(0, Shelves.Num() - 1)];
		if (ShelfToCorrupt->GameplayTags.Contains("Corrupted")) return;
		if (ShelfToCorrupt->GameplayTags.Contains("Complete")) return;
		ShelfToCorrupt->GameplayTags.Add("Corrupted");
	}, 10.f, true);
}

void ACorruptedMemoryGameMode::ServerSpawnPlayer_Implementation(APlayerController* PlayerController)
{
	if (!IsValid(PlayerController)) return;
	if (IsValid(PlayerController->GetPawn()))
	{
		PlayerController->GetPawn()->Destroy();
	}
	ACharacter* SpawnedPlayerCharacter = GetWorld()->SpawnActor<ACharacter>(PlayerCharacterClass, SpawnPoints[PlayerController->GetPlayerState<ACM_PlayerState>()->GetPlayerId()]);
	PlayerController->Possess(SpawnedPlayerCharacter);
	PlayerController->EnableInput(PlayerController);
	PlayerController->SetInputMode(FInputModeGameOnly());
}
