// Creates child/parent relationship between the nodes
static void LinkToParents(TMultiMap<UObject*, FRefGraphItem*>& InputGraphNodeList, FRefGraphItem* NodeToLink)
{
	for (auto It = InputGraphNodeList.CreateConstIterator(); It; ++It)
	{
		if (It.Value()->Link.ReferencedObj == NodeToLink->Link.ReferencedBy)
		{
			It.Value()->Children.Push(NodeToLink);
			NodeToLink->Parents.Push(It.Value());
		}
	}
}
void FReferenceChainSearch::BuildRefGraph()
{
	UE_LOG(LogReferenceChain, Log, TEXT("Generating reference graph ..."));

	bool bContinue = true;

	TMultiMap<UObject*, FRefGraphItem*> GraphNodes;

	// Create the first graph-nodes referencing the target object
	for (FRawObjectIterator It;It;++It)
	{
		UObject* Obj = *It;

		TArray<FReferenceChainLink>& RefList = ReferenceMap.FindChecked(Obj);

		for (int32 i=0; i < RefList.Num(); ++i)
		{
			if (RefList[i].ReferencedObj == ObjectToFind)
			{
				FRefGraphItem* Node = new FRefGraphItem();
				Node->Link = RefList[i];
				GraphNodes.Add(Node->Link.ReferencedBy, Node);

				RefList[i].ReferenceType = EReferenceType::Invalid;
			}
		}
	}

	int32 Level = 0;
	UE_LOG(LogReferenceChain, Log, TEXT("Level 0 has %d nodes ..."), GraphNodes.Num());

	while(bContinue)
	{
		int32 AddedNodes = 0;

		TArray<FRefGraphItem*> NewGraphNodes;


		for (FRawObjectIterator It;It;++It)
		{
			UObject* Obj = *It;

			TArray<FReferenceChainLink>& RefList = ReferenceMap.FindChecked(Obj);

			for (int32 i=0; i < RefList.Num(); ++i)
			{
				if (RefList[i].ReferenceType == EReferenceType::Invalid ||
					RefList[i].ReferencedObj->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) // references to rooted objects are not important
				{ 
					continue; 
				}

				TArray<FRefGraphItem*> RefNodes;

				if (FindReferencedGraphNodes(GraphNodes, RefList[i].ReferencedObj, RefNodes) > 0)
				{
					FRefGraphItem* Node = FindNode(GraphNodes, RefList[i].ReferencedBy, RefList[i].ReferencedObj);
					if (Node == NULL)
					{
						Node = new FRefGraphItem();
						Node->Link = RefList[i];

						NewGraphNodes.Push(Node);
					}
				
					for (int32 j=0; j < RefNodes.Num(); ++j)
					{
						Node->Children.Push(RefNodes[j]);
						RefNodes[j]->Parents.Push(Node);
					}

					++AddedNodes;
					
					RefList[i].ReferenceType = EReferenceType::Invalid;
				}
			}
		}
		Level++;
		UE_LOG(LogReferenceChain, Log, TEXT("Level %d added %d nodes ..."), Level, NewGraphNodes.Num());

		for (int32 i = 0; i < NewGraphNodes.Num(); ++i)
		{
			GraphNodes.Add(NewGraphNodes[i]->Link.ReferencedBy, NewGraphNodes[i]);
		}


		NewGraphNodes.Empty(NewGraphNodes.Num());

		bContinue = AddedNodes > 0;
	}

	TArray<FReferenceChain> Chains;

	UE_LOG(LogReferenceChain, Log, TEXT("Generating reference chains ..."));
	for (auto It = GraphNodes.CreateConstIterator(); It; ++It)
	{
		FRefGraphItem* Node = It.Value();

		if (Node->Link.ReferencedBy->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet))
		{
			FReferenceChain CurChain;
			CreateReferenceChain(Node, CurChain, Chains, ObjectToFind, Level);
		}
	}

	for (int32 i=0; i < Chains.Num(); ++i)
	{
		InsertReferenceChain(Chains[i]);
	}
}
void FAudioComponentVisualizer::DrawVisualization( const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI )
{
	if(View->Family->EngineShowFlags.AudioRadius)
	{
		const UAudioComponent* AudioComp = Cast<const UAudioComponent>(Component);
		if(AudioComp != NULL)
		{
			const FTransform& Transform = AudioComp->ComponentToWorld;

			TMultiMap<EAttenuationShape::Type, FAttenuationSettings::AttenuationShapeDetails> ShapeDetailsMap;
			AudioComp->CollectAttenuationShapesForVisualization(ShapeDetailsMap);

			FVector Translation = Transform.GetTranslation();
			FVector UnitXAxis   = Transform.GetUnitAxis( EAxis::X );
			FVector UnitYAxis   = Transform.GetUnitAxis( EAxis::Y );
			FVector UnitZAxis   = Transform.GetUnitAxis( EAxis::Z );

			for ( auto It = ShapeDetailsMap.CreateConstIterator(); It; ++It )
			{
				FColor AudioOuterRadiusColor(255, 153, 0);
				FColor AudioInnerRadiusColor(216, 130, 0);

				const FAttenuationSettings::AttenuationShapeDetails& ShapeDetails = It.Value();
				switch(It.Key())
				{
				case EAttenuationShape::Box:

					if (ShapeDetails.Falloff > 0.f)
					{
						DrawOrientedWireBox( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, ShapeDetails.Extents + FVector(ShapeDetails.Falloff), AudioOuterRadiusColor, SDPG_World);
						DrawOrientedWireBox( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, ShapeDetails.Extents, AudioInnerRadiusColor, SDPG_World);
					}
					else
					{
						DrawOrientedWireBox( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, ShapeDetails.Extents, AudioOuterRadiusColor, SDPG_World);
					}
					break;

				case EAttenuationShape::Capsule:

					if (ShapeDetails.Falloff > 0.f)
					{
						DrawWireCapsule( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, AudioOuterRadiusColor, ShapeDetails.Extents.Y + ShapeDetails.Falloff, ShapeDetails.Extents.X + ShapeDetails.Falloff, 25, SDPG_World);
						DrawWireCapsule( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, AudioInnerRadiusColor, ShapeDetails.Extents.Y, ShapeDetails.Extents.X, 25, SDPG_World);
					}
					else
					{
						DrawWireCapsule( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, AudioOuterRadiusColor, ShapeDetails.Extents.Y, ShapeDetails.Extents.X, 25, SDPG_World);
					}
					break;

				case EAttenuationShape::Cone:
					{
						FTransform Origin = Transform;
						Origin.SetScale3D(FVector(1.f));
						Origin.SetTranslation(Translation - (UnitXAxis * ShapeDetails.ConeOffset));

						if (ShapeDetails.Falloff > 0.f || ShapeDetails.Extents.Z > 0.f)
						{
							float ConeRadius = ShapeDetails.Extents.X + ShapeDetails.Falloff + ShapeDetails.ConeOffset;
							float ConeAngle = ShapeDetails.Extents.Y + ShapeDetails.Extents.Z;
							DrawWireSphereCappedCone(PDI, Origin, ConeRadius, ConeAngle, 16, 4, 10, AudioOuterRadiusColor, SDPG_World);

							ConeRadius = ShapeDetails.Extents.X + ShapeDetails.ConeOffset;
							ConeAngle = ShapeDetails.Extents.Y;
							DrawWireSphereCappedCone(PDI, Origin, ConeRadius, ConeAngle, 16, 4, 10, AudioInnerRadiusColor, SDPG_World );
						}
						else
						{
							const float ConeRadius = ShapeDetails.Extents.X + ShapeDetails.ConeOffset;
							const float ConeAngle = ShapeDetails.Extents.Y;
							DrawWireSphereCappedCone(PDI, Origin, ConeRadius, ConeAngle, 16, 4, 10, AudioOuterRadiusColor, SDPG_World );
						}
					}
					break;

				case EAttenuationShape::Sphere:

					if (ShapeDetails.Falloff > 0.f)
					{
						DrawWireSphereAutoSides(PDI, Translation, AudioOuterRadiusColor, ShapeDetails.Extents.X + ShapeDetails.Falloff, SDPG_World);
						DrawWireSphereAutoSides(PDI, Translation, AudioInnerRadiusColor, ShapeDetails.Extents.X, SDPG_World);
					}
					else
					{
						DrawWireSphereAutoSides(PDI, Translation, AudioOuterRadiusColor, ShapeDetails.Extents.X, SDPG_World);
					}
					break;

				default:
					check(false);
				}
			}
		}
	}
}