//------------------------------------------------------------------------------
static void BlueprintActionDatabaseImpl::AddBlueprintGraphActions(UBlueprint const* const Blueprint, FActionList& ActionListOut)
{
	using namespace FBlueprintNodeSpawnerFactory; // for MakeMacroNodeSpawner()

	for (auto GraphIt = Blueprint->MacroGraphs.CreateConstIterator(); GraphIt; ++GraphIt)
	{
		ActionListOut.Add(MakeMacroNodeSpawner(*GraphIt));
	}

	// local variables
	for (auto GraphIt = Blueprint->FunctionGraphs.CreateConstIterator(); GraphIt; ++GraphIt)
	{
		UEdGraph* FunctionGraph = (*GraphIt);

		TArray<UK2Node_FunctionEntry*> GraphEntryNodes;
		FunctionGraph->GetNodesOfClass<UK2Node_FunctionEntry>(GraphEntryNodes);

		for (UK2Node_FunctionEntry* FunctionEntry : GraphEntryNodes)
		{
			for (FBPVariableDescription const& LocalVar : FunctionEntry->LocalVariables)
			{
				UBlueprintNodeSpawner* GetVarSpawner = UBlueprintVariableNodeSpawner::Create(UK2Node_VariableGet::StaticClass(), FunctionGraph, LocalVar);
				ActionListOut.Add(GetVarSpawner);
				UBlueprintNodeSpawner* SetVarSpawner = UBlueprintVariableNodeSpawner::Create(UK2Node_VariableSet::StaticClass(), FunctionGraph, LocalVar);
				ActionListOut.Add(SetVarSpawner);
			}
		}
	}
}
//------------------------------------------------------------------------------
static void BlueprintActionDatabaseImpl::AddClassCastActions(UClass* Class, FActionList& ActionListOut)
{
	Class = Class->GetAuthoritativeClass();
	check(Class);

	UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
	bool bIsCastPermitted  = UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Class);

	if (bIsCastPermitted)
	{
		auto CustomizeCastNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, UClass* TargetType)
		{
			UK2Node_DynamicCast* CastNode = CastChecked<UK2Node_DynamicCast>(NewNode);
			CastNode->TargetType = TargetType;
		};

		UBlueprintNodeSpawner* CastObjNodeSpawner = UBlueprintNodeSpawner::Create<UK2Node_DynamicCast>();
		CastObjNodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeCastNodeLambda, Class);
		ActionListOut.Add(CastObjNodeSpawner);

		UBlueprintNodeSpawner* CastClassNodeSpawner = UBlueprintNodeSpawner::Create<UK2Node_ClassDynamicCast>();
		CastClassNodeSpawner->CustomizeNodeDelegate = CastObjNodeSpawner->CustomizeNodeDelegate;
		ActionListOut.Add(CastClassNodeSpawner);
	}
}
//------------------------------------------------------------------------------
void FBlueprintActionDatabase::ClearAssetActions(UObject* const AssetObject)
{
	FActionList* ActionList = ActionRegistry.Find(AssetObject);

	bool const bHasEntry = (ActionList != nullptr);
	if (bHasEntry)
	{
		for (UBlueprintNodeSpawner* Action : *ActionList)
		{
			// because some asserts expect everything to be cleaned up in a 
			// single GC pass, we can't wait for the GC'd Action to release its
			// template node from the cache
			Action->ClearCachedTemplateNode();
		}
		ActionRegistry.Remove(AssetObject);
	}

	if (UBlueprint* BlueprintAsset = Cast<UBlueprint>(AssetObject))
	{
		BlueprintAsset->OnChanged().RemoveAll(this);
		BlueprintAsset->OnCompiled().RemoveAll(this);
	}

	if (bHasEntry && (ActionList->Num() > 0) && !BlueprintActionDatabaseImpl::bIsInitializing)
	{
		EntryRemovedDelegate.Broadcast(AssetObject);
	}
}
//------------------------------------------------------------------------------
static void BlueprintActionDatabaseImpl::AddClassPropertyActions(UClass const* const Class, FActionList& ActionListOut)
{
	using namespace FBlueprintNodeSpawnerFactory; // for MakeDelegateNodeSpawner()

	bool const bIsComponent  = Class->IsChildOf<UActorComponent>();
	bool const bIsActorClass = Class->IsChildOf<AActor>();
	
	// loop over all the properties in the specified class; exclude-super because 
	// we can always get the super properties by looking up that class separateHavely 
	for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt)
	{
		UProperty* Property = *PropertyIt;
		if (!IsPropertyBlueprintVisible(Property))
		{
			continue;
		}

 		bool const bIsDelegate = Property->IsA(UMulticastDelegateProperty::StaticClass());
 		if (bIsDelegate)
 		{
			UMulticastDelegateProperty* DelegateProperty = CastChecked<UMulticastDelegateProperty>(Property);
			if (DelegateProperty->HasAnyPropertyFlags(CPF_BlueprintAssignable))
			{
				UBlueprintNodeSpawner* AddSpawner = UBlueprintDelegateNodeSpawner::Create(UK2Node_AddDelegate::StaticClass(), DelegateProperty);
				ActionListOut.Add(AddSpawner);
				
				UBlueprintNodeSpawner* AssignSpawner = MakeAssignDelegateNodeSpawner(DelegateProperty);
				ActionListOut.Add(AssignSpawner);
			}
			
			if (DelegateProperty->HasAnyPropertyFlags(CPF_BlueprintCallable))
			{
				UBlueprintNodeSpawner* CallSpawner = UBlueprintDelegateNodeSpawner::Create(UK2Node_CallDelegate::StaticClass(), DelegateProperty);
				ActionListOut.Add(CallSpawner);
			}
			
			UBlueprintNodeSpawner* RemoveSpawner = UBlueprintDelegateNodeSpawner::Create(UK2Node_RemoveDelegate::StaticClass(), DelegateProperty);
			ActionListOut.Add(RemoveSpawner);
			UBlueprintNodeSpawner* ClearSpawner = UBlueprintDelegateNodeSpawner::Create(UK2Node_ClearDelegate::StaticClass(), DelegateProperty);
			ActionListOut.Add(ClearSpawner);

			if (bIsComponent)
			{
				ActionListOut.Add(MakeComponentBoundEventSpawner(DelegateProperty));
			}
			else if (bIsActorClass)
			{
				ActionListOut.Add(MakeActorBoundEventSpawner(DelegateProperty));
			}
 		}
		else
		{
			UBlueprintVariableNodeSpawner* GetterSpawner = UBlueprintVariableNodeSpawner::Create(UK2Node_VariableGet::StaticClass(), Property);
			ActionListOut.Add(GetterSpawner);
			UBlueprintVariableNodeSpawner* SetterSpawner = UBlueprintVariableNodeSpawner::Create(UK2Node_VariableSet::StaticClass(), Property);
			ActionListOut.Add(SetterSpawner);
		}
	}
}
//------------------------------------------------------------------------------
static void BlueprintActionDatabaseImpl::AddClassFunctionActions(UClass const* const Class, FActionList& ActionListOut)
{
	using namespace FBlueprintNodeSpawnerFactory; // for MakeMessageNodeSpawner()
	check(Class != nullptr);

	// loop over all the functions in the specified class; exclude-super because 
	// we can always get the super functions by looking up that class separately 
	for (TFieldIterator<UFunction> FunctionIt(Class, EFieldIteratorFlags::ExcludeSuper); FunctionIt; ++FunctionIt)
	{
		UFunction* Function = *FunctionIt;

		bool const bIsInheritedFunction = BlueprintActionDatabaseImpl::IsInheritedBlueprintFunction(Function);
		if (bIsInheritedFunction)
		{
			// inherited functions will be captured when the parent class is ran
			// through this function (no need to duplicate)
			continue;
		}

		bool const bIsBpInterfaceFunc = BlueprintActionDatabaseImpl::IsBlueprintInterfaceFunction(Function);
		if (UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(Function) && !bIsBpInterfaceFunc)
		{
			if (UBlueprintEventNodeSpawner* NodeSpawner = UBlueprintEventNodeSpawner::Create(Function))
			{
				ActionListOut.Add(NodeSpawner);
			}
		}
		
		if (UEdGraphSchema_K2::CanUserKismetCallFunction(Function))
		{
			// @TODO: if this is a Blueprint, and this function is from a 
			//        Blueprint "implemented interface", then we don't need to 
			//        include it (the function is accounted for in from the 
			//        interface class).
			UBlueprintFunctionNodeSpawner* FuncSpawner = UBlueprintFunctionNodeSpawner::Create(Function);
			ActionListOut.Add(FuncSpawner);

			if (FKismetEditorUtilities::IsClassABlueprintInterface(Class))
			{
				FuncSpawner->DefaultMenuSignature.MenuName = FText::Format(LOCTEXT("InterfaceCallMenuName", "{0} (Interface Call)"), 
					FuncSpawner->DefaultMenuSignature.MenuName);

				ActionListOut.Add(MakeMessageNodeSpawner(Function));
			}
		}
	}
}
//------------------------------------------------------------------------------
void FBlueprintActionDatabase::ClearUnloadedAssetActions(FName ObjectPath)
{
	// Check if the asset can be found in the unloaded action registry, if it can, we need to remove it
	if(TArray<UBlueprintNodeSpawner*>* UnloadedActionList = UnloadedActionRegistry.Find(ObjectPath))
	{
		for(UBlueprintNodeSpawner* NodeSpawner : *UnloadedActionList)
		{
			FActionList* ActionList = ActionRegistry.Find(NodeSpawner->NodeClass);

			// Remove the NodeSpawner from the main registry, it will be replaced with the loaded version of the action
			ActionList->Remove(NodeSpawner);
		}

		// Remove the asset's path from the unloaded registry, it is no longer needed
		UnloadedActionRegistry.Remove(ObjectPath);
	}
}
void FLatentActionManager::TickLatentActionForObject(float DeltaTime, FActionList& ObjectActionList, UObject* InObject)
{
    typedef TPair<int32, FPendingLatentAction*> FActionListPair;
    TArray<FActionListPair, TInlineAllocator<4>> ItemsToRemove;

    FLatentResponse Response(DeltaTime);
    for (TMultiMap<int32, FPendingLatentAction*>::TConstIterator It(ObjectActionList); It; ++It)
    {
        FPendingLatentAction* Action = It.Value();

        Response.bRemoveAction = false;

        Action->UpdateOperation(Response);

        if (Response.bRemoveAction)
        {
            new (ItemsToRemove) FActionListPair(TPairInitializer<int32, FPendingLatentAction*>(It.Key(), Action));
        }
    }

    // Remove any items that were deleted
    for (int32 i = 0; i < ItemsToRemove.Num(); ++i)
    {
        const FActionListPair& ItemPair = ItemsToRemove[i];
        const int32 ItemIndex = ItemPair.Key;
        FPendingLatentAction* DyingAction = ItemPair.Value;
        ObjectActionList.Remove(ItemIndex, DyingAction);
        delete DyingAction;
    }

    // Trigger any pending execution links
    for (int32 i = 0; i < Response.LinksToExecute.Num(); ++i)
    {
        FLatentResponse::FExecutionInfo& LinkInfo = Response.LinksToExecute[i];
        if (LinkInfo.LinkID != INDEX_NONE)
        {
            if (UObject* CallbackTarget = LinkInfo.CallbackTarget.Get())
            {
                check(CallbackTarget == InObject);

                if (UFunction* ExecutionFunction = CallbackTarget->FindFunction(LinkInfo.ExecutionFunction))
                {
                    CallbackTarget->ProcessEvent(ExecutionFunction, &(LinkInfo.LinkID));
                }
                else
                {
                    UE_LOG(LogScript, Warning, TEXT("FLatentActionManager::ProcessLatentActions: Could not find latent action resume point named '%s' on '%s' called by '%s'"),
                           *LinkInfo.ExecutionFunction.ToString(), *(CallbackTarget->GetPathName()), *(InObject->GetPathName()));
                }
            }
            else
            {
                UE_LOG(LogScript, Warning, TEXT("FLatentActionManager::ProcessLatentActions: CallbackTarget is None."));
            }
        }
    }
}
//------------------------------------------------------------------------------
static void BlueprintActionDatabaseImpl::AddSkeletonActions(const USkeleton& Skeleton, FActionList& ActionListOut)
{
	for (int32 I = 0; I < Skeleton.AnimationNotifies.Num(); ++I)
	{
		FName NotifyName = Skeleton.AnimationNotifies[I];
		FString Label = NotifyName.ToString();

		FString SignatureName = FString::Printf(TEXT("AnimNotify_%s"), *Label);
		ActionListOut.Add(FBlueprintNodeSpawnerFactory::MakeAnimOwnedEventSpawner(FName(*SignatureName), FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::AnimNotify)));
	}
}
//------------------------------------------------------------------------------
static void BlueprintActionDatabaseImpl::AddAnimBlueprintGraphActions(UAnimBlueprint const* AnimBlueprint, FActionList& ActionListOut)
{
	if (UAnimBlueprintGeneratedClass* GeneratedClass = AnimBlueprint->GetAnimBlueprintGeneratedClass())
	{
		for (int32 NotifyIdx = 0; NotifyIdx < GeneratedClass->AnimNotifies.Num(); NotifyIdx++)
		{
			FName NotifyName = GeneratedClass->AnimNotifies[NotifyIdx].NotifyName;
			if (NotifyName != NAME_None)
			{
				FString Label = NotifyName.ToString();
				FString SignatureName = FString::Printf(TEXT("AnimNotify_%s"), *Label);
				ActionListOut.Add(FBlueprintNodeSpawnerFactory::MakeAnimOwnedEventSpawner(FName(*SignatureName), FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::AnimNotify)));
			}
		}
	}
}