#pragma once

class FHeroAsyncWriter : public FRunnable, public FArchive
{
	enum EConstants
	{
		InitialBufferSize = 128 * 1024
	};

	/** Thread to run the worker FRunnable on. Serializes the ring buffer to disk. */
	volatile FRunnableThread* Thread;
	/** Stops this thread */
	FThreadSafeCounter StopTaskCounter;

	/** Writer archive */
	FArchive& Ar;
	/** Data ring buffer */
	TArray<uint8> Buffer;
	/** [WRITER THREAD] Position where the unserialized data starts in the buffer */
	TAtomic<int32> BufferStartPos;
	/** [CLIENT THREAD] Position where the unserialized data ends in the buffer (such as if (BufferEndPos > BufferStartPos) Length = BufferEndPos - BufferStartPos; */
	TAtomic<int32> BufferEndPos;
	/** [CLIENT THREAD] Sync object for the buffer pos */
	FCriticalSection BufferPosCritical;
	/** [CLIENT/WRITER THREAD] Outstanding serialize request counter. This is to make sure we flush all requests. */
	FThreadSafeCounter SerializeRequestCounter;
	/** [CLIENT/WRITER THREAD] Tells the writer thread, the client requested flush. */
	FThreadSafeCounter WantsArchiveFlush;

	/** [WRITER THREAD] Last time the archive was flushed. used in threaded situations to flush the underlying archive at a certain maximum rate. */
	double LastArchiveFlushTime;

	/** [WRITER THREAD] Flushes the archive and reset the flush timer. */
	void FlushArchiveAndResetTimer();

	/** [WRITER THREAD] Serialize the contents of the ring buffer to disk */
	void SerializeBufferToArchive();

	/** [CLIENT THREAD] Flush the memory buffer (doesn't force the archive to flush). Can only be used from inside of BufferPosCritical lock. */
	void FlushBuffer();

public:

	FHeroAsyncWriter(FArchive& InAr);

	virtual ~FHeroAsyncWriter();

	/** [CLIENT THREAD] Serialize data to buffer that will later be saved to disk by the async thread */
	virtual void Serialize(void* InData, int64 Length) override;

	/** Flush all buffers to disk */
	void Flush();

	//~ Begin FRunnable Interface.
	virtual bool Init();
	virtual uint32 Run();
	virtual void Stop();
	//~ End FRunnable Interface
};