#include "RuntimeLayerModel.h"

#include "RuntimeParameterUtils.h"

#include <cctype>
#include <cstdint>
#include <set>
#include <utility>

namespace RenderCadenceCompositor
{
namespace
{
bool ParseRuntimeLayerNumber(const std::string& layerId, uint64_t& number)
{
	const std::string prefix = "runtime-layer-";
	if (layerId.compare(0, prefix.size(), prefix) != 0)
		return false;

	const std::string suffix = layerId.substr(prefix.size());
	if (suffix.empty())
		return false;

	uint64_t parsed = 0;
	for (char character : suffix)
	{
		if (!std::isdigit(static_cast<unsigned char>(character)))
			return false;
		parsed = parsed * 10 + static_cast<uint64_t>(character - '0');
	}

	number = parsed;
	return true;
}

std::string AllocateRestoredLayerId(std::set<std::string>& usedLayerIds, uint64_t& nextLayerNumber)
{
	for (;;)
	{
		std::string candidate = "runtime-layer-" + std::to_string(nextLayerNumber++);
		if (usedLayerIds.insert(candidate).second)
			return candidate;
	}
}
}

bool RuntimeLayerModel::InitializeFromRuntimeState(const SupportedShaderCatalog& shaderCatalog, const JsonValue& runtimeState, std::string& error)
{
	if (!runtimeState.isObject())
	{
		error = "Runtime state root must be a JSON object.";
		return false;
	}

	const JsonValue* layersValue = runtimeState.find("layers");
	if (!layersValue || !layersValue->isArray())
	{
		error = "Runtime state must contain a layers array.";
		return false;
	}

	std::vector<Layer> restoredLayers;
	std::set<std::string> usedLayerIds;
	uint64_t nextLayerNumber = 1;

	for (const JsonValue& layerValue : layersValue->asArray())
	{
		if (!layerValue.isObject())
			continue;

		const JsonValue* shaderIdValue = layerValue.find("shaderId");
		if (!shaderIdValue || !shaderIdValue->isString() || shaderIdValue->asString().empty())
			continue;

		const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(shaderIdValue->asString());
		if (!shaderPackage)
			continue;

		Layer layer;
		const JsonValue* layerIdValue = layerValue.find("id");
		if (layerIdValue && layerIdValue->isString() && !layerIdValue->asString().empty() && usedLayerIds.insert(layerIdValue->asString()).second)
			layer.id = layerIdValue->asString();
		else
			layer.id = AllocateRestoredLayerId(usedLayerIds, nextLayerNumber);

		uint64_t restoredLayerNumber = 0;
		if (ParseRuntimeLayerNumber(layer.id, restoredLayerNumber) && restoredLayerNumber >= nextLayerNumber)
			nextLayerNumber = restoredLayerNumber + 1;

		layer.shaderId = shaderPackage->id;
		layer.packageFingerprint = ShaderPackageFingerprint(*shaderPackage);
		layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
		const JsonValue* bypassValue = layerValue.find("bypass");
		layer.bypass = bypassValue && bypassValue->isBoolean() ? bypassValue->asBoolean() : false;
		layer.buildState = RuntimeLayerBuildState::Pending;
		layer.message = "Runtime Slang build is waiting to start.";
		InitializeDefaultParameterValues(layer, *shaderPackage);

		const JsonValue* parameterValues = layerValue.find("parameterValues");
		if (parameterValues && parameterValues->isObject())
		{
			for (const ShaderParameterDefinition& definition : layer.parameterDefinitions)
			{
				const JsonValue* value = parameterValues->find(definition.id);
				if (!value)
					continue;

				ShaderParameterValue normalizedValue;
				std::string normalizeError;
				if (NormalizeAndValidateParameterValue(definition, *value, normalizedValue, normalizeError))
					layer.parameterValues[definition.id] = normalizedValue;
			}
		}

		restoredLayers.push_back(std::move(layer));
	}

	if (restoredLayers.empty())
	{
		error = "Runtime state did not contain any supported layers.";
		return false;
	}

	mLayers = std::move(restoredLayers);
	mNextLayerNumber = nextLayerNumber;
	error.clear();
	return true;
}
}
