#include "RenderThread.h"

#include "../logging/Logger.h"

#include <mutex>
#include <utility>
#include <vector>

void RenderThread::SubmitRuntimeShaderArtifact(const RuntimeShaderArtifact& artifact)
{
	if (artifact.fragmentShaderSource.empty())
		return;

	std::lock_guard<std::mutex> lock(mShaderArtifactMutex);
	mPendingShaderArtifact = artifact;
	mHasPendingShaderArtifact = true;
}

void RenderThread::SubmitRuntimeRenderLayers(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers)
{
	std::lock_guard<std::mutex> lock(mRenderLayersMutex);
	mPendingRenderLayers = layers;
	mHasPendingRenderLayers = true;
}

bool RenderThread::TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact)
{
	std::lock_guard<std::mutex> lock(mShaderArtifactMutex);
	if (!mHasPendingShaderArtifact)
		return false;

	artifact = std::move(mPendingShaderArtifact);
	mPendingShaderArtifact = RuntimeShaderArtifact();
	mHasPendingShaderArtifact = false;
	return true;
}

bool RenderThread::TryTakePendingRenderLayers(std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers)
{
	std::lock_guard<std::mutex> lock(mRenderLayersMutex);
	if (!mHasPendingRenderLayers)
		return false;

	layers = std::move(mPendingRenderLayers);
	mPendingRenderLayers.clear();
	mHasPendingRenderLayers = false;
	return true;
}

void RenderThread::TryCommitReadyRuntimeShader(RuntimeRenderScene& runtimeRenderScene)
{
	std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel> layers;
	std::string commitError;
	if (TryTakePendingRenderLayers(layers))
	{
		bool structuralChange = false;
		if (!runtimeRenderScene.CommitRenderLayers(layers, commitError, &structuralChange))
		{
			RenderCadenceCompositor::TryLog(
				RenderCadenceCompositor::LogLevel::Error,
				"render-thread",
				"Runtime render-layer commit failed: " + commitError);
			mShaderBuildFailures.fetch_add(1, std::memory_order_relaxed);
			return;
		}

		if (structuralChange)
		{
			RenderCadenceCompositor::TryLog(
				RenderCadenceCompositor::LogLevel::Log,
				"render-thread",
				"Runtime render layer snapshot committed.");
			mShaderBuildsCommitted.fetch_add(1, std::memory_order_relaxed);
		}
		return;
	}

	RuntimeShaderArtifact artifact;
	if (!TryTakePendingRuntimeShaderArtifact(artifact))
		return;

	RenderCadenceCompositor::RuntimeRenderLayerModel layer;
	layer.id = artifact.layerId.empty() ? "runtime-layer-1" : artifact.layerId;
	layer.shaderId = artifact.shaderId;
	layer.artifact = artifact;
	layers.push_back(std::move(layer));
	if (!runtimeRenderScene.CommitRenderLayers(layers, commitError))
	{
		RenderCadenceCompositor::TryLog(
			RenderCadenceCompositor::LogLevel::Error,
			"render-thread",
			"Runtime shader GL commit failed: " + commitError);
		mShaderBuildFailures.fetch_add(1, std::memory_order_relaxed);
		return;
	}

	RenderCadenceCompositor::TryLog(
		RenderCadenceCompositor::LogLevel::Log,
		"render-thread",
		"Runtime shader committed: " + artifact.shaderId + ". " + artifact.message);
	mShaderBuildsCommitted.fetch_add(1, std::memory_order_relaxed);
}
