#include "RenderSnapshotBuilder.h"

#include "RuntimeClock.h"
#include "RuntimeParameterUtils.h"
#include "RuntimeStore.h"
#include "ShaderCompiler.h"

#include <algorithm>
#include <chrono>
#include <filesystem>
#include <mutex>
#include <utility>

RenderSnapshotBuilder::RenderSnapshotBuilder(RuntimeStore& runtimeStore) :
	mRuntimeStore(runtimeStore)
{
}

bool RenderSnapshotBuilder::BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error) const
{
	try
	{
		ShaderPackage shaderPackage;
		if (!mRuntimeStore.CopyShaderPackageForStoredLayer(layerId, shaderPackage, error))
			return false;

		const ShaderCompilerInputs inputs = mRuntimeStore.GetShaderCompilerInputs();

		ShaderCompiler compiler(
			inputs.repoRoot,
			inputs.wrapperPath,
			inputs.generatedGlslPath,
			inputs.patchedGlslPath,
			inputs.maxTemporalHistoryFrames);
		passSources.clear();
		passSources.reserve(shaderPackage.passes.size());
		for (const ShaderPassDefinition& pass : shaderPackage.passes)
		{
			ShaderPassBuildSource passSource;
			passSource.passId = pass.id;
			passSource.inputNames = pass.inputNames;
			passSource.outputName = pass.outputName;
			if (!compiler.BuildPassFragmentShaderSource(shaderPackage, pass, passSource.fragmentShaderSource, error))
				return false;
			passSources.push_back(std::move(passSource));
		}
		return true;
	}
	catch (const std::exception& exception)
	{
		error = std::string("RenderSnapshotBuilder::BuildLayerPassFragmentShaderSources exception: ") + exception.what();
		return false;
	}
	catch (...)
	{
		error = "RenderSnapshotBuilder::BuildLayerPassFragmentShaderSources threw a non-standard exception.";
		return false;
	}
}

unsigned RenderSnapshotBuilder::GetMaxTemporalHistoryFrames() const
{
	return mRuntimeStore.GetConfiguredMaxTemporalHistoryFrames();
}

RuntimeSnapshotVersions RenderSnapshotBuilder::GetVersions() const
{
	RuntimeSnapshotVersions versions;
	versions.renderStateVersion = mRenderStateVersion.load(std::memory_order_relaxed);
	versions.parameterStateVersion = mParameterStateVersion.load(std::memory_order_relaxed);
	return versions;
}

void RenderSnapshotBuilder::AdvanceFrame()
{
	++mFrameCounter;
}

void RenderSnapshotBuilder::BuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
{
	BuildLayerRenderStates(outputWidth, outputHeight, mRuntimeStore.BuildRenderSnapshotReadModel(), states);
}

bool RenderSnapshotBuilder::TryBuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
{
	BuildLayerRenderStates(outputWidth, outputHeight, mRuntimeStore.BuildRenderSnapshotReadModel(), states);
	return true;
}

bool RenderSnapshotBuilder::TryRefreshLayerParameters(std::vector<RuntimeRenderState>& states) const
{
	RefreshLayerParameters(mRuntimeStore.CopyCommittedLiveLayerStates(), states);
	return true;
}

void RenderSnapshotBuilder::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const
{
	RefreshDynamicRenderStateFields(mRuntimeStore.GetRenderTimingSnapshot(), states);
}

void RenderSnapshotBuilder::MarkRenderStateDirty()
{
	mRenderStateVersion.fetch_add(1, std::memory_order_relaxed);
	mParameterStateVersion.fetch_add(1, std::memory_order_relaxed);
}

void RenderSnapshotBuilder::MarkParameterStateDirty()
{
	mParameterStateVersion.fetch_add(1, std::memory_order_relaxed);
}

void RenderSnapshotBuilder::BuildLayerRenderStates(unsigned outputWidth, unsigned outputHeight, const RenderSnapshotReadModel& readModel, std::vector<RuntimeRenderState>& states) const
{
	states.clear();

	for (const LayerStackStore::LayerPersistentState& layer : readModel.committedLiveState.layers)
	{
		auto shaderIt = readModel.committedLiveState.packagesById.find(layer.shaderId);
		if (shaderIt == readModel.committedLiveState.packagesById.end())
			continue;
		const ShaderPackage& shaderPackage = shaderIt->second;

		RuntimeRenderState state;
		state.layerId = layer.id;
		state.shaderId = layer.shaderId;
		state.shaderName = shaderPackage.displayName;
		state.mixAmount = 1.0;
		state.bypass = layer.bypass ? 1.0 : 0.0;
		state.inputWidth = readModel.signalStatus.width;
		state.inputHeight = readModel.signalStatus.height;
		state.outputWidth = outputWidth;
		state.outputHeight = outputHeight;
		state.parameterDefinitions = shaderPackage.parameters;
		state.textureAssets = shaderPackage.textureAssets;
		state.fontAssets = shaderPackage.fontAssets;
		state.isTemporal = shaderPackage.temporal.enabled;
		state.temporalHistorySource = shaderPackage.temporal.historySource;
		state.requestedTemporalHistoryLength = shaderPackage.temporal.requestedHistoryLength;
		state.effectiveTemporalHistoryLength = shaderPackage.temporal.effectiveHistoryLength;
		state.feedback = shaderPackage.feedback;

		for (const ShaderParameterDefinition& definition : shaderPackage.parameters)
		{
			ShaderParameterValue value = DefaultValueForDefinition(definition);
			auto valueIt = layer.parameterValues.find(definition.id);
			if (valueIt != layer.parameterValues.end())
				value = valueIt->second;
			state.parameterValues[definition.id] = value;
		}

		states.push_back(state);
	}

	RefreshDynamicRenderStateFields(readModel.timing, states);
}

void RenderSnapshotBuilder::RefreshLayerParameters(const std::vector<LayerStackStore::LayerPersistentState>& layers, std::vector<RuntimeRenderState>& states) const
{
	for (RuntimeRenderState& state : states)
	{
		const auto layerIt = std::find_if(layers.begin(), layers.end(),
			[&state](const LayerStackStore::LayerPersistentState& layer) { return layer.id == state.layerId; });
		if (layerIt == layers.end())
			continue;

		state.bypass = layerIt->bypass ? 1.0 : 0.0;
		state.parameterValues.clear();
		for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
		{
			ShaderParameterValue value = DefaultValueForDefinition(definition);
			auto valueIt = layerIt->parameterValues.find(definition.id);
			if (valueIt != layerIt->parameterValues.end())
				value = valueIt->second;
			state.parameterValues[definition.id] = value;
		}
	}
}

void RenderSnapshotBuilder::RefreshDynamicRenderStateFields(const RenderTimingSnapshot& timing, std::vector<RuntimeRenderState>& states) const
{
	const RuntimeClockSnapshot clock = GetRuntimeClockSnapshot();
	const double timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - timing.startTime).count();
	const double frameCount = static_cast<double>(mFrameCounter.load(std::memory_order_relaxed));

	for (RuntimeRenderState& state : states)
	{
		state.timeSeconds = timeSeconds;
		state.utcTimeSeconds = clock.utcTimeSeconds;
		state.utcOffsetSeconds = clock.utcOffsetSeconds;
		state.startupRandom = timing.startupRandom;
		state.frameCount = frameCount;
	}
}
