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


#include "Game/ShooterGameMode.h"
#include "UObject/ConstructorHelpers.h"
#include "Kismet/GameplayStatics.h"

#if WITH_GAMELIFT
#include "GameLiftServerSDKModels.h"
#endif

#include "GenericPlatform/GenericPlatformOutputDevices.h"
#include "Misc/TypeContainer.h"

DEFINE_LOG_CATEGORY(LogShooterGameMode)

AShooterGameMode::AShooterGameMode()
{
	UE_LOG(LogShooterGameMode, Log, TEXT("Initializing ShooterGameMode..."));
}

void AShooterGameMode::BeginPlay()
{
	Super::BeginPlay();
#if WITH_GAMELIFT
	InitGameLift();
#endif
}

#if WITH_GAMELIFT
void AShooterGameMode::SetServerParameters(TSharedPtr<FServerParameters> OutServerParameters)
{
	// AuthToken returned from the "aws gamelift get-compute-auth-token" API.  Note this will expire and require a new call to the API after 15 minutes.
	FString GameLiftAnywhereAuthToken = "";
	if (FParse::Value(FCommandLine::Get(), TEXT("-authtoken="), GameLiftAnywhereAuthToken))
	{
		OutServerParameters->m_authToken = TCHAR_TO_UTF8(*GameLiftAnywhereAuthToken);
	}
	else
	{
		UE_LOG(LogShooterGameMode, Warning, TEXT("-authtoken not found in command line."));
	}
		
	//  The Host/compute-name of the GameLift Anywhere instance
	FString GameLiftAnywhereHostId = "";
	if (FParse::Value(FCommandLine::Get(), TEXT("-hostid="), GameLiftAnywhereHostId))
	{
		OutServerParameters->m_hostId = TCHAR_TO_UTF8(*GameLiftAnywhereHostId);
	}
	else
	{
		UE_LOG(LogShooterGameMode, Warning, TEXT("-hostid not found in command line."));
	}
		
	//  The AnywhereFleet ID.
	FString GameLiftAnywhereFleetId = "";
	if (FParse::Value(FCommandLine::Get(), TEXT("-fleetid="), GameLiftAnywhereFleetId))
	{
		OutServerParameters->m_fleetId = TCHAR_TO_UTF8(*GameLiftAnywhereFleetId);
	}
	else
	{
		UE_LOG(LogShooterGameMode, Warning, TEXT("-fleetid not found in command line."));
	}
		
	//  The WebSocket URL (GameLiftServiceSdkEndpoint)
	FString GameLiftAnywhereWebSocketUrl = "";
	if (FParse::Value(FCommandLine::Get(), TEXT("-websocketurl="), GameLiftAnywhereWebSocketUrl))
	{
		OutServerParameters->m_webSocketUrl = TCHAR_TO_UTF8(*GameLiftAnywhereWebSocketUrl);
	}
	else
	{
		UE_LOG(LogShooterGameMode, Warning, TEXT("-websocketurl not found in command line."));
	}
	
	// The PID of the running process
    OutServerParameters->m_processId = FString::Printf(TEXT("%d"), GetCurrentProcessId());
}

void AShooterGameMode::LogServerParameters(TSharedPtr<FServerParameters> ServerParameters)
{
	//  Log Server Parameters
		
	UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_YELLOW);
	UE_LOG(LogShooterGameMode, Log, TEXT(">>>> WebSocket URL: %s"), *ServerParameters->m_webSocketUrl);
	UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Fleet ID: %s"), *ServerParameters->m_fleetId);
	UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Process ID: %s"), *ServerParameters->m_processId);
	UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Host ID (Compute Name): %s"), *ServerParameters->m_hostId);
	UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Auth Token: %s"), *ServerParameters->m_authToken);
	//		UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Aws Region: %s"), *ServerParameters.m_awsRegion);
	//		UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Access Key: %s"), *ServerParameters.m_accessKey);
	//		UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Secret Key: %s"), *ServerParameters.m_secretKey);
	//		UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Session Token: %s"), *ServerParameters.m_sessionToken);
	UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE);
}
#endif

void AShooterGameMode::ParseCommandLinePort(int32& OutPort)
{
	TArray<FString> CommandLineTokens;
	TArray<FString> CommandLineSwitches;
	
	FCommandLine::Parse(FCommandLine::Get(), CommandLineTokens, CommandLineSwitches);
	
	for (FString SwitchStr : CommandLineSwitches)
	{
		FString Key;
		FString Value;
		
		if (SwitchStr.Split("=", &Key, &Value))
		{
			if (Key.Equals(TEXT("port"), ESearchCase::IgnoreCase))
			{
				OutPort = FCString::Atoi(*Value);
				return;
			}
		}
	}
}

