// Copyright CodeSpartan, 2022. All rights reserved.
#include "AsyncLoadAppearanceAssets.h"
#include "Engine/AssetManager.h"
#include "Engine/SkeletalMesh.h"
#include "Animation/AnimInstance.h"

UAsyncLoadAppearanceAssets::UAsyncLoadAppearanceAssets(const FObjectInitializer& ObjectInitializer) 
	: Super(ObjectInitializer), SkelMeshInput(nullptr), AnimBpClassInput(nullptr), GearAssetsInput(TArray<FPrimaryAssetId>())
{
}

UAsyncLoadAppearanceAssets* UAsyncLoadAppearanceAssets::AsyncLoadAppearanceAssets(UObject* WorldContextObject, const TSoftObjectPtr<USkeletalMesh> SkelMesh, const TSoftClassPtr<UAnimInstance> AnimBpClass, const TArray<FPrimaryAssetId> GearAssets)
{
	UAsyncLoadAppearanceAssets* BlueprintNode = NewObject<UAsyncLoadAppearanceAssets>();
	BlueprintNode->SkelMeshInput = SkelMesh;
	BlueprintNode->AnimBpClassInput = AnimBpClass;
	BlueprintNode->GearAssetsInput = GearAssets;
	BlueprintNode->RegisterWithGameInstance(WorldContextObject);
	return BlueprintNode;
}

void UAsyncLoadAppearanceAssets::Activate()
{		
	if (UAssetManager* Manager = UAssetManager::GetIfInitialized())
	{
		// async load primary data assets
		TArray<FName> Bundles;
		FStreamableDelegate Delegate1 = FStreamableDelegate::CreateUObject(this, &UAsyncLoadAppearanceAssets::OnPrimaryAssetsLoaded);

		GearAssetsInput.RemoveAll([](const FPrimaryAssetId Ptr){ return !Ptr.IsValid(); }); // purge invalid ids
		if (GearAssetsInput.Num() > 0) 
		{
			gearAssetsHandle = Manager->PreloadPrimaryAssets(GearAssetsInput, Bundles, true);
		}
		
		// async load soft pointers
		FStreamableManager& Streamable = Manager->GetStreamableManager();
		TArray<FSoftObjectPath> ItemsToStream{ SkelMeshInput.ToSoftObjectPath(), AnimBpClassInput.ToSoftObjectPath() };
		FStreamableDelegate Delegate2 = FStreamableDelegate::CreateUObject(this, &UAsyncLoadAppearanceAssets::OnSoftPointersLoaded);
		Streamable.RequestAsyncLoad(ItemsToStream, Delegate2);

		if (GearAssetsInput.Num() > 0 && gearAssetsHandle.IsValid())
		{
			if (!gearAssetsHandle->HasLoadCompleted())
			{
				gearAssetsHandle->BindCompleteDelegate(Delegate1);
				return;
			}
		}

		// Either Gear Assets load already succeeded, or it failed
		ExecuteLoadComplete();
	}
	else {
		FFrame::KismetExecutionMessage(TEXT("AsyncLoadAppearanceAssets failed, because it couldn't get Asset Manager!"), ELogVerbosity::Error);
	}		
}

void UAsyncLoadAppearanceAssets::OnSoftPointersLoaded()
{
	SkelMeshOutput = SkelMeshInput.Get();
	AnimBpClassOutput = AnimBpClassInput.Get();
	ExecuteLoadComplete();
}

void UAsyncLoadAppearanceAssets::OnPrimaryAssetsLoaded()
{
	if (UAssetManager* Manager = UAssetManager::GetIfInitialized())
	{
		for (const FPrimaryAssetId& IdToLoad : GearAssetsInput)
		{
			UObject* LoadedObject = Manager->GetPrimaryAssetObject(IdToLoad);
			if (LoadedObject)
			{
				GearAssetsOutput.Add(LoadedObject);
			}
		}
	}
	else {
		FFrame::KismetExecutionMessage(TEXT("AsyncLoadAppearanceAssets failed, because it couldn't get Asset Manager!"), ELogVerbosity::Error);
	}
	ExecuteLoadComplete();
}

void UAsyncLoadAppearanceAssets::ExecuteLoadComplete()
{
	// if both primary assets and soft pointers are loaded, broadcast the delegate
	if (assetsLoaded == 0) {
		assetsLoaded++;
	}
	else {
		LoadComplete.Broadcast(SkelMeshOutput, AnimBpClassOutput, GearAssetsOutput);
		SetReadyToDestroy();
	}
}