// Give specific editor modes a chance to highlight this connection or darken non-interesting connections
void FKismetConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ float& Thickness, /*inout*/ FLinearColor& WireColor, /*inout*/bool& bDrawBubbles, /*inout*/bool& bBidirectional)
{
	// Get the schema and grab the default color from it
	check(OutputPin);
	check(GraphObj);
	const UEdGraphSchema* Schema = GraphObj->GetSchema();

	WireColor = Schema->GetPinTypeColor(OutputPin->PinType);

	const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0;

	// If this is a K2 graph, try to be a little more specific
	const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(Schema);
	if (K2Schema != NULL)
	{
		if (TreatWireAsExecutionPin(InputPin, OutputPin))
		{
			if (CanBuildRoadmap())
			{
				bool bExecuted = false;

				// Run thru the predecessors, and on
				if (FExecPairingMap* PredecessorMap = PredecessorNodes.Find(InputPin->GetOwningNode()))
				{
					if (FTimePair* Times = PredecessorMap->Find(OutputPin->GetOwningNode()))
					{
						bExecuted = true;

						DetermineStyleOfExecWire(/*inout*/ Thickness, /*inout*/ WireColor, /*inout*/ bDrawBubbles, *Times);
					}
				}

				if (!bExecuted)
				{
					// It's not followed, fade it and keep it thin
					WireColor = ReleaseColor;
					Thickness = ReleaseWireThickness;
				}
			}
			else
			{
				// Make exec wires slightly thicker even outside of debug
				Thickness = 3.0f;
			}
		}
		else
		{
			// Array types should draw thicker
			if( (InputPin && InputPin->PinType.bIsArray) || (OutputPin && OutputPin->PinType.bIsArray) )
			{
				Thickness = 3.0f;
			}
		}
	}

	if (bDeemphasizeUnhoveredPins)
	{
		ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Thickness, /*inout*/ WireColor);
	}
}
void FBlueprintProfilerConnectionDrawingPolicy::BuildExecutionRoadmap()
{
	LatestTimeDiscovered = 0.0;

	// Only do highlighting in PIE or SIE
	if (!CanBuildRoadmap())
	{
		return;
	}

	// Grab relevant profiler interfaces
	const FName InstanceName = BlueprintContext->GetActiveInstanceName();
	TSharedPtr<FBlueprintFunctionContext> FunctionContext = BlueprintContext->GetFunctionContextFromGraph(GraphObj);

	if (!FunctionContext.IsValid())
	{
		// No point in proceeding without a valid function context.
		return;
	}

	TArray<double> SequentialNodeTimes;
	TArray<UEdGraphPin*> SequentialExecPinsInGraph;
	TArray<FTracePath> SequentialTracePathsInGraph;
	TArray<TWeakPtr<FScriptExecutionNode>> SequentialProfilerNodesInGraph;
	const FName ScopedFunctionName = FunctionContext->GetFunctionName();
	const FName GraphName = FunctionContext->GetGraphName();

	const TSimpleRingBuffer<FBlueprintExecutionTrace>& TraceHistory = BlueprintContext->GetTraceHistory();
	for (int32 i = 0; i < TraceHistory.Num(); ++i)
	{
		const FBlueprintExecutionTrace& Sample = TraceHistory(i);
		const bool bInstanceMatch = InstanceName == SPDN_Blueprint ? true : (InstanceName == Sample.InstanceName);
		if (bInstanceMatch && GraphName == Sample.GraphName)
		{
			if (Sample.ProfilerNode.IsValid())
			{
				const bool bSamplePinValid = Sample.PinReference.Get() != nullptr;
				// Patch in any missing pins using the script offset.
				UEdGraphPin* Pin = bSamplePinValid ? Sample.PinReference.Get() : const_cast<UEdGraphPin*>(FunctionContext->GetPinFromCodeLocation(Sample.Offset));

				if (Pin && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
				{
					SequentialProfilerNodesInGraph.Add(Sample.ProfilerNode);
					SequentialNodeTimes.Add(Sample.ObservationTime);
					SequentialExecPinsInGraph.Add(Pin);
					SequentialTracePathsInGraph.Add(Sample.TracePath);
				}
			}
		}
	}
	// Run thru and apply bonus time
	const float InvNumNodes = 1.0f / (float)SequentialNodeTimes.Num();
	for (int32 i = 0; i < SequentialProfilerNodesInGraph.Num(); ++i)
	{
		double& ObservationTime = SequentialNodeTimes[i];

		const float PositionRatio = (SequentialNodeTimes.Num() - i) * InvNumNodes;
		const float PositionBonus = FMath::Pow(PositionRatio, TracePositionExponent) * TracePositionBonusPeriod;
		ObservationTime += PositionBonus;

		LatestTimeDiscovered = FMath::Max<double>(LatestTimeDiscovered, ObservationTime);
	}

	UEdGraphPin* OutputPin = nullptr;
	int32 OutputPinIndex = INDEX_NONE;
	for (int32 i = SequentialExecPinsInGraph.Num() - 1; i > 0; --i)
	{
		UEdGraphPin* InputPin = SequentialExecPinsInGraph[i];
		if (InputPin->Direction == EGPD_Output)
		{
			OutputPin = InputPin;
			OutputPinIndex = i;
		}
		else if (OutputPin)
		{
			bool bLinked = false;
			for (auto LinkedPin : OutputPin->LinkedTo)
			{
				if (LinkedPin == InputPin)
				{
					bLinked = true;
					break;
				}
			}
			if (bLinked)
			{
				TSharedPtr<FScriptExecutionNode> CurExecNode = SequentialProfilerNodesInGraph[OutputPinIndex].Pin();
				TSharedPtr<FScriptExecutionNode> NextExecNode = SequentialProfilerNodesInGraph[i].Pin();

				if (CurExecNode.IsValid() && NextExecNode.IsValid() && CurExecNode != NextExecNode)
				{
					double NextNodeTime = SequentialNodeTimes[i];
					UEdGraphNode* NextNode = const_cast<UEdGraphNode*>(NextExecNode->GetTypedObservedObject<UEdGraphNode>());
					FExecPairingMap& ExecPaths			= PredecessorPins.FindOrAdd(NextNode);
					FTimePair& ExecTiming				= ExecPaths.FindOrAdd(OutputPin);
					FProfilerPairingMap& ProfilerPaths	= ProfilerPins.FindOrAdd(NextNode);
					FProfilerPair& ProfilerData			= ProfilerPaths.FindOrAdd(OutputPin);
					// make sure that if we've already visited this exec-pin (like 
					// in a for-loop or something), that we're replacing it with a 
					// more recent execution time
					//
					// @TODO I don't see when this wouldn't be the case
					if (ExecTiming.ThisExecTime < NextNodeTime)
					{
						double CurNodeTime = SequentialNodeTimes[OutputPinIndex];
						ExecTiming.ThisExecTime = NextNodeTime;
						ExecTiming.PredExecTime = CurNodeTime;
						const bool bExecPin = CurExecNode->HasFlags(EScriptExecutionNodeFlags::ExecPin);
						const FTracePath& CurTracePath = bExecPin ? SequentialTracePathsInGraph[i] : SequentialTracePathsInGraph[OutputPinIndex];
						const FTracePath& NextTracePath = SequentialTracePathsInGraph[i];
						TSharedPtr<FScriptPerfData> CurPerfData = CurExecNode->GetPerfDataByInstanceAndTracePath(InstanceName, CurTracePath);
						TSharedPtr<FScriptPerfData> NextPerfData = NextExecNode->GetPerfDataByInstanceAndTracePath(InstanceName, NextTracePath);
						ProfilerData.bPureNode = CurExecNode->IsPureNode();
						switch(WireHeatMode)
						{
							case EBlueprintProfilerHeatMapDisplayMode::Average:
							case EBlueprintProfilerHeatMapDisplayMode::PinToPin:
							{
								ProfilerData.PredPerfData = CurPerfData->GetAverageHeatLevel();
								ProfilerData.ThisPerfData = NextPerfData->GetAverageHeatLevel();
								break;
							}
							case EBlueprintProfilerHeatMapDisplayMode::Inclusive:
							{
								ProfilerData.PredPerfData = CurPerfData->GetInclusiveHeatLevel();
								ProfilerData.ThisPerfData = NextPerfData->GetInclusiveHeatLevel();
								break;
							}
							case EBlueprintProfilerHeatMapDisplayMode::MaxTiming:
							{
								ProfilerData.PredPerfData = CurPerfData->GetMaxTimeHeatLevel();
								ProfilerData.ThisPerfData = NextPerfData->GetMaxTimeHeatLevel();
								break;
							}
							case EBlueprintProfilerHeatMapDisplayMode::Total:
							{
								ProfilerData.PredPerfData = CurPerfData->GetTotalHeatLevel();
								ProfilerData.ThisPerfData = NextPerfData->GetTotalHeatLevel();
								break;
							}
							case EBlueprintProfilerHeatMapDisplayMode::HottestPath:
							{
								ProfilerData.PredPerfData = CurPerfData->GetHottestPathHeatLevel();
								ProfilerData.ThisPerfData = NextPerfData->GetHottestPathHeatLevel();
								break;
							}
						}
					}
				}
			}
		}
	}
	// Fade only when free-running (since we're using FApp::GetCurrentTime(), instead of FPlatformTime::Seconds)
	const double MaxTimeAhead = FMath::Min(FApp::GetCurrentTime() + 2*TracePositionBonusPeriod, LatestTimeDiscovered); //@TODO: Rough clamping; should be exposed as a parameter
	CurrentTime = FMath::Max(FApp::GetCurrentTime(), MaxTimeAhead);
}
void FKismetConnectionDrawingPolicy::BuildExecutionRoadmap()
{
	LatestTimeDiscovered = 0.0;

	// Only do highlighting in PIE or SIE
	if (!CanBuildRoadmap())
	{
		return;
	}

	UBlueprint* TargetBP = FBlueprintEditorUtils::FindBlueprintForGraphChecked(GraphObj);
	UObject* ActiveObject = TargetBP->GetObjectBeingDebugged();
	check(ActiveObject); // Due to CanBuildRoadmap

	// Redirect the target Blueprint when debugging with a macro graph visible
	if (TargetBP->BlueprintType == BPTYPE_MacroLibrary)
	{
		TargetBP = Cast<UBlueprint>(ActiveObject->GetClass()->ClassGeneratedBy);
	}

	TArray<UEdGraphNode*> SequentialNodesInGraph;
	TArray<double> SequentialNodeTimes;
	TArray<UEdGraphPin*> SequentialExecPinsInGraph;

	{
		const TSimpleRingBuffer<FKismetTraceSample>& TraceStack = FKismetDebugUtilities::GetTraceStack();

		UBlueprintGeneratedClass* TargetClass = Cast<UBlueprintGeneratedClass>(TargetBP->GeneratedClass);
		FBlueprintDebugData& DebugData = TargetClass->GetDebugData();

		for (int32 i = 0; i < TraceStack.Num(); ++i)
		{
			const FKismetTraceSample& Sample = TraceStack(i);

			if (UObject* TestObject = Sample.Context.Get())
			{
				if (TestObject == ActiveObject)
				{
					UEdGraphPin* AssociatedPin = DebugData.FindExecPinFromCodeLocation(Sample.Function.Get(), Sample.Offset);

					if (UEdGraphNode* Node = DebugData.FindSourceNodeFromCodeLocation(Sample.Function.Get(), Sample.Offset, /*bAllowImpreciseHit=*/ false))
					{
						if (GraphObj == Node->GetGraph())
						{
							SequentialNodesInGraph.Add(Node);
							SequentialNodeTimes.Add(Sample.ObservationTime);
							SequentialExecPinsInGraph.Add(AssociatedPin);
						}
						else
						{
							// If the top-level source node is a macro instance node
							UK2Node_MacroInstance* MacroInstanceNode = Cast<UK2Node_MacroInstance>(Node);
							if (MacroInstanceNode)
							{
								// Attempt to locate the macro source node through the code mapping
								UEdGraphNode* MacroSourceNode = DebugData.FindMacroSourceNodeFromCodeLocation(Sample.Function.Get(), Sample.Offset);
								if (MacroSourceNode)
								{
									// If the macro source node is located in the current graph context
									if (GraphObj == MacroSourceNode->GetGraph())
									{
										// Add it to the sequential node list
										SequentialNodesInGraph.Add(MacroSourceNode);
										SequentialNodeTimes.Add(Sample.ObservationTime);
										SequentialExecPinsInGraph.Add(AssociatedPin);
									}
									else
									{
										// The macro source node isn't in the current graph context, but we might have a macro instance node that is
										// in the current graph context, so obtain the set of macro instance nodes that are mapped to the code here.
										TArray<UEdGraphNode*> MacroInstanceNodes;
										DebugData.FindMacroInstanceNodesFromCodeLocation(Sample.Function.Get(), Sample.Offset, MacroInstanceNodes);

										// For each macro instance node in the set
										for (auto MacroInstanceNodeIt = MacroInstanceNodes.CreateConstIterator(); MacroInstanceNodeIt; ++MacroInstanceNodeIt)
										{
											// If the macro instance node is located in the current graph context
											MacroInstanceNode = Cast<UK2Node_MacroInstance>(*MacroInstanceNodeIt);
											if (MacroInstanceNode && GraphObj == MacroInstanceNode->GetGraph())
											{
												// Add it to the sequential node list
												SequentialNodesInGraph.Add(MacroInstanceNode);
												SequentialNodeTimes.Add(Sample.ObservationTime);
												SequentialExecPinsInGraph.Add(AssociatedPin);

												// Exit the loop; we're done
												break;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	// Run thru and apply bonus time
	const float InvNumNodes = 1.0f / (float)SequentialNodeTimes.Num();
	for (int32 i = 0; i < SequentialNodesInGraph.Num(); ++i)
	{
		double& ObservationTime = SequentialNodeTimes[i];

		const float PositionRatio = (SequentialNodeTimes.Num() - i) * InvNumNodes;
		const float PositionBonus = FMath::Pow(PositionRatio, TracePositionExponent) * TracePositionBonusPeriod;
		ObservationTime += PositionBonus;

		LatestTimeDiscovered = FMath::Max<double>(LatestTimeDiscovered, ObservationTime);
	}

	UEdGraphPin* LastExecPin = NULL;
	// Record the unique exec-pin to time pairings, keeping only the most recent 
	// times for each pairing... reverse the "SequentialNodes" because right now
	// it is in stack order (with the last executed node first)
	for (int32 i = SequentialNodesInGraph.Num() - 1; i >= 1; --i)
	{
		UEdGraphNode* CurNode  = SequentialNodesInGraph[i];
		UEdGraphNode* NextNode = SequentialNodesInGraph[i-1];

		// keep track of the last exec-pin executed by CurNode (these tracked 
		// pins coincide with "WireTraceSite" op-codes that have been injected 
		// prior to every "goto" statement... this way we have context for which
		// pin executed the jump)
		if (UEdGraphPin* AssociatedPin = SequentialExecPinsInGraph[i])
		{
			LastExecPin = AssociatedPin;
		}
		
		// if this statement is a jump (from one node to another)
		if (CurNode != NextNode)
		{
			// if there was a wire-trace op-code inserted before this jump
			if (LastExecPin != NULL)
			{
				//ensure(LastExecPin->GetOwningNode() == CurNode);
				double NextNodeTime = SequentialNodeTimes[i-1];

				FExecPairingMap& ExecPaths  = PredecessorPins.FindOrAdd(NextNode);
				FTimePair&       ExecTiming = ExecPaths.FindOrAdd(LastExecPin);
				// make sure that if we've already visited this exec-pin (like 
				// in a for-loop or something), that we're replacing it with a 
				// more recent execution time
				//
				// @TODO I don't see when this wouldn't be the case
				if (ExecTiming.ThisExecTime < NextNodeTime)
				{
					double CurNodeTime = SequentialNodeTimes[i];
					ExecTiming.ThisExecTime = NextNodeTime;
					ExecTiming.PredExecTime = CurNodeTime;
				}
			}
			// if the nodes aren't graphically connected how could they be 
			// executed back-to-back? well, this could be a pop back to a 
			// sequence node from the end of one thread of execution, etc.
			else if (AreNodesGraphicallySequential(CurNode, NextNode))
			{
				// only warn when the nodes are directly connected (this is all
				// for execution flow visualization after all)
				UE_LOG(LogConnectionDrawingPolicy, Warning, TEXT("Looks like a wire-trace was not injected before the jump from '%s' to '%s'."), 
					*CurNode->GetNodeTitle(ENodeTitleType::FullTitle).ToString(), *NextNode->GetNodeTitle(ENodeTitleType::FullTitle).ToString());
			}

			// clear the exec-pin (we're moving to a new node and want to find 
			// it's executed out pin)
			LastExecPin = NULL;
		}
		// else, we're only collecting this data for tracing node-to-node
		// executions (so we don't care about this sequence of statements)
	}

	// Fade only when free-running (since we're using FApp::GetCurrentTime(), instead of FPlatformTime::Seconds)
	const double MaxTimeAhead = FMath::Min(FApp::GetCurrentTime() + 2*TracePositionBonusPeriod, LatestTimeDiscovered); //@TODO: Rough clamping; should be exposed as a parameter
	CurrentTime = FMath::Max(FApp::GetCurrentTime(), MaxTimeAhead);
}
void FKismetConnectionDrawingPolicy::BuildExecutionRoadmap()
{
	LatestTimeDiscovered = 0.0;

	// Only do highlighting in PIE or SIE
	if (!CanBuildRoadmap())
	{
		return;
	}

	UBlueprint* TargetBP = FBlueprintEditorUtils::FindBlueprintForGraphChecked(GraphObj);
	UObject* ActiveObject = TargetBP->GetObjectBeingDebugged();
	check(ActiveObject); // Due to CanBuildRoadmap

	// Redirect the target Blueprint when debugging with a macro graph visible
	if (TargetBP->BlueprintType == BPTYPE_MacroLibrary)
	{
		TargetBP = Cast<UBlueprint>(ActiveObject->GetClass()->ClassGeneratedBy);
	}

	TArray<UEdGraphNode*> SequentialNodesInGraph;
	TArray<double> SequentialNodeTimes;

	{
		const TSimpleRingBuffer<FKismetTraceSample>& TraceStack = FKismetDebugUtilities::GetTraceStack();

		UBlueprintGeneratedClass* TargetClass = Cast<UBlueprintGeneratedClass>(TargetBP->GeneratedClass);
		FBlueprintDebugData& DebugData = TargetClass->GetDebugData();

		for (int32 i = 0; i < TraceStack.Num(); ++i)
		{
			const FKismetTraceSample& Sample = TraceStack(i);

			if (UObject* TestObject = Sample.Context.Get())
			{
				if (TestObject == ActiveObject)
				{
					if (UEdGraphNode* Node = DebugData.FindSourceNodeFromCodeLocation(Sample.Function.Get(), Sample.Offset, /*bAllowImpreciseHit=*/ false))
					{
						if (GraphObj == Node->GetGraph())
						{
							SequentialNodesInGraph.Add(Node);
							SequentialNodeTimes.Add(Sample.ObservationTime);
						}
						else
						{
							// If the top-level source node is a macro instance node
							UK2Node_MacroInstance* MacroInstanceNode = Cast<UK2Node_MacroInstance>(Node);
							if (MacroInstanceNode)
							{
								// Attempt to locate the macro source node through the code mapping
								UEdGraphNode* MacroSourceNode = DebugData.FindMacroSourceNodeFromCodeLocation(Sample.Function.Get(), Sample.Offset);
								if (MacroSourceNode)
								{
									// If the macro source node is located in the current graph context
									if (GraphObj == MacroSourceNode->GetGraph())
									{
										// Add it to the sequential node list
										SequentialNodesInGraph.Add(MacroSourceNode);
										SequentialNodeTimes.Add(Sample.ObservationTime);
									}
									else
									{
										// The macro source node isn't in the current graph context, but we might have a macro instance node that is
										// in the current graph context, so obtain the set of macro instance nodes that are mapped to the code here.
										TArray<UEdGraphNode*> MacroInstanceNodes;
										DebugData.FindMacroInstanceNodesFromCodeLocation(Sample.Function.Get(), Sample.Offset, MacroInstanceNodes);

										// For each macro instance node in the set
										for (auto MacroInstanceNodeIt = MacroInstanceNodes.CreateConstIterator(); MacroInstanceNodeIt; ++MacroInstanceNodeIt)
										{
											// If the macro instance node is located in the current graph context
											MacroInstanceNode = Cast<UK2Node_MacroInstance>(*MacroInstanceNodeIt);
											if (MacroInstanceNode && GraphObj == MacroInstanceNode->GetGraph())
											{
												// Add it to the sequential node list
												SequentialNodesInGraph.Add(MacroInstanceNode);
												SequentialNodeTimes.Add(Sample.ObservationTime);

												// Exit the loop; we're done
												break;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	// Run thru and apply bonus time
	const float InvNumNodes = 1.0f / (float)SequentialNodeTimes.Num();
	for (int32 i = 0; i < SequentialNodesInGraph.Num(); ++i)
	{
		double& ObservationTime = SequentialNodeTimes[i];

		const float PositionRatio = (SequentialNodeTimes.Num() - i) * InvNumNodes;
		const float PositionBonus = FMath::Pow(PositionRatio, TracePositionExponent) * TracePositionBonusPeriod;
		ObservationTime += PositionBonus;

		LatestTimeDiscovered = FMath::Max<double>(LatestTimeDiscovered, ObservationTime);
	}

	// Record the unique node->node pairings, keeping only the most recent times for each pairing
	for (int32 i = SequentialNodesInGraph.Num() - 1; i >= 1; --i)
	{
		UEdGraphNode* CurNode = SequentialNodesInGraph[i];
		double CurNodeTime = SequentialNodeTimes[i];
		UEdGraphNode* NextNode = SequentialNodesInGraph[i-1];
		double NextNodeTime = SequentialNodeTimes[i-1];

		FExecPairingMap& Predecessors = PredecessorNodes.FindOrAdd(NextNode);

		// Update the timings if this is a more recent pairing
		FTimePair& Timings = Predecessors.FindOrAdd(CurNode);
		if (Timings.ThisExecTime < NextNodeTime)
		{
			Timings.PredExecTime = CurNodeTime;
			Timings.ThisExecTime = NextNodeTime;
		}
	}

	// Fade only when free-running (since we're using GCurrentTime, instead of FPlatformTime::Seconds)
	const double MaxTimeAhead = FMath::Min(GCurrentTime + 2*TracePositionBonusPeriod, LatestTimeDiscovered); //@TODO: Rough clamping; should be exposed as a parameter
	CurrentTime = FMath::Max(GCurrentTime, MaxTimeAhead);
}
// Give specific editor modes a chance to highlight this connection or darken non-interesting connections
void FKismetConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params)
{
	Params.AssociatedPin1 = OutputPin;
	Params.AssociatedPin2 = InputPin;

	// Get the schema and grab the default color from it
	check(OutputPin);
	check(GraphObj);
	const UEdGraphSchema* Schema = GraphObj->GetSchema();

	Params.WireColor = Schema->GetPinTypeColor(OutputPin->PinType);

	UEdGraphNode* OutputNode = OutputPin->GetOwningNode();
	UEdGraphNode* InputNode = (InputPin != nullptr) ? InputPin->GetOwningNode() : nullptr;

	const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0;

	// If this is a K2 graph, try to be a little more specific
	const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(Schema);
	if (K2Schema != NULL)
	{
		// If the output or input connect to a knot that is going backwards, we will flip the direction on values going into them
		{
			if (UK2Node_Knot* OutputKnotNode = Cast<UK2Node_Knot>(OutputNode))
			{
				if (ShouldChangeTangentForKnot(OutputKnotNode))
				{
					Params.StartDirection = EGPD_Input;
				}
			}

			if (UK2Node_Knot* InputKnotNode = Cast<UK2Node_Knot>(InputNode))
			{
				if (ShouldChangeTangentForKnot(InputKnotNode))
				{
					Params.EndDirection = EGPD_Output;
				}
			}
		}

		if (TreatWireAsExecutionPin(InputPin, OutputPin))
		{
			if (CanBuildRoadmap())
			{
				// knot nodes are removed from the graph at compile time, so we 
				// have to follow them until we find something that would have 
				// actually executed
				while (UK2Node_Knot* InputKnotNode = Cast<UK2Node_Knot>(InputNode))
				{
					InputNode = nullptr;

					UEdGraphPin* OutPin = InputKnotNode->GetOutputPin();
					if (OutPin->LinkedTo.Num() > 0)
					{
						check(OutPin->LinkedTo.Num() == 1);
						InputNode = OutPin->LinkedTo[0]->GetOwningNode();
					}	
				}

				// track if this node connection was ran or not
				bool bExecuted = false;

				// if the node belonging to InputPin was actually executed
				if (FExecPairingMap* ExecPaths = PredecessorPins.Find(InputNode))
				{
					// if the output pin is one of the pins that lead to InputNode being ran
					if (FTimePair const* ExecTiming = BackTraceExecPath(OutputPin, ExecPaths))
					{
						bExecuted = true;
						DetermineStyleOfExecWire(/*inout*/ Params.WireThickness, /*inout*/ Params.WireColor, /*inout*/ Params.bDrawBubbles, *ExecTiming);
					}
				}
				
				if (!bExecuted)
				{
					// It's not followed, fade it and keep it thin
					Params.WireColor = ReleaseColor;
					Params.WireThickness = ReleaseWireThickness;
				}
			}
			else
			{
				// Make exec wires slightly thicker even outside of debug
				Params.WireThickness = 3.0f;
			}
		}
		else
		{
			// Array types should draw thicker
			if ((InputPin && InputPin->PinType.bIsArray) || (OutputPin && OutputPin->PinType.bIsArray))
			{
				Params.WireThickness = 3.0f;
			}
		}
	}

	if (bDeemphasizeUnhoveredPins)
	{
		ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor);
	}
}