// Copyright CodeSpartan, 2022. All rights reserved.

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Engine/StreamableManager.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Templates/SubclassOf.h"

#include "AsyncPreloadPrimaryAssets.generated.h"

/** Base class of all asset manager load calls */
UCLASS(Abstract)
class UAsyncActionPreloadPrimaryAssetBase : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

public:
	/** Execute the actual load */
	virtual void Activate() override;

protected:

	/** Called from asset manager */
	virtual void HandleLoadCompleted();

	/** Gets all UObjects matching AssetsToLoad that are already in memory */
	virtual void GetCurrentlyLoadedAssets(TArray<UObject*>& AssetList);

	/** Specific assets requested */
	TArray<FPrimaryAssetId> AssetsToLoad;

	/** Bundle states */
	TArray<FName> LoadBundles;

	/** Handle of load request */
	TSharedPtr<FStreamableHandle> LoadHandle;
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPrimaryAssetPreloaded, UObject*, Loaded);

UCLASS()
class UAsyncActionPreloadPrimaryAsset : public UAsyncActionPreloadPrimaryAssetBase
{
	GENERATED_BODY()

public:
	/**
	 * Load a primary asset object into memory, this will cause it to stay loaded until it is explicitly unloaded.
	 * The completed event will happen when the load succeeds or fails, you should cast the Loaded object to verify it is the correct type.
	 * If LoadBundles is specified, those bundles are loaded along with the asset.
	 */
	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", Category = "AssetManager", AutoCreateRefTerm = "LoadBundles", WorldContext = "WorldContextObject"))
	static UAsyncActionPreloadPrimaryAsset* AsyncPreloadPrimaryAsset(UObject* WorldContextObject, FPrimaryAssetId PrimaryAsset, const TArray<FName>& LoadBundles);

	UPROPERTY(BlueprintAssignable)
	FOnPrimaryAssetPreloaded Completed;

protected:

	/** Called from asset manager */
	virtual void HandleLoadCompleted() override;
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPrimaryAssetClassPreloaded, TSubclassOf<UObject>, Loaded);

UCLASS()
class UAsyncActionPreloadPrimaryAssetClass : public UAsyncActionPreloadPrimaryAssetBase
{
	GENERATED_BODY()

public:
	/**
	 * Load a primary asset class  into memory, this will cause it to stay loaded until it is explicitly unloaded.
	 * The completed event will happen when the load succeeds or fails, you should cast the Loaded class to verify it is the correct type.
	 * If LoadBundles is specified, those bundles are loaded along with the asset.
	 */
	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", Category = "AssetManager", AutoCreateRefTerm = "LoadBundles", WorldContext = "WorldContextObject"))
	static UAsyncActionPreloadPrimaryAssetClass* AsyncPreloadPrimaryAssetClass(UObject* WorldContextObject, FPrimaryAssetId PrimaryAsset, const TArray<FName>& LoadBundles);

	UPROPERTY(BlueprintAssignable)
	FOnPrimaryAssetClassPreloaded Completed;

protected:

	/** Called from asset manager */
	virtual void HandleLoadCompleted() override;
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPrimaryAssetListPreloaded, const TArray<UObject*>&, Loaded);

UCLASS()
class UAsyncActionPreloadPrimaryAssetList : public UAsyncActionPreloadPrimaryAssetBase
{
	GENERATED_BODY()

public:
	/**
	 * Load a list of primary asset objects into memory, this will cause them to stay loaded until explicitly unloaded.
	 * The completed event will happen when the load succeeds or fails, and the Loaded list will contain all of the requested assets found at completion.
	 * If LoadBundles is specified, those bundles are loaded along with the assets.
	 */
	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", Category = "AssetManager", AutoCreateRefTerm = "LoadBundles", WorldContext = "WorldContextObject"))
	static UAsyncActionPreloadPrimaryAssetList* AsyncPreloadPrimaryAssetList(UObject* WorldContextObject, const TArray<FPrimaryAssetId>& PrimaryAssetList, const TArray<FName>& LoadBundles);

	UPROPERTY(BlueprintAssignable)
	FOnPrimaryAssetListPreloaded Completed;

protected:

	/** Called from asset manager */
	virtual void HandleLoadCompleted() override;
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPrimaryAssetClassListPreloaded, const TArray<TSubclassOf<UObject>>&, Loaded);

UCLASS()
class UAsyncActionPreloadPrimaryAssetClassList : public UAsyncActionPreloadPrimaryAssetBase
{
	GENERATED_BODY()

public:
	/**
	 * Load a list of primary asset classes into memory, this will cause them to stay loaded until explicitly unloaded.
	 * The completed event will happen when the load succeeds or fails, and the Loaded list will contain all of the requested classes found at completion.
	 * If LoadBundles is specified, those bundles are loaded along with the assets.
	 */
	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", Category = "AssetManager", AutoCreateRefTerm = "LoadBundles", WorldContext = "WorldContextObject"))
	static UAsyncActionPreloadPrimaryAssetClassList* AsyncPreloadPrimaryAssetClassList(UObject* WorldContextObject, const TArray<FPrimaryAssetId>& PrimaryAssetList, const TArray<FName>& LoadBundles);

	UPROPERTY(BlueprintAssignable)
	FOnPrimaryAssetClassListPreloaded Completed;

protected:

	/** Called from asset manager */
	virtual void HandleLoadCompleted() override;
};