#include "HyperTwistXR/HyperTwistXrTrainingGameMode.h"

#include "EngineUtils.h"
#include "GameFramework/PlayerController.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "HyperTwistUX/HyperTwistPlayerControllerBase.h"
#include "HyperTwistXR/HyperTwistXrPackageValidationLibrary.h"
#include "HyperTwistXR/HyperTwistXrTrainingPawn.h"
#include "Misc/CommandLine.h"
#include "Misc/Parse.h"

AHyperTwistXrTrainingGameMode::AHyperTwistXrTrainingGameMode()
{
	PrimaryActorTick.bCanEverTick = true;
	PlayerControllerClass = AHyperTwistPlayerControllerBase::StaticClass();
	DefaultPawnClass = AHyperTwistXrTrainingPawn::StaticClass();
	ConfiguredValidationObservationSeconds = DefaultValidationObservationSeconds;
}

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

	ApplyValidationOverridesFromCommandLine();
	RefreshObservedValidationState();
}

void AHyperTwistXrTrainingGameMode::Tick(const float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if (!bPackagedValidationRequested || bPackagedValidationFinalized)
	{
		return;
	}

	RefreshObservedValidationState();
	RemainingValidationObservationSeconds -= FMath::Max(static_cast<double>(DeltaSeconds), 0.0);
	if (ShouldFinalizePackagedValidationCapture())
	{
		FinalizePackagedValidationCapture();
	}
}

void AHyperTwistXrTrainingGameMode::ApplyValidationOverridesFromCommandLine()
{
	bPackagedValidationRequested = false;
	bPackagedValidationFinalized = false;
	ValidationReportPath.Reset();
	ConfiguredValidationObservationSeconds = DefaultValidationObservationSeconds;
	RemainingValidationObservationSeconds = -1.0;

	if (!bEnableCommandLinePackagedValidation)
	{
		return;
	}

	const TCHAR* CommandLine = FCommandLine::Get();
	FString ParsedReportPath;
	if (!FParse::Value(
			CommandLine,
			TEXT("HyperTwistXrValidationReportPath="),
			ParsedReportPath))
	{
		return;
	}

	ParsedReportPath.TrimQuotesInline();
	if (ParsedReportPath.IsEmpty())
	{
		return;
	}

	bPackagedValidationRequested = true;
	ValidationReportPath = ParsedReportPath;

	double ParsedObservationSeconds = DefaultValidationObservationSeconds;
	if (FParse::Value(
			CommandLine,
			TEXT("HyperTwistXrValidationObservationSeconds="),
			ParsedObservationSeconds))
	{
		ParsedObservationSeconds = FMath::Max(ParsedObservationSeconds, 0.0);
	}

	ConfiguredValidationObservationSeconds = ParsedObservationSeconds;
	RemainingValidationObservationSeconds = ParsedObservationSeconds;
}

AHyperTwistXrTrainingPawn* AHyperTwistXrTrainingGameMode::ResolveTrainingPawn() const
{
	if (GetWorld() == nullptr)
	{
		return nullptr;
	}

	if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
	{
		if (AHyperTwistXrTrainingPawn* TrainingPawn =
				Cast<AHyperTwistXrTrainingPawn>(PlayerController->GetPawn()))
		{
			return TrainingPawn;
		}
	}

	for (TActorIterator<AHyperTwistXrTrainingPawn> ActorIt(GetWorld()); ActorIt; ++ActorIt)
	{
		return *ActorIt;
	}

	return nullptr;
}

void AHyperTwistXrTrainingGameMode::RefreshObservedValidationState()
{
	if (AHyperTwistXrTrainingPawn* TrainingPawn = ResolveTrainingPawn())
	{
		bObservedHeadMountedDisplaySessionDuringValidation |=
			TrainingPawn->HasObservedHeadMountedDisplaySession();
		const float ObservationMagnitude =
			TrainingPawn->GetControllerInputObservationMagnitude();
		MaxObservedControllerInputMagnitude = FMath::Max(
			MaxObservedControllerInputMagnitude,
			ObservationMagnitude
		);
		bObservedControllerInputActivityDuringValidation |=
			TrainingPawn->HasObservedControllerInputActivity();
	}
}

bool AHyperTwistXrTrainingGameMode::ShouldFinalizePackagedValidationCapture() const
{
	return RemainingValidationObservationSeconds <= 0.0
		|| (bObservedHeadMountedDisplaySessionDuringValidation
			&& bObservedControllerInputActivityDuringValidation);
}

void AHyperTwistXrTrainingGameMode::FinalizePackagedValidationCapture()
{
	if (bPackagedValidationFinalized)
	{
		return;
	}

	const AHyperTwistXrTrainingPawn* TrainingPawn = ResolveTrainingPawn();
	FHyperTwistXrPackagedValidationReport Report =
		UHyperTwistXrPackageValidationLibrary::BuildPackagedValidationReport(
			TrainingPawn,
			ResolveActiveCookMapAssetPath(),
			ValidationReportPath,
			ConfiguredValidationObservationSeconds,
			bObservedHeadMountedDisplaySessionDuringValidation,
			bObservedControllerInputActivityDuringValidation,
			MaxObservedControllerInputMagnitude
		);

	FString WriteError;
	if (!UHyperTwistXrPackageValidationLibrary::WritePackagedValidationReportToFile(
			Report,
			ValidationReportPath,
			WriteError))
	{
		UE_LOG(
			LogTemp,
			Error,
			TEXT("%s"),
			WriteError.IsEmpty()
				? TEXT("Failed to write the XR packaged validation report.")
				: *WriteError
		);
	}

	bPackagedValidationFinalized = true;
	FGenericPlatformMisc::RequestExit(false);
}

FString AHyperTwistXrTrainingGameMode::ResolveActiveCookMapAssetPath() const
{
	if (GetWorld() == nullptr)
	{
		return UHyperTwistXrPackageValidationLibrary::GetDefaultPackagedValidationCookMapPath();
	}

	if (const UPackage* WorldPackage = GetWorld()->GetPackage())
	{
		const FString PackageName = WorldPackage->GetName();
		if (PackageName.StartsWith(TEXT("/Game/")))
		{
			return PackageName;
		}
	}

	return UHyperTwistXrPackageValidationLibrary::GetDefaultPackagedValidationCookMapPath();
}