void AShooterGameMode::InitGameLift()
{

#if WITH_GAMELIFT
	UE_LOG(LogShooterGameMode, Log, TEXT("Calling InitGameLift"));
	
	//  Getting the module first.
	FGameLiftServerSDKModule* GameLiftSdkModule = &FModuleManager::LoadModuleChecked<FGameLiftServerSDKModule>(FName("GameLiftServerSDK"));
	
	//Define the server parameters for a GameLift Anywhere fleet.  These are not needed for a GameLift managed EC2 fleet.
	FServerParameters ServerParameters;
	
	bool bIsAnywhereActive = false;
	if (FParse::Param(FCommandLine::Get(), TEXT("glAnywhere")))
	{
		bIsAnywhereActive = true;
	}
	
	if (bIsAnywhereActive)
	{
		UE_LOG(LogShooterGameMode, Log, TEXT("Configuring server parameters for Anywhere..."));
		
		// If GameLift Anywhere is enabled, parse command line arguments and pass them in the ServerParameters object.
		
		SetServerParameters(MakeShared<FServerParameters>(ServerParameters));
		LogServerParameters(MakeShared<FServerParameters>(ServerParameters));
	}

	UE_LOG(LogShooterGameMode, Log, TEXT("Initializing the GameLift Server..."));
	
	// InitSDK will establish a local connection with GameLift's agent to enable futher communication
	
	FGameLiftGenericOutcome InitSdkOutcome = GameLiftSdkModule->InitSDK(ServerParameters);
	if (InitSdkOutcome.IsSuccess())
	{
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_GREEN);
		UE_LOG(LogShooterGameMode, Log, TEXT("GameLift Init SDK Succeeded"));
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE);
	}
	else
	{
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_RED);
		UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: InitSDK failed : ("));
		FGameLiftError GameLiftError = InitSdkOutcome.GetError();
		UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: %s"), *GameLiftError.m_errorMessage);
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE);
		return;
	}
	
	
	// Implement callback function onStartGameSession
	// GameLift sends a game session activation request to the game server
	// and passes a game session object with game properties and other settings.
	// Here is where a game server takes action based on the game session object.
	// When the game server is ready to receive incoming player connections,
	// it invokes the server SDK call ActivateGameSession().
	
	auto OnGameSession = [=](const Aws::GameLift::Server::Model::GameSession& InGameSession)
	{
		FString GameSessionId = FString(InGameSession.GetGameSessionId());
		UE_LOG(LogShooterGameMode, Log, TEXT("GameSession Initializing: %s"), *GameSessionId);
		GameLiftSdkModule->ActivateGameSession();
	};
	
	ProcessParameters->OnStartGameSession.BindLambda(OnGameSession);
	
	auto OnTerminate = [=]()
	{
		UE_LOG(LogShooterGameMode, Log, TEXT("Game Server Process is terminating"));
		GameLiftSdkModule->ProcessEnding();
	};
	
	ProcessParameters->OnTerminate.BindLambda(OnTerminate);
	
	auto OnHealthCheck = [=]()
	{
		UE_LOG(LogShooterGameMode, Log, TEXT("Performing Health Check"));
		return true;
	};
	
	
	
	ProcessParameters->OnHealthCheck.BindLambda(OnHealthCheck);
	
	int32 Port = FURL::UrlConfig.DefaultPort;
	ParseCommandLinePort(Port);
	
	ProcessParameters->port = Port;
	
	// Here, the game server tells GameLift where to find game session Log files.
	// At the end of a game session, GameLift uploads everything in the specified
	// location and stores it in the cloud for access later.
	
	TArray<FString> LogFiles;
	LogFiles.Add(TEXT("FPSTemplate/Saved/Logs/FPSTemplate.log"));
	ProcessParameters->logParameters = LogFiles;
	
	
	//  The game server calls ProcessReady() to tell Amazon GameLift Servers it's ready to host game sessions.
	UE_LOG(LogShooterGameMode, Log, TEXT("Calling Process Ready..."));
	FGameLiftGenericOutcome ProcessReadyOutcome = GameLiftSdkModule->ProcessReady(*ProcessParameters);
	
	if (ProcessReadyOutcome.IsSuccess())
	{
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_GREEN);
		UE_LOG(LogShooterGameMode, Log, TEXT("Process Ready!"));
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE);
	}
	else
	{
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_RED);
		UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: Process Ready Failed!"));
		FGameLiftError ProcessReadyError = ProcessReadyOutcome.GetError();
		UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: %s"), *ProcessReadyError.m_errorMessage);
		UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE);
	}

	UE_LOG(LogShooterGameMode, Log, TEXT("InitGameLift completed!"));
	
#endif

	
};
