/**
 * Find the error report folder and check it matches the app name if provided
 */
FPlatformErrorReport LoadErrorReport()
{
	if (ReportDirectoryAbsolutePath.IsEmpty())
	{
		UE_LOG(CrashReportClientLog, Warning, TEXT("No error report found"));
		return FPlatformErrorReport();
	}

	FPlatformErrorReport ErrorReport(ReportDirectoryAbsolutePath);
	
	FString XMLWerFilename;
	ErrorReport.FindFirstReportFileWithExtension( XMLWerFilename, TEXT( ".xml" ) );

	extern FCrashDescription& GetCrashDescription();
	GetCrashDescription() = FCrashDescription( ReportDirectoryAbsolutePath / XMLWerFilename );

#if CRASH_REPORT_UNATTENDED_ONLY
	return ErrorReport;
#else
	if( !GameNameFromCmd.IsEmpty() && GameNameFromCmd != GetCrashDescription().GameName )
	{
		// Don't display or upload anything if it's not the report we expected
		ErrorReport = FPlatformErrorReport();
	}
	return ErrorReport;
#endif
}
void FCrashReportClient::AllowToBeContacted_OnCheckStateChanged( ECheckBoxState NewRadioState )
{
	FCrashReportClientConfig::Get().SetAllowToBeContacted( NewRadioState == ECheckBoxState::Checked );

	// Refresh PII based on the bAllowToBeContacted flag.
	GetCrashDescription().UpdateIDs();

	// Update diagnostics text.
	FormattedDiagnosticText = FCrashReportUtil::FormatDiagnosticText( DiagnosticText, GetCrashDescription().MachineId, GetCrashDescription().EpicAccountId, GetCrashDescription().UserName );
}
void FCrashReportClient::FinalizeDiagnoseReportWorker( FText ReportText )
{
	DiagnosticText = ReportText;
	FormattedDiagnosticText = FCrashReportUtil::FormatDiagnosticText( ReportText, GetCrashDescription().MachineId, GetCrashDescription().EpicAccountId, GetCrashDescription().UserName );

	auto DiagnosticsFilePath = ErrorReport.GetReportDirectory() / FCrashReportClientConfig::Get().GetDiagnosticsFilename();
	Uploader.LocalDiagnosisComplete(FPaths::FileExists(DiagnosticsFilePath) ? DiagnosticsFilePath : TEXT(""));
}
FReply FCrashReportClient::SubmitAndRestart()
{
	Submit();

	const FString CrashedAppPath = ErrorReport.FindCrashedAppPath();
	const FCrashDescription& CrashDescription = GetCrashDescription();
	const FString CommandLineArguments = CrashDescription.CommandLine;

	FPlatformProcess::CreateProc(*CrashedAppPath, *CommandLineArguments, true, false, false, NULL, 0, NULL, NULL);

	return FReply::Handled();
}
FReply FCrashReportClient::Submit()
{
	if (ErrorReport.HasFilesToUpload())
	{
		// Send analytics.
		GetCrashDescription().SendAnalytics();
	}

	bSendData = true;
	StoreCommentAndUpload();
	bShouldWindowBeHidden = true;
	return FReply::Handled();
}
FCrashReportClient::FCrashReportClient(const FPlatformErrorReport& InErrorReport)
	: DiagnosticText( LOCTEXT("ProcessingReport", "Processing crash report ...") )
	, DiagnoseReportTask(nullptr)
	, ErrorReport( InErrorReport )
	, Uploader( FCrashReportClientConfig::Get().GetReceiverAddress() )
	, bBeginUploadCalled(false)
	, bShouldWindowBeHidden(false)
	, bSendData(false)
{

	if (!ErrorReport.TryReadDiagnosticsFile(DiagnosticText) && !FParse::Param(FCommandLine::Get(), TEXT("no-local-diagnosis")))
	{
		DiagnoseReportTask = new FAsyncTask<FDiagnoseReportWorker>( this );
		DiagnoseReportTask->StartBackgroundTask();
	}
	else if( !DiagnosticText.IsEmpty() )
	{
		FormattedDiagnosticText = FCrashReportUtil::FormatDiagnosticText( DiagnosticText, GetCrashDescription().MachineId, GetCrashDescription().EpicAccountId, GetCrashDescription().UserName );
	}
}
void RunCrashReportClient(const TCHAR* CommandLine)
{
	// Override the stack size for the thread pool.
	FQueuedThreadPool::OverrideStackSize = 256 * 1024;

	// Set up the main loop
	GEngineLoop.PreInit(CommandLine);

	// Initialize config.
	FCrashReportClientConfig::Get();

	const bool bUnattended = 
#if CRASH_REPORT_UNATTENDED_ONLY
		true;
#else
		FApp::IsUnattended();
#endif // CRASH_REPORT_UNATTENDED_ONLY

	// Set up the main ticker
	FMainLoopTiming MainLoop(IdealTickRate, bUnattended ? EMainLoopOptions::CoreTickerOnly : EMainLoopOptions::UsingSlate);

	// Find the report to upload in the command line arguments
	ParseCommandLine(CommandLine);

	// Increase the HttpSendTimeout to 5 minutes
	GConfig->SetFloat(TEXT("HTTP"), TEXT("HttpSendTimeout"), 5*60.0f, GEngineIni);

	FPlatformErrorReport::Init();
	auto ErrorReport = LoadErrorReport();
	
	if( ErrorReport.HasFilesToUpload() )
	{
		// Send analytics.
		extern FCrashDescription& GetCrashDescription();
		GetCrashDescription().SendAnalytics();
	}

	if (bUnattended)
	{
		ErrorReport.SetUserComment( NSLOCTEXT( "CrashReportClient", "UnattendedMode", "Sent in the unattended mode" ), false );
		FCrashReportClientUnattended CrashReportClient( ErrorReport );

		// loop until the app is ready to quit
		while (!GIsRequestingExit)
		{
			MainLoop.Tick();
		}
	}
	else
	{
#if !CRASH_REPORT_UNATTENDED_ONLY
		// crank up a normal Slate application using the platform's standalone renderer
		FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer());

		// Prepare the custom Slate styles
		FCrashReportClientStyle::Initialize();

		// Create the main implementation object
		TSharedRef<FCrashReportClient> CrashReportClient = MakeShareable(new FCrashReportClient(ErrorReport));

		// open up the app window	
		TSharedRef<SCrashReportClient> ClientControl = SNew(SCrashReportClient, CrashReportClient);

		auto Window = FSlateApplication::Get().AddWindow(
			SNew(SWindow)
			.Title(NSLOCTEXT("CrashReportClient", "CrashReportClientAppName", "Unreal Engine 4 Crash Reporter"))
			.ClientSize(InitialWindowDimensions)
			[
				ClientControl
			]);

		Window->SetRequestDestroyWindowOverride(FRequestDestroyWindowOverride::CreateSP(CrashReportClient, &FCrashReportClient::RequestCloseWindow));

		// Setting focus seems to have to happen after the Window has been added
		FSlateApplication::Get().ClearKeyboardFocus(EFocusCause::Cleared);

		// Debugging code
		if (RunWidgetReflector)
		{
			FSlateApplication::Get().AddWindow(
				SNew(SWindow)
				.ClientSize(FVector2D(800, 600))
				[
					FModuleManager::LoadModuleChecked<ISlateReflectorModule>("SlateReflector").GetWidgetReflector()
				]);
		}

		// loop until the app is ready to quit
		while (!GIsRequestingExit)
		{
			MainLoop.Tick();

			if (CrashReportClient->ShouldWindowBeHidden())
			{
				Window->HideWindow();
			}
		}

		// Clean up the custom styles
		FCrashReportClientStyle::Shutdown();

		// Close down the Slate application
		FSlateApplication::Shutdown();
#endif // !CRASH_REPORT_UNATTENDED_ONLY
	}

	FPlatformErrorReport::ShutDown();

	FEngineLoop::AppPreExit();
	FTaskGraphInterface::Shutdown();

	FEngineLoop::AppExit();
}
FString FCrashReportClient::GetCrashedAppName() const
{
	return GetCrashDescription().GameName;
}