#pragma once

#include "RuntimeSnapshotProvider.h"
#include "ShaderTypes.h"

#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

class RuntimeEventDispatcher;

struct PreparedLayerShader
{
	RuntimeRenderState state;
	std::vector<ShaderPassBuildSource> passes;
};

struct PreparedShaderBuild
{
	uint64_t generation = 0;
	bool succeeded = false;
	std::string message;
	RuntimeRenderStateSnapshot renderSnapshot;
	std::vector<PreparedLayerShader> layers;
};

class ShaderBuildQueue
{
public:
	ShaderBuildQueue(RuntimeSnapshotProvider& runtimeSnapshotProvider, RuntimeEventDispatcher& runtimeEventDispatcher);
	~ShaderBuildQueue();

	ShaderBuildQueue(const ShaderBuildQueue&) = delete;
	ShaderBuildQueue& operator=(const ShaderBuildQueue&) = delete;

	void RequestBuild(unsigned outputWidth, unsigned outputHeight);
	bool TryConsumeReadyBuild(PreparedShaderBuild& build);
	bool TryConsumeReadyBuild(uint64_t expectedGeneration, PreparedShaderBuild& build);
	void Stop();

private:
	void WorkerLoop();
	PreparedShaderBuild Build(uint64_t generation, unsigned outputWidth, unsigned outputHeight);
	void PublishBuildLifecycleEvent(const PreparedShaderBuild& build, unsigned outputWidth, unsigned outputHeight) const;

	RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
	RuntimeEventDispatcher& mRuntimeEventDispatcher;
	std::thread mWorkerThread;
	std::mutex mMutex;
	std::condition_variable mCondition;
	bool mStopping = false;
	bool mHasRequest = false;
	uint64_t mRequestedGeneration = 0;
	unsigned mRequestedOutputWidth = 0;
	unsigned mRequestedOutputHeight = 0;
	bool mHasReadyBuild = false;
	PreparedShaderBuild mReadyBuild;
};
