// Copyright Epic Games, Inc. All Rights Reserved.

/*=============================================================================
	MeshCardRepresentation.h
=============================================================================*/

#pragma once

#include "CoreMinimal.h"
#include "Containers/LockFreeList.h"
#include "UObject/GCObject.h"
#include "Templates/UniquePtr.h"
#include "DerivedMeshDataTaskUtils.h"
#include "DistanceFieldAtlas2.h"
#include "Async/AsyncWork.h"

template <class T>
class TLockFreePointerListLIFO;
class FSignedDistanceFieldBuildMaterialData;

class FAsyncCardRepresentationTask2;

class FAsyncCardRepresentationTaskWorker2 : public FNonAbandonableTask
{
public:
	FAsyncCardRepresentationTaskWorker2(FAsyncCardRepresentationTask2& InTask)
		: Task(InTask)
	{
	}

	FORCEINLINE TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(FAsyncCardRepresentationTaskWorker2, STATGROUP_ThreadPoolAsyncTasks);
	}

	void DoWork();

private:
	FAsyncCardRepresentationTask2& Task;
};

class FAsyncCardRepresentationTask2
{
public:
	bool bSuccess = false;

	// #if WITH_EDITOR
	TArray<FSignedDistanceFieldBuildMaterialData2> MaterialBlendModes;
	// #endif

	FSourceMeshDataForDerivedDataTask SourceMeshData;
	bool bGenerateDistanceFieldAsIfTwoSided = false;
	UStaticMesh* StaticMesh = nullptr;
	UStaticMesh* GenerateSource = nullptr;
	FString DDCKey;
	FCardRepresentationData* GeneratedCardRepresentation;
	TUniquePtr<FAsyncTask<FAsyncCardRepresentationTaskWorker2>> AsyncTask = nullptr;
};

/** Class that manages asynchronous building of mesh distance fields. */
class MESHUTILITIES2_API FCardRepresentationAsyncQueue2 : public FGCObject
{
public:
	FCardRepresentationAsyncQueue2();

	virtual ~FCardRepresentationAsyncQueue2() override;

	/** Adds a new build task. */
	void AddTask(FAsyncCardRepresentationTask2* Task);

	/** Cancel the build on this specific static mesh or block until it is completed if already started. */
	void CancelBuild(UStaticMesh* StaticMesh);

	/** Blocks the main thread until the async build are either cancelled or completed. */
	void CancelAllOutstandingBuilds();

	/** Blocks the main thread until the async build of the specified mesh is complete. */
	void BlockUntilBuildComplete(UStaticMesh* StaticMesh, bool bWarnIfBlocked);

	/** Blocks the main thread until all async builds complete. */
	void BlockUntilAllBuildsComplete();

	/** Called once per frame, fetches completed tasks and applies them to the scene. */
	void ProcessAsyncTasks(bool bLimitExecutionTime = false);

	/** Exposes UObject references used by the async build. */
	void AddReferencedObjects(FReferenceCollector& Collector);

	virtual FString GetReferencerName() const override;

	/** Blocks until it is safe to shut down (worker threads are idle). */
	void Shutdown();

	int32 GetNumOutstandingTasks() const
	{
		FScopeLock Lock(&CriticalSection);
		return ReferencedTasks.Num();
	}

private:
	friend FAsyncCardRepresentationTaskWorker2;
	void ProcessPendingTasks();

	TUniquePtr<FQueuedThreadPool> ThreadPool;

	/** Builds a single task with the given threadpool.  Called from the worker thread. */
	void Build(FAsyncCardRepresentationTask2* Task, class FQueuedThreadPool& ThreadPool);

	/** Change the priority of the background task. */
	void RescheduleBackgroundTask(FAsyncCardRepresentationTask2* InTask, EQueuedWorkPriority InPriority);

	/** Task will be sent to a background worker. */
	void StartBackgroundTask(FAsyncCardRepresentationTask2* Task);

	/** Cancel or finish any background work for the given task. */
	void CancelBackgroundTask(TArray<FAsyncCardRepresentationTask2*> Tasks);

	/** Game-thread managed list of tasks in the async system. */
	TArray<FAsyncCardRepresentationTask2*> ReferencedTasks;

	/** Tasks that are waiting on static mesh compilation to proceed */
	TArray<FAsyncCardRepresentationTask2*> PendingTasks;

	/** Tasks that have completed processing. */
	TLockFreePointerListLIFO<FAsyncCardRepresentationTask2> CompletedTasks;

	class IMeshUtilities2* MeshUtilities;

	mutable FCriticalSection CriticalSection;
};

/** Global build queue. */
extern MESHUTILITIES2_API FCardRepresentationAsyncQueue2* GCardRepresentationAsyncQueue2;
