#pragma once

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

#include <chrono>
#include <cstdint>
#include <map>
#include <string>
#include <vector>

struct RuntimeLiveOscUpdate
{
	std::string routeKey;
	std::string layerKey;
	std::string parameterKey;
	JsonValue targetValue;
};

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

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

struct RuntimeLiveStateApplyOptions
{
	bool allowCommit = false;
	double smoothing = 0.0;
	std::chrono::milliseconds commitDelay = std::chrono::milliseconds(150);
	std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
};

class RuntimeLiveState
{
public:
	void Clear();
	void ClearForLayerKey(const std::string& layerKey);
	std::size_t OverlayCount() const;
	void ApplyOscUpdates(const std::vector<RuntimeLiveOscUpdate>& updates);
	void ApplyOscCommitCompletions(const std::vector<RuntimeLiveOscCommitCompletion>& completedCommits);
	void PruneIncompatibleOverlays(const std::vector<RuntimeRenderState>& states);
	void ApplyToLayerStates(
		std::vector<RuntimeRenderState>& states,
		const RuntimeLiveStateApplyOptions& options,
		std::vector<RuntimeLiveOscCommitRequest>* commitRequests);

private:
	struct OscOverlayState
	{
		std::string layerKey;
		std::string parameterKey;
		JsonValue targetValue;
		ShaderParameterValue currentValue;
		bool hasCurrentValue = false;
		std::chrono::steady_clock::time_point lastUpdatedTime;
		std::chrono::steady_clock::time_point lastAppliedTime;
		uint64_t generation = 0;
		uint64_t pendingCommitGeneration = 0;
		bool commitQueued = false;
	};

	static bool OverlayMatchesLayerKey(const OscOverlayState& overlay, const std::string& layerKey);
	static bool TryResolveOverlayTarget(
		const OscOverlayState& overlay,
		const std::vector<RuntimeRenderState>& states,
		std::vector<RuntimeRenderState>::const_iterator& stateIt,
		std::vector<ShaderParameterDefinition>::const_iterator& definitionIt);

	std::map<std::string, OscOverlayState> mOscOverlayStates;
};
