void UGameplayDebuggingControllerComponent::OnActivationKeyPressed()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	if (GetDebuggingReplicator() && PlayerOwner.IsValid())
	{
		if (!bToolActivated)
		{
			Activate();
			SetComponentTickEnabled(true);

			BindAIDebugViewKeys(AIDebugViewInputComponent);
			if (PlayerOwner.IsValid())
			{
				PlayerOwner->PushInputComponent(AIDebugViewInputComponent);
			}

			GetDebuggingReplicator()->EnableDraw(true);
			GetDebuggingReplicator()->ServerReplicateMessage(nullptr, EDebugComponentMessage::ActivateReplication, EAIDebugDrawDataView::Empty);
		}

		ControlKeyPressedTime = GetWorld()->GetTimeSeconds();
		EnableTargetSelection(true);
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::CloseDebugTool()
{
	if (GetDebuggingReplicator())
	{
		Deactivate();
		SetComponentTickEnabled(false);
		GetDebuggingReplicator()->ServerReplicateMessage(NULL, EDebugComponentMessage::DeactivateReplilcation, EAIDebugDrawDataView::Empty);
		GetDebuggingReplicator()->EnableDraw(false);
		GetDebuggingReplicator()->ServerReplicateMessage(NULL, EDebugComponentMessage::DeactivateReplilcation, EAIDebugDrawDataView::Empty);
		bToolActivated = false;
	}
}
void UGameplayDebuggingControllerComponent::SetActiveViews(uint32 InActiveViews)
{
	GameplayDebuggerSettings(GetDebuggingReplicator()).DebuggerShowFlags = InActiveViews;

	if (GetDebuggingReplicator())
	{
		for (uint32 Index = 0; Index < EAIDebugDrawDataView::MAX; ++Index)
		{
			EAIDebugDrawDataView::Type CurrentView = (EAIDebugDrawDataView::Type)Index;
			EnableActiveView(CurrentView, IsViewActive(CurrentView));
		}
	}
}
void UGameplayDebuggingControllerComponent::CloseDebugTool()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	if (GetDebuggingReplicator())
	{
		Deactivate();
		SetComponentTickEnabled(false);
		GetDebuggingReplicator()->ServerReplicateMessage(nullptr, EDebugComponentMessage::DeactivateReplilcation, EAIDebugDrawDataView::Empty);
		GetDebuggingReplicator()->EnableDraw(false);
		GetDebuggingReplicator()->ServerReplicateMessage(nullptr, EDebugComponentMessage::DeactivateReplilcation, EAIDebugDrawDataView::Empty);
		bToolActivated = false;
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::EnableActiveView(EAIDebugDrawDataView::Type View, bool bEnable)
{
	bEnable ? GameplayDebuggerSettings(GetDebuggingReplicator()).SetFlag(View) : GameplayDebuggerSettings(GetDebuggingReplicator()).ClearFlag(View);

	if (GetDebuggingReplicator())
	{
		GetDebuggingReplicator()->ServerReplicateMessage(DebugAITargetActor, bEnable ? EDebugComponentMessage::ActivateDataView : EDebugComponentMessage::DeactivateDataView, View);
#if WITH_EQS
		if (GetDebuggingReplicator()->GetDebugComponent() && View == EAIDebugDrawDataView::EQS)
		{
			GetDebuggingReplicator()->GetDebugComponent()->EnableClientEQSSceneProxy(IsViewActive(EAIDebugDrawDataView::EQS));
		}
#endif // WITH_EQS
	}
}
void UGameplayDebuggingControllerComponent::NextEQSQuery()
{
	if (IsViewActive(EAIDebugDrawDataView::EQS))
	{
		GetDebuggingReplicator()->OnChangeEQSQuery.Broadcast();
	}
}
void AGameplayDebuggingHUDComponent::DrawOverHeadInformation(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	APawn* MyPawn = Cast<APawn>(DebugComponent->GetSelectedActor());
	const FVector Loc3d = MyPawn ? MyPawn->GetActorLocation() + FVector(0.f, 0.f, MyPawn->GetSimpleCollisionHalfHeight()) : FVector::ZeroVector;

	if (OverHeadContext.Canvas->SceneView == NULL || OverHeadContext.Canvas->SceneView->ViewFrustum.IntersectBox(Loc3d, FVector::ZeroVector) == false)
	{
		return;
	}

	const FVector ScreenLoc = OverHeadContext.Canvas->Project(Loc3d);
	static const FVector2D FontScale(1.f, 1.f);
	UFont* Font = GEngine->GetSmallFont();

	float TextXL = 0.f;
	float YL = 0.f;
	FString ObjectName = FString::Printf( TEXT("{yellow}%s {white}(%s)"), *DebugComponent->ControllerName, *DebugComponent->PawnName);
	CalulateStringSize(OverHeadContext, OverHeadContext.Font, ObjectName, TextXL, YL);

	bool bDrawFullOverHead = MyPawn != nullptr && GetDebuggingReplicator()->GetSelectedActorToDebug() == MyPawn;
	float IconXLocation = OverHeadContext.DefaultX;
	float IconYLocation = OverHeadContext.DefaultY;
	if (bDrawFullOverHead)
	{
		OverHeadContext.DefaultX -= (0.5f*TextXL*FontScale.X);
		OverHeadContext.DefaultY -= (1.2f*YL*FontScale.Y);
		IconYLocation = OverHeadContext.DefaultY;
		OverHeadContext.CursorX = OverHeadContext.DefaultX;
		OverHeadContext.CursorY = OverHeadContext.DefaultY;
	}

	if (DebugComponent->DebugIcon.Len() > 0)
	{
		UTexture2D* RegularIcon = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, *DebugComponent->DebugIcon, NULL, LOAD_NoWarn | LOAD_Quiet, NULL);
		if (RegularIcon)
		{
			FCanvasIcon Icon = UCanvas::MakeIcon(RegularIcon);
			if (Icon.Texture)
			{
				const float DesiredIconSize = bDrawFullOverHead ? 32.f : 16.f;
				DrawIcon(OverHeadContext, FColor::White, Icon, IconXLocation, IconYLocation - DesiredIconSize, DesiredIconSize / Icon.Texture->GetSurfaceWidth());
			}
		}
	}

	if (bDrawFullOverHead)
	{
		OverHeadContext.FontRenderInfo.bEnableShadow = bDrawFullOverHead;
		PrintString(OverHeadContext, bDrawFullOverHead ? FColor::White : FColor(255, 255, 255, 128), FString::Printf(TEXT("%s\n"), *ObjectName));
		OverHeadContext.FontRenderInfo.bEnableShadow = false;
	}

	if (EngineShowFlags.DebugAI)
	{
		DrawPath(PC, DebugComponent);
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::OpenDebugTool()
{
	if (GetDebuggingReplicator() && IsActive())
	{
		bToolActivated = true;
	}

}
void UGameplayDebuggingControllerComponent::OnActivationKeyPressed()
{
	if (GetDebuggingReplicator() && PlayerOwner.IsValid())
	{
		if (!bToolActivated)
		{
			Activate();
			SetComponentTickEnabled(true);

			BindAIDebugViewKeys();
			GetDebuggingReplicator()->EnableDraw(true);
			GetDebuggingReplicator()->ServerReplicateMessage(NULL, EDebugComponentMessage::ActivateReplication, EAIDebugDrawDataView::Empty);
		}

		ControlKeyPressedTime = GetWorld()->GetTimeSeconds();
		EnableTargetSelection(true);
	}
}
void UGameplayDebuggingControllerComponent::OpenDebugTool()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	if (GetDebuggingReplicator() && IsActive())
	{
		bToolActivated = true;
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::OnActivationKeyReleased()
{
	const float KeyPressedTime = GetWorld()->GetTimeSeconds() - ControlKeyPressedTime;

	EnableTargetSelection(false);
	if (GetDebuggingReplicator() && bToolActivated)
	{
		if (KeyPressedTime < KeyPressActivationTime)
		{
			CloseDebugTool();
		}
		else
		{
			APawn* TargetPawn = GetDebuggingReplicator()->GetDebugComponent() ? Cast<APawn>(GetDebuggingReplicator()->GetDebugComponent()->GetSelectedActor()) : NULL;
			if (TargetPawn != NULL)
			{
				FBehaviorTreeDelegates::OnDebugLocked.Broadcast(TargetPawn);
			}
		}
	}
}
void UGameplayDebuggingControllerComponent::UpdateNavMeshTimer()
{
	const APawn* PlayerPawn = PlayerOwner.IsValid() ? PlayerOwner->GetPawn() : NULL;
	UGameplayDebuggingComponent* DebuggingComponent = GetDebuggingReplicator() ? GetDebuggingReplicator()->GetDebugComponent() : NULL;
	if (DebuggingComponent)
	{
		const AActor* SelectedActor = DebuggingComponent->GetSelectedActor();
		const APawn* SelectedActorAsPawn = Cast<APawn>(SelectedActor);

		const FVector AdditionalTargetLoc =
			SelectedActorAsPawn ? SelectedActorAsPawn->GetNavAgentLocation() :
			SelectedActor ? SelectedActor->GetActorLocation() :
			PlayerPawn ? PlayerPawn->GetNavAgentLocation() :
			FVector::ZeroVector;

		if (AdditionalTargetLoc != FVector::ZeroVector)
		{
			DebuggingComponent->ServerCollectNavmeshData(AdditionalTargetLoc);
		}
	}
}
void UGameplayDebuggingControllerComponent::ToggleAIDebugView_SetView0()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	APawn* Pawn = PlayerOwner.IsValid() ? PlayerOwner->GetPawn() : NULL;

	if (PlayerOwner.IsValid() && Pawn && bToolActivated && GetDebuggingReplicator())
	{
		if (UGameplayDebuggingComponent* OwnerComp = GetDebuggingReplicator() ? GetDebuggingReplicator()->GetDebugComponent() : NULL)
		{
			GameplayDebuggerSettings(GetDebuggingReplicator()).DebuggerShowFlags ^= 1 << EAIDebugDrawDataView::NavMesh;

			if (IsViewActive(EAIDebugDrawDataView::NavMesh))
			{
				GetWorld()->GetTimerManager().SetTimer(TimerHandle_UpdateNavMeshTimer, this, &UGameplayDebuggingControllerComponent::UpdateNavMeshTimer, 5.0f, true);
				UpdateNavMeshTimer();

				GetDebuggingReplicator()->ServerReplicateMessage(Pawn, EDebugComponentMessage::ActivateDataView, EAIDebugDrawDataView::NavMesh);
				OwnerComp->MarkRenderStateDirty();
			}
			else
			{
				GetWorld()->GetTimerManager().ClearTimer(TimerHandle_UpdateNavMeshTimer);

				GetDebuggingReplicator()->ServerReplicateMessage(Pawn, EDebugComponentMessage::DeactivateDataView, EAIDebugDrawDataView::NavMesh);
				OwnerComp->ServerDiscardNavmeshData();
				OwnerComp->MarkRenderStateDirty();
			}
		}
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::BeginDestroy()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	AHUD* GameHUD = PlayerOwner.IsValid() ? PlayerOwner->GetHUD() : nullptr;
	if (GameHUD)
	{
		GameHUD->bShowHUD = true;
	}

	if (PlayerOwner.IsValid() && PlayerOwner->InputComponent && !PlayerOwner->IsPendingKill())
	{
		for (int32 Index = PlayerOwner->InputComponent->KeyBindings.Num() - 1; Index >= 0; --Index)
		{
			const FInputKeyBinding& KeyBind = PlayerOwner->InputComponent->KeyBindings[Index];
			if (KeyBind.KeyDelegate.IsBoundToObject(this))
			{
				PlayerOwner->InputComponent->KeyBindings.RemoveAtSwap(Index);
			}
		}
	}

	if (GetDebuggingReplicator() && PlayerOwner.IsValid() && !PlayerOwner->IsPendingKill())
	{
		APawn* Pawn = PlayerOwner->GetPawnOrSpectator();
		if (Pawn && !Pawn->IsPendingKill())
		{
			GetDebuggingReplicator()->ServerReplicateMessage(Pawn, EDebugComponentMessage::DeactivateReplilcation, 0);
		}

		for (uint32 Index = 0; Index < EAIDebugDrawDataView::MAX; ++Index)
		{
			GetDebuggingReplicator()->ServerReplicateMessage(DebugAITargetActor, EDebugComponentMessage::DeactivateDataView, (EAIDebugDrawDataView::Type)Index);
		}
		GetDebuggingReplicator()->ServerReplicateMessage(DebugAITargetActor, EDebugComponentMessage::DisableExtendedView);
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)

	Super::BeginDestroy();
}
void UGameplayDebuggingControllerComponent::OnActivationKeyReleased()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	const float KeyPressedTime = GetWorld()->GetTimeSeconds() - ControlKeyPressedTime;

	EnableTargetSelection(false);
	if (GetDebuggingReplicator() && bToolActivated)
	{
		if (KeyPressedTime < KeyPressActivationTime && DebugCameraController.IsValid() == false)
		{
			CloseDebugTool();
		}
		else
		{
			APawn* TargetPawn = GetDebuggingReplicator()->GetDebugComponent() ? Cast<APawn>(GetDebuggingReplicator()->GetDebugComponent()->GetSelectedActor()) : nullptr;
			if (TargetPawn != nullptr)
			{
				FBehaviorTreeDelegates::OnDebugLocked.Broadcast(TargetPawn);
			}
		}
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::UpdateNavMeshTimer()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	const APawn* PlayerPawn = PlayerOwner.IsValid() ? PlayerOwner->GetPawnOrSpectator() : nullptr;
	UGameplayDebuggingComponent* DebuggingComponent = GetDebuggingReplicator() ? GetDebuggingReplicator()->GetDebugComponent() : nullptr;
	if (DebuggingComponent)
	{
		const AActor* SelectedActor = DebuggingComponent->GetSelectedActor();
		const APawn* SelectedActorAsPawn = Cast<APawn>(SelectedActor);

		const FVector AdditionalTargetLoc =
			SelectedActorAsPawn ? SelectedActorAsPawn->GetNavAgentLocation() :
			SelectedActor ? SelectedActor->GetActorLocation() :
			PlayerPawn ? PlayerPawn->GetNavAgentLocation() :
			FVector::ZeroVector;

		if (AdditionalTargetLoc != FVector::ZeroVector)
		{
			DebuggingComponent->ServerCollectNavmeshData(AdditionalTargetLoc);
		}
	}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void AGameplayDebuggingHUDComponent::PrintAllData()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)

	// Allow child hud components to position the displayed info
	DefaultContext = FPrintContext(GEngine->GetSmallFont(), Canvas, DebugInfoStartX, DebugInfoStartY);
	DefaultContext.FontRenderInfo.bEnableShadow = true;

	if (DefaultContext.Canvas != NULL)
	{
		float XL, YL;
		const FString ToolName = FString::Printf(TEXT("Gameplay Debug Tool [Timestamp: %05.03f]"), GetWorld()->TimeSeconds);
		CalulateStringSize(DefaultContext, DefaultContext.Font, ToolName, XL, YL);
		PrintString(DefaultContext, FColorList::White, ToolName, DefaultContext.Canvas->ClipX / 2.0f - XL / 2.0f, 0);
	}

	const float MenuX = DefaultContext.CursorX;
	const float MenuY = DefaultContext.CursorY;

	UGameplayDebuggingComponent* DebugComponent = NULL;
	if (GetDebuggingReplicator())
	{
		DebugComponent = GetDebuggingReplicator()->GetDebugComponent();
	}
				
	if (DebugComponent)
	{
		APlayerController* const MyPC = Cast<APlayerController>(PlayerOwner);
		DrawDebugComponentData(MyPC, DebugComponent);
	}

	if (DefaultContext.Canvas && DefaultContext.Canvas->SceneView && DefaultContext.Canvas->SceneView->Family && DefaultContext.Canvas->SceneView->Family->EngineShowFlags.Game)
	{
		DrawMenu(MenuX, MenuY, DebugComponent);
	}
#endif
}
void UGameplayDebuggingControllerComponent::CycleDetailsView()
{
    GetDebuggingReplicator()->OnCycleDetailsView.Broadcast();
}
uint32 UGameplayDebuggingControllerComponent::GetActiveViews()
{
	return GameplayDebuggerSettings(GetDebuggingReplicator()).DebuggerShowFlags;
}
bool UGameplayDebuggingControllerComponent::IsViewActive(EAIDebugDrawDataView::Type View) const
{
	return GameplayDebuggerSettings(GetDebuggingReplicator()).CheckFlag(View);
}
void AGameplayDebuggingHUDComponent::DrawDebugComponentData(APlayerController* MyPC, class UGameplayDebuggingComponent *DebugComponent)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	AActor* SelectedActor = DebugComponent->GetSelectedActor();
	const bool bDrawFullData = GetDebuggingReplicator()->GetSelectedActorToDebug() == SelectedActor;
	const FVector ScreenLoc = SelectedActor ? ProjectLocation(DefaultContext, SelectedActor->GetActorLocation() + FVector(0.f, 0.f, SelectedActor->GetSimpleCollisionHalfHeight())) : FVector::ZeroVector;

	OverHeadContext = FPrintContext(GEngine->GetSmallFont(), Canvas, ScreenLoc.X, ScreenLoc.Y);
	//DefaultContext.CursorY += 20;
	BlackboardFinishY = 0.0f;

	FGameplayDebuggerSettings DebuggerSettings = GameplayDebuggerSettings(GetDebuggingReplicator());
	bool bForceOverhead = false;
#if !WITH_EDITOR
	bForceOverhead = bDrawFullData;
#endif

	if (DebuggerSettings.CheckFlag(EAIDebugDrawDataView::OverHead) || bForceOverhead)
	{
		DrawOverHeadInformation(MyPC, DebugComponent);
	}

	if (DebuggerSettings.CheckFlag(EAIDebugDrawDataView::NavMesh))
	{
		DrawNavMeshSnapshot(MyPC, DebugComponent);
	}

	if (SelectedActor && bDrawFullData)
	{
		if (DebuggerSettings.CheckFlag(EAIDebugDrawDataView::Basic) /*|| EngineShowFlags.DebugAI*/)
		{
			DrawBasicData(MyPC, DebugComponent);
		}

		if (DebuggerSettings.CheckFlag(EAIDebugDrawDataView::BehaviorTree))
		{
			DrawBehaviorTreeData(MyPC, DebugComponent);
		}

		if (DebuggerSettings.CheckFlag(EAIDebugDrawDataView::EQS))
		{
			bool bEnabledEnvironmentQueryEd = true;
			if (GConfig)
			{
				GConfig->GetBool(TEXT("EnvironmentQueryEd"), TEXT("EnableEnvironmentQueryEd"), bEnabledEnvironmentQueryEd, GEngineIni);
			}
			if (bEnabledEnvironmentQueryEd)
			{
				DrawEQSData(MyPC, DebugComponent);
			}
		}

		if (DebuggerSettings.CheckFlag(EAIDebugDrawDataView::Perception) /*|| EngineShowFlags.DebugAI*/)
		{
			DrawPerception(MyPC, DebugComponent);
		}
	}

	DrawGameSpecificView(MyPC, DebugComponent);
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void AGameplayDebuggingHUDComponent::DrawEQSData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && WITH_EQS
	PrintString(DefaultContext, TEXT("\n{green}EQS {white}[Use + key to switch query]\n"));

	if (DebugComponent->EQSLocalData.Num() == 0)
	{
		return;
	}

	const int32 EQSIndex = DebugComponent->EQSLocalData.Num() > 0 ? FMath::Clamp(DebugComponent->CurrentEQSIndex, 0, DebugComponent->EQSLocalData.Num() - 1) : INDEX_NONE;
	if (!DebugComponent->EQSLocalData.IsValidIndex(EQSIndex))
	{
		return;
	}

	{
		int32 Index = 0;
		PrintString(DefaultContext, TEXT("{white}Queries: "));
		for (auto CurrentQuery : DebugComponent->EQSLocalData)
		{
			if (EQSIndex == Index)
			{
				PrintString(DefaultContext, FString::Printf(TEXT("{green}%s, "), *CurrentQuery.Name));
			}
			else
			{
				PrintString(DefaultContext, FString::Printf(TEXT("{yellow}%s, "), *CurrentQuery.Name));
			}
			Index++;
		}
		PrintString(DefaultContext, TEXT("\n"));
	}

	auto& CurrentLocalData = DebugComponent->EQSLocalData[EQSIndex];

	/** find and draw item selection */
	int32 BestItemIndex = INDEX_NONE;
	{
		APlayerController* const MyPC = Cast<APlayerController>(PlayerOwner);
		FVector CamLocation;
		FVector FireDir;
		if (!MyPC->GetSpectatorPawn())
		{
			FRotator CamRotation;
			MyPC->GetPlayerViewPoint(CamLocation, CamRotation);
			FireDir = CamRotation.Vector();
		}
		else
		{
			FireDir = DefaultContext.Canvas->SceneView->GetViewDirection();
			CamLocation = DefaultContext.Canvas->SceneView->ViewMatrices.ViewOrigin;
		}

		float bestAim = 0;
		for (int32 Index = 0; Index < CurrentLocalData.RenderDebugHelpers.Num(); ++Index)
		{
			auto& CurrentItem = CurrentLocalData.RenderDebugHelpers[Index];

			const FVector AimDir = CurrentItem.Location - CamLocation;
			float FireDist = AimDir.SizeSquared();

			FireDist = FMath::Sqrt(FireDist);
			float newAim = FireDir | AimDir;
			newAim = newAim / FireDist;
			if (newAim > bestAim)
			{
				BestItemIndex = Index;
				bestAim = newAim;
			}
		}

		if (BestItemIndex != INDEX_NONE)
		{
			DrawDebugSphere(World, CurrentLocalData.RenderDebugHelpers[BestItemIndex].Location, CurrentLocalData.RenderDebugHelpers[BestItemIndex].Radius, 8, FColor::Red, false);
			int32 FailedTestIndex = CurrentLocalData.RenderDebugHelpers[BestItemIndex].FailedTestIndex;
			if (FailedTestIndex != INDEX_NONE)
			{
				PrintString(DefaultContext, FString::Printf(TEXT("{red}Selected item failed with test %d: {yellow}%s {LightBlue}(%s)\n")
					, FailedTestIndex
					, *CurrentLocalData.Tests[FailedTestIndex].ShortName
					, *CurrentLocalData.Tests[FailedTestIndex].Detailed
					));
				PrintString(DefaultContext, FString::Printf(TEXT("{white}'%s' with score %3.3f\n\n"), *CurrentLocalData.RenderDebugHelpers[BestItemIndex].AdditionalInformation, CurrentLocalData.RenderDebugHelpers[BestItemIndex].FailedScore));
			}
		}
	}

	PrintString(DefaultContext, FString::Printf(TEXT("{white}Timestamp: {yellow}%.3f (%.2fs ago)\n")
		, CurrentLocalData.Timestamp, PC->GetWorld()->GetTimeSeconds() - CurrentLocalData.Timestamp
		));
	PrintString(DefaultContext, FString::Printf(TEXT("{white}Query ID: {yellow}%d\n")
		, CurrentLocalData.Id
		));

	PrintString(DefaultContext, FString::Printf(TEXT("{white}Query contains %d options: "), CurrentLocalData.Options.Num()));
	for (int32 OptionIndex = 0; OptionIndex < CurrentLocalData.Options.Num(); ++OptionIndex)
	{
		if (OptionIndex == CurrentLocalData.UsedOption)
		{
			PrintString(DefaultContext, FString::Printf(TEXT("{green}%s, "), *CurrentLocalData.Options[OptionIndex]));
		}
		else
		{
			PrintString(DefaultContext, FString::Printf(TEXT("{yellow}%s, "), *CurrentLocalData.Options[OptionIndex]));
		}
	}
	PrintString(DefaultContext, TEXT("\n"));

	const float RowHeight = 20.0f;
	const int32 NumTests = CurrentLocalData.Tests.Num();
	if (CurrentLocalData.NumValidItems > 0 && GetDebuggingReplicator()->EnableEQSOnHUD )
	{
		// draw test weights for best X items
		const int32 NumItems = CurrentLocalData.Items.Num();

		FCanvasTileItem TileItem(FVector2D(0, 0), GWhiteTexture, FVector2D(Canvas->SizeX, RowHeight), FLinearColor::Black);
		FLinearColor ColorOdd(0, 0, 0, 0.6f);
		FLinearColor ColorEven(0, 0, 0.4f, 0.4f);
		TileItem.BlendMode = SE_BLEND_Translucent;

		// table header		
		{
			DefaultContext.CursorY += RowHeight;
			const float HeaderY = DefaultContext.CursorY + 3.0f;
			TileItem.SetColor(ColorOdd);
			DrawItem(DefaultContext, TileItem, 0, DefaultContext.CursorY);

			float HeaderX = DefaultContext.CursorX;
			PrintString(DefaultContext, FColor::Yellow, FString::Printf(TEXT("Num items: %d"), CurrentLocalData.NumValidItems), HeaderX, HeaderY);
			HeaderX += ItemDescriptionWidth;

			PrintString(DefaultContext, FColor::White, TEXT("Score"), HeaderX, HeaderY);
			HeaderX += ItemScoreWidth;

			for (int32 TestIdx = 0; TestIdx < NumTests; TestIdx++)
			{
				PrintString(DefaultContext, FColor::White, FString::Printf(TEXT("Test %d"), TestIdx), HeaderX, HeaderY);
				HeaderX += TestScoreWidth;
			}

			DefaultContext.CursorY += RowHeight;
		}

		// valid items
		for (int32 Idx = 0; Idx < NumItems; Idx++)
		{
			TileItem.SetColor((Idx % 2) ? ColorOdd : ColorEven);
			DrawItem(DefaultContext, TileItem, 0, DefaultContext.CursorY);

			DrawEQSItemDetails(Idx, DebugComponent);
			DefaultContext.CursorY += RowHeight;
		}
		DefaultContext.CursorY += RowHeight;
	}

	// test description
	PrintString(DefaultContext, TEXT("All tests from used option:\n"));
	for (int32 TestIdx = 0; TestIdx < NumTests; TestIdx++)
	{
		FString TestDesc = FString::Printf(TEXT("{white}Test %d = {yellow}%s {LightBlue}(%s)\n"), TestIdx,
			*CurrentLocalData.Tests[TestIdx].ShortName,
			*CurrentLocalData.Tests[TestIdx].Detailed);

		PrintString(DefaultContext, TestDesc);
	}

#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingControllerComponent::EnableTargetSelection(bool bEnable)
{
	GetDebuggingReplicator()->ServerEnableTargetSelection(bEnable, PlayerOwner.Get());
}
void UGameplayDebuggingControllerComponent::EnableTargetSelection(bool bEnable)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	GetDebuggingReplicator()->ClientEnableTargetSelection(bEnable, PlayerOwner.Get());
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void AGameplayDebuggingHUDComponent::DrawMenu(const float X, const float Y, class UGameplayDebuggingComponent* DebugComponent)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	const float OldX = DefaultContext.CursorX;
	const float OldY = DefaultContext.CursorY;

	UGameplayDebuggingControllerComponent*  GDC = GetDebuggingReplicator()->FindComponentByClass<UGameplayDebuggingControllerComponent>();
	if (DefaultContext.Canvas != NULL)
	{
		TArray<FDebugCategoryView> Categories;
		GetKeyboardDesc(Categories);

		UFont* OldFont = DefaultContext.Font;
		DefaultContext.Font = GEngine->GetMediumFont();

		TArray<float> CategoriesWidth;
		CategoriesWidth.AddZeroed(Categories.Num());
		float TotalWidth = 0.0f, MaxHeight = 0.0f;

		FString ActivationKeyDisplayName = TEXT("'");
		FString ActivationKeyName = TEXT("Apostrophe");

		APlayerController* const MyPC = Cast<APlayerController>(PlayerOwner);
		if (GDC)
		{
			ActivationKeyDisplayName = GDC->GetActivationKey().Key.GetDisplayName().ToString();
			ActivationKeyName = GDC->GetActivationKey().Key.GetFName().ToString();
		}

		const FString KeyDesc = ActivationKeyName != ActivationKeyDisplayName ? FString::Printf(TEXT("(%s key)"), *ActivationKeyName) : TEXT("");
		FString HeaderDesc = FString::Printf(TEXT("Tap %s %s to close, use Numpad numbers to toggle categories"), *ActivationKeyDisplayName, *KeyDesc);

		float HeaderWidth = 0.0f;
		CalulateStringSize(DefaultContext, DefaultContext.Font, HeaderDesc, HeaderWidth, MaxHeight);

		for (int32 i = 0; i < Categories.Num(); i++)
		{
			Categories[i].Desc = FString::Printf(TEXT("%d:%s "), i, *Categories[i].Desc);

			float StrHeight = 0.0f;
			CalulateStringSize(DefaultContext, DefaultContext.Font, Categories[i].Desc, CategoriesWidth[i], StrHeight);
			TotalWidth += CategoriesWidth[i];
			MaxHeight = FMath::Max(MaxHeight, StrHeight);
		}

		{
			static FString KeyShortcut = GDC->DebugCameraBind.GetInputText().ToString();
			const int32 DebugCameraIndex = Categories.Add(FDebugCategoryView());
			CategoriesWidth.AddZeroed(1);
			Categories[DebugCameraIndex].Desc = FString::Printf(TEXT(" %s[%s]: %s  "), GDC && GDC->GetDebugCameraController().IsValid() ? TEXT("{Green}") : TEXT("{White}"), *KeyShortcut, TEXT("Debug Camera"));
			float StrHeight = 0.0f;
			CalulateStringSize(DefaultContext, DefaultContext.Font, Categories[DebugCameraIndex].Desc, CategoriesWidth[DebugCameraIndex], StrHeight);
			TotalWidth += CategoriesWidth[DebugCameraIndex];
			MaxHeight = FMath::Max(MaxHeight, StrHeight);
		}
		{
			static FString KeyShortcut = GDC->OnScreenDebugMessagesBind.GetInputText().ToString();
			const int32 DebugCameraIndex = Categories.Add(FDebugCategoryView());
			CategoriesWidth.AddZeroed(1);
			Categories[DebugCameraIndex].Desc = FString::Printf(TEXT(" %s[%s]: %s  "), GEngine && GEngine->bEnableOnScreenDebugMessages ? TEXT("{Green}") : TEXT("{White}"), *KeyShortcut, TEXT("DebugMessages"));
			float StrHeight = 0.0f;
			CalulateStringSize(DefaultContext, DefaultContext.Font, Categories[DebugCameraIndex].Desc, CategoriesWidth[DebugCameraIndex], StrHeight);
			TotalWidth += CategoriesWidth[DebugCameraIndex];
			MaxHeight = FMath::Max(MaxHeight, StrHeight);
		}
		{
			static FString KeyShortcut = GDC->GameHUDBind.GetInputText().ToString();
			const AHUD* GameHUD = MyPC ? MyPC->GetHUD() : NULL;
			const int32 DebugCameraIndex = Categories.Add(FDebugCategoryView());
			CategoriesWidth.AddZeroed(1);
			Categories[DebugCameraIndex].Desc = FString::Printf(TEXT(" %s[%s]: %s  "), GameHUD && GameHUD->bShowHUD ? TEXT("{Green}") : TEXT("{White}"), *KeyShortcut, TEXT("GameHUD"));
			float StrHeight = 0.0f;
			CalulateStringSize(DefaultContext, DefaultContext.Font, Categories[DebugCameraIndex].Desc, CategoriesWidth[DebugCameraIndex], StrHeight);
			TotalWidth += CategoriesWidth[DebugCameraIndex];
			MaxHeight = FMath::Max(MaxHeight, StrHeight);
		}


		TotalWidth = FMath::Max(TotalWidth, HeaderWidth);

		FCanvasTileItem TileItem(FVector2D(10, 10), GWhiteTexture, FVector2D(TotalWidth + 20, MaxHeight + 20), FColor(0, 0, 0, 20));
		TileItem.BlendMode = SE_BLEND_Translucent;
		DrawItem(DefaultContext, TileItem, MenuStartX, MenuStartY);

		PrintString(DefaultContext, FColorList::LightBlue, HeaderDesc, MenuStartX + 2.f, MenuStartY + 2.f);

		float XPos = MenuStartX + 20.f;
		for (int32 i = 0; i < Categories.Num(); i++)
		{
			const bool bIsActive = GameplayDebuggerSettings(GetDebuggingReplicator()).CheckFlag(Categories[i].View) ? true : false;
			const bool bIsDisabled = Categories[i].View == EAIDebugDrawDataView::NavMesh ? false : (DebugComponent && DebugComponent->GetSelectedActor() ? false: true);

			PrintString(DefaultContext, bIsDisabled ? (bIsActive ? FColorList::DarkGreen  : FColorList::LightGrey) : (bIsActive ? FColorList::Green : FColorList::White), Categories[i].Desc, XPos, MenuStartY + MaxHeight + 2.f);
			XPos += CategoriesWidth[i];
		}
		DefaultContext.Font = OldFont;
	}

	if ((!DebugComponent || !DebugComponent->GetSelectedActor()) && GetWorld()->GetNetMode() == NM_Client)
	{
		PrintString(DefaultContext, "\n{red}No Pawn selected - waiting for data to replicate from server. {green}Press and hold ' to select Pawn \n");
	}

	if (GDC && GDC->GetDebugCameraController().IsValid())
	{
		ADebugCameraController* DebugCamController = GDC->GetDebugCameraController().Get();
		if (DebugCamController != NULL)
		{
			FVector const CamLoc = DebugCamController->PlayerCameraManager->GetCameraLocation();
			FRotator const CamRot = DebugCamController->PlayerCameraManager->GetCameraRotation();

			FString HitString;
			FCollisionQueryParams TraceParams(NAME_None, true, this);
			FHitResult Hit;
			bool bHit = GetWorld()->LineTraceSingleByChannel(Hit, CamLoc, CamRot.Vector() * 100000.f + CamLoc, ECC_Pawn, TraceParams);
			if (bHit && Hit.GetActor() != nullptr)
			{
				HitString = FString::Printf(TEXT("{white}Under cursor: {yellow}'%s'"), *Hit.GetActor()->GetName());
				DrawDebugLine(GetWorld(), Hit.Location, Hit.Location + Hit.Normal*30.f, FColor::White);
			}
			else
			{
				HitString = FString::Printf(TEXT("Not actor under cursor"));
			}

			PrintString(DefaultContext, FColor::White, HitString, MenuStartX, MenuStartY + 40);
		}
	}


	DefaultContext.CursorX = OldX;
	DefaultContext.CursorY = OldY;
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}