#pragma once

#include "../control/ControlActionResult.h"
#include "../control/RuntimeControlCommand.h"
#include "RuntimeLayerModel.h"
#include "RuntimeStatePersistence.h"
#include "RuntimeShaderBridge.h"
#include "SupportedShaderCatalog.h"
#include "../telemetry/CadenceTelemetry.h"

#include <deque>
#include <functional>
#include <filesystem>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

namespace RenderCadenceCompositor
{
class RuntimeLayerController
{
public:
	using RenderLayerPublisher = std::function<void(const std::vector<RuntimeRenderLayerModel>&)>;

	explicit RuntimeLayerController(RenderLayerPublisher publisher = RenderLayerPublisher());
	RuntimeLayerController(const RuntimeLayerController&) = delete;
	RuntimeLayerController& operator=(const RuntimeLayerController&) = delete;
	~RuntimeLayerController();

	void SetPublisher(RenderLayerPublisher publisher);
	void Initialize(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames, std::string& runtimeShaderId);
	void StartStartupBuild(const std::string& runtimeShaderId);
	void Stop();

	ControlActionResult HandleAddLayer(const std::string& body);
	ControlActionResult HandleRemoveLayer(const std::string& body);
	ControlActionResult HandleControlCommand(const RuntimeControlCommand& command);

	RuntimeLayerModelSnapshot Snapshot(const CadenceTelemetrySnapshot& telemetry) const;
	const SupportedShaderCatalog& ShaderCatalog() const { return mShaderCatalog; }

private:
	bool LoadSupportedShaderCatalog(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames);
	void InitializeLayerModel(std::string& runtimeShaderId);
	bool InitializeLayerModelFromRuntimeState();
	void RequestRuntimeStatePersistence();
	void RequestRuntimeStatePersistenceLocked();
	std::filesystem::path ResolveRuntimeStatePath() const;
	void StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId, bool preserveExistingRenderArtifact = false);
	void PumpShaderBuildQueue();
	void StartQueuedShaderBuild(const std::string& layerId, const std::string& shaderId);
	void CompleteLayerShaderBuild(const std::string& layerId);
	void RetireLayerShaderBuild(const std::string& layerId);
	void CleanupRetiredShaderBuilds();
	void StopAllRuntimeShaderBuilds();
	void PublishRuntimeRenderLayers();
	bool MarkRuntimeBuildReady(const RuntimeShaderArtifact& artifact);
	void MarkRuntimeBuildFailedForLayer(const std::string& layerId, const std::string& message);

	std::string FirstRuntimeLayerId() const;
	static bool ExtractStringField(const std::string& body, const char* fieldName, std::string& value, std::string& error);

	RenderLayerPublisher mPublisher;
	std::string mShaderLibrary;
	unsigned mMaxTemporalHistoryFrames = 0;
	std::filesystem::path mRuntimeStatePath;
	RuntimeStatePersistenceWriter mPersistenceWriter;
	SupportedShaderCatalog mShaderCatalog;
	mutable std::mutex mRuntimeLayerMutex;
	RuntimeLayerModel mRuntimeLayerModel;
	std::mutex mShaderBuildMutex;
	struct QueuedShaderBuild
	{
		std::string layerId;
		std::string shaderId;
	};
	std::deque<QueuedShaderBuild> mQueuedShaderBuilds;
	std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> mShaderBuilds;
	std::vector<std::unique_ptr<RuntimeShaderBridge>> mRetiredShaderBuilds;
};
}
