#pragma once

#include "HealthTelemetry.h"
#include "CommittedLiveState.h"
#include "LayerStackStore.h"
#include "PersistenceWriter.h"
#include "RenderSnapshotBuilder.h"
#include "RuntimeConfigStore.h"
#include "RuntimeJson.h"
#include "RuntimeStoreReadModels.h"
#include "ShaderPackageCatalog.h"
#include "ShaderTypes.h"

#include <chrono>
#include <filesystem>
#include <mutex>
#include <string>
#include <vector>

class RuntimeStore
{
public:
	using StoredParameterSnapshot = LayerStackStore::StoredParameterSnapshot;
	using LayerPersistentState = LayerStackStore::LayerPersistentState;

	RuntimeStore();
	HealthTelemetry& GetHealthTelemetry();
	const HealthTelemetry& GetHealthTelemetry() const;
	RenderSnapshotBuilder& GetRenderSnapshotBuilder();
	const RenderSnapshotBuilder& GetRenderSnapshotBuilder() const;

	bool InitializeStore(std::string& error);
	std::string BuildPersistentStateJson() const;
	PersistenceSnapshot BuildRuntimeStatePersistenceSnapshot(const PersistenceRequest& request) const;
	bool RequestPersistence(const PersistenceRequest& request, std::string& error);
	bool FlushPersistenceForShutdown(std::chrono::milliseconds timeout, std::string& error);
	bool PollStoredFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error);

	bool CreateStoredLayer(const std::string& shaderId, std::string& error);
	bool DeleteStoredLayer(const std::string& layerId, std::string& error);
	bool MoveStoredLayer(const std::string& layerId, int direction, std::string& error);
	bool MoveStoredLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error);
	bool SetStoredLayerBypassState(const std::string& layerId, bool bypassed, std::string& error);
	bool SetStoredLayerShaderSelection(const std::string& layerId, const std::string& shaderId, std::string& error);
	bool SetStoredParameterValue(const std::string& layerId, const std::string& parameterId, const ShaderParameterValue& value, bool persistState, std::string& error);
	bool ResetStoredLayerParameterValues(const std::string& layerId, std::string& error);
	bool SaveStackPresetSnapshot(const std::string& presetName, std::string& error) const;
	bool LoadStackPresetSnapshot(const std::string& presetName, std::string& error);
	bool HasStoredLayer(const std::string& layerId) const;
	bool HasStoredShader(const std::string& shaderId) const;
	bool TryGetStoredParameterById(const std::string& layerId, const std::string& parameterId, StoredParameterSnapshot& snapshot, std::string& error) const;
	bool TryGetStoredParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, StoredParameterSnapshot& snapshot, std::string& error) const;
	bool ResolveStoredLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const;
	bool ResolveStoredLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const;
	bool IsValidStackPresetName(const std::string& presetName) const;
	double GetRuntimeElapsedSeconds() const;

	const std::filesystem::path& GetRuntimeRepositoryRoot() const;
	const std::filesystem::path& GetRuntimeUiRoot() const;
	const std::filesystem::path& GetRuntimeDocsRoot() const;
	const std::filesystem::path& GetRuntimeDataRoot() const;
	unsigned short GetConfiguredControlServerPort() const;
	unsigned short GetConfiguredOscPort() const;
	const std::string& GetConfiguredOscBindAddress() const;
	double GetConfiguredOscSmoothing() const;
	unsigned GetConfiguredMaxTemporalHistoryFrames() const;
	unsigned GetConfiguredPreviewFps() const;
	bool IsExternalKeyingConfigured() const;
	const std::string& GetConfiguredInputVideoFormat() const;
	const std::string& GetConfiguredInputFrameRate() const;
	const std::string& GetConfiguredOutputVideoFormat() const;
	const std::string& GetConfiguredOutputFrameRate() const;
	void SetBoundControlServerPort(unsigned short port);

	void SetCompileStatus(bool succeeded, const std::string& message);
	void ClearReloadRequest();
	bool CopyShaderPackageForStoredLayer(const std::string& layerId, ShaderPackage& shaderPackage, std::string& error) const;
	::ShaderCompilerInputs GetShaderCompilerInputs() const;
	::CommittedLiveStateReadModel BuildCommittedLiveStateReadModel() const;
	::RenderSnapshotReadModel BuildRenderSnapshotReadModel() const;
	std::vector<LayerPersistentState> CopyCommittedLiveLayerStates() const;
	std::vector<LayerPersistentState> CopyLayerStates() const;
	::RenderTimingSnapshot GetRenderTimingSnapshot() const;
	::RuntimeStatePresentationReadModel BuildRuntimeStatePresentationReadModel() const;

private:
	bool LoadPersistentState(std::string& error);
	PersistenceSnapshot BuildRuntimeStatePersistenceSnapshotLocked(const PersistenceRequest& request) const;
	PersistenceSnapshot BuildStackPresetPersistenceSnapshot(const std::string& presetName) const;
	bool ScanShaderPackages(std::string& error);
	std::string ReadTextFile(const std::filesystem::path& path, std::string& error) const;
	std::vector<std::string> GetStackPresetNamesLocked() const;
	void MarkRenderStateDirtyLocked();
	void MarkParameterStateDirtyLocked();

	RenderSnapshotBuilder mRenderSnapshotBuilder;
	RuntimeConfigStore mConfigStore;
	ShaderPackageCatalog mShaderCatalog;
	CommittedLiveState mCommittedLiveState;
	HealthTelemetry mHealthTelemetry;
	mutable PersistenceWriter mPersistenceWriter;
	mutable std::mutex mMutex;
	bool mReloadRequested;
	bool mCompileSucceeded;
	std::string mCompileMessage;
	double mStartupRandom;
	unsigned short mServerPort;
	bool mAutoReloadEnabled;
	std::chrono::steady_clock::time_point mStartTime;
	std::chrono::steady_clock::time_point mLastScanTime;
};
