FVisualLogEntry* FVisualLogger::GetEntryToWrite(const class UObject* Object, float TimeStamp, ECreateIfNeeded ShouldCreate)
{
	FVisualLogEntry* CurrentEntry = nullptr;
	UObject * LogOwner = FVisualLogger::FindRedirection(Object);
	if (LogOwner == nullptr || (LogOwner != Object && CurrentEntryPerObject.Contains(LogOwner) == false))
	{
		return nullptr;
	}

	bool InitializeNewEntry = false;

	TWeakObjectPtr<UWorld> World = GetWorld(Object);

	if (CurrentEntryPerObject.Contains(LogOwner))
	{
		CurrentEntry = &CurrentEntryPerObject[LogOwner];
		InitializeNewEntry = TimeStamp > CurrentEntry->TimeStamp && ShouldCreate == ECreateIfNeeded::Create;
		if (World.IsValid())
		{
			World->GetTimerManager().ClearTimer(VisualLoggerCleanupTimerHandle);
			for (auto& CurrentPair : CurrentEntryPerObject)
			{
				FVisualLogEntry* Entry = &CurrentPair.Value;
				if (Entry->TimeStamp >= 0 && Entry->TimeStamp < TimeStamp)
				{
					for (auto* Device : OutputDevices)
					{
						Device->Serialize(CurrentPair.Key, ObjectToNameMap[CurrentPair.Key], ObjectToClassNameMap[CurrentPair.Key], *Entry);
					}
					Entry->Reset();
				}
			}
		}
	}

	if (!CurrentEntry)
	{
		// It's first and only one usage of LogOwner as regular object to get names. We assume once that LogOwner is correct here and only here.
		CurrentEntry = &CurrentEntryPerObject.Add(LogOwner);
		ObjectToNameMap.Add(LogOwner, LogOwner->GetFName());
		ObjectToClassNameMap.Add(LogOwner, *(LogOwner->GetClass()->GetName()));
		ObjectToPointerMap.Add(LogOwner, LogOwner);
		InitializeNewEntry = true;
	}

	if (InitializeNewEntry)
	{
		CurrentEntry->Reset();
		CurrentEntry->TimeStamp = TimeStamp;

		if (RedirectionMap.Contains(LogOwner))
		{
			if (ObjectToPointerMap.Contains(LogOwner) && ObjectToPointerMap[LogOwner].IsValid())
			{
				const class AActor* LogOwnerAsActor = Cast<class AActor>(LogOwner);
				if (LogOwnerAsActor)
				{
					LogOwnerAsActor->GrabDebugSnapshot(CurrentEntry);
				}
			}
			for (auto Child : RedirectionMap[LogOwner])
			{
				if (Child.IsValid())
				{
					const class AActor* ChildAsActor = Cast<class AActor>(Child.Get());
					if (ChildAsActor)
					{
						ChildAsActor->GrabDebugSnapshot(CurrentEntry);
					}
				}
			}
		}
		else
		{
			const class AActor* ObjectAsActor = Cast<class AActor>(Object);
			if (ObjectAsActor)
			{
				CurrentEntry->Location = ObjectAsActor->GetActorLocation();
				ObjectAsActor->GrabDebugSnapshot(CurrentEntry);
			}
		}
	}

	if (World.IsValid())
	{
		//set next tick timer to flush obsolete/old entries
		World->GetTimerManager().SetTimer(VisualLoggerCleanupTimerHandle, FTimerDelegate::CreateLambda(
			[this, World](){
			for (auto& CurrentPair : CurrentEntryPerObject)
			{
				FVisualLogEntry* Entry = &CurrentPair.Value;
				if (Entry->TimeStamp >= 0 && (!World.IsValid() || Entry->TimeStamp < World->GetTimeSeconds())) // CurrentEntry->TimeStamp == -1 means it's not initialized entry information
				{
					for (auto* Device : OutputDevices)
					{
						Device->Serialize(CurrentPair.Key, ObjectToNameMap[CurrentPair.Key], ObjectToClassNameMap[CurrentPair.Key], *Entry);
					}
					Entry->Reset();
				}
			}
		}
		), 0.1, false);
	}

	return CurrentEntry;
}