#pragma once

#include "RuntimeJson.h"
#include "RuntimeCoordinator.h"
#include "ShaderTypes.h"

#include <atomic>
#include <condition_variable>
#include <chrono>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

class ControlServer;
class OpenGLComposite;
class OscServer;
class RuntimeEventDispatcher;
class RuntimeStore;

class ControlServices
{
public:
	struct AppliedOscUpdate
	{
		std::string routeKey;
		std::string layerKey;
		std::string parameterKey;
		JsonValue targetValue;
	};

	struct CompletedOscCommit
	{
		std::string routeKey;
		uint64_t generation = 0;
	};

	explicit ControlServices(RuntimeEventDispatcher& runtimeEventDispatcher);
	~ControlServices();

	bool Start(OpenGLComposite& composite, RuntimeStore& runtimeStore, std::string& error);
	void BeginPolling(RuntimeCoordinator& runtimeCoordinator);
	void Stop();
	void BroadcastState();
	void RequestBroadcastState();
	bool QueueOscUpdate(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error);
	bool ApplyPendingOscUpdates(std::vector<AppliedOscUpdate>& appliedUpdates, std::string& error);
	bool QueueOscCommit(const std::string& routeKey, const std::string& layerKey, const std::string& parameterKey, const JsonValue& value, uint64_t generation, std::string& error);
	void ClearOscState();
	void ClearOscStateForLayerKey(const std::string& layerKey);
	void ConsumeCompletedOscCommits(std::vector<CompletedOscCommit>& completedCommits);

private:
	struct PendingOscUpdate
	{
		std::string layerKey;
		std::string parameterKey;
		std::string valueJson;
	};

	struct PendingOscCommit
	{
		std::string routeKey;
		std::string layerKey;
		std::string parameterKey;
		JsonValue value;
		uint64_t generation = 0;
	};

	void StartPolling(RuntimeCoordinator& runtimeCoordinator);
	void StopPolling();
	void PollLoop(RuntimeCoordinator& runtimeCoordinator);
	void WakePolling();
	void PublishRuntimeStateBroadcastRequested(const std::string& reason);
	void PublishOscValueReceived(const PendingOscUpdate& update, const std::string& routeKey);
	void PublishOscCommitRequested(const PendingOscCommit& commit);
	void PublishOscOverlaySettled(const PendingOscCommit& commit);

	std::unique_ptr<ControlServer> mControlServer;
	std::unique_ptr<OscServer> mOscServer;
	RuntimeEventDispatcher& mRuntimeEventDispatcher;
	std::thread mPollThread;
	std::atomic<bool> mPollRunning;
	std::mutex mPollWakeMutex;
	std::condition_variable mPollWakeCondition;
	bool mPollWakeRequested = false;
	std::mutex mPendingOscMutex;
	std::map<std::string, PendingOscUpdate> mPendingOscUpdates;
	std::mutex mPendingOscCommitMutex;
	std::map<std::string, PendingOscCommit> mPendingOscCommits;
	std::mutex mCompletedOscCommitMutex;
	std::vector<CompletedOscCommit> mCompletedOscCommits;
};
