#include "RuntimeLayerModel.h"

#include "RuntimeParameterUtils.h"

#include <map>
#include <utility>

namespace RenderCadenceCompositor
{
namespace
{
JsonValue ParameterValueToJson(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
{
	switch (definition.type)
	{
	case ShaderParameterType::Float:
		return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front());
	case ShaderParameterType::Vec2:
	case ShaderParameterType::Color:
	{
		JsonValue array = JsonValue::MakeArray();
		for (double number : value.numberValues)
			array.pushBack(JsonValue(number));
		return array;
	}
	case ShaderParameterType::Boolean:
		return JsonValue(value.booleanValue);
	case ShaderParameterType::Enum:
		return JsonValue(value.enumValue);
	case ShaderParameterType::Text:
		return JsonValue(value.textValue);
	case ShaderParameterType::Trigger:
		return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front());
	}
	return JsonValue();
}
}

bool RuntimeLayerModel::ReloadFromCatalog(const SupportedShaderCatalog& shaderCatalog, std::vector<std::pair<std::string, std::string>>& buildsToStart, std::string& error)
{
	buildsToStart.clear();
	for (Layer& layer : mLayers)
	{
		const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(layer.shaderId);
		if (!shaderPackage)
		{
			layer.buildState = RuntimeLayerBuildState::Failed;
			layer.message = "Shader '" + layer.shaderId + "' is no longer available after reload.";
			continue;
		}

		const std::string nextFingerprint = ShaderPackageFingerprint(*shaderPackage);
		if (layer.packageFingerprint == nextFingerprint)
		{
			buildsToStart.push_back({ layer.id, layer.shaderId });
			continue;
		}

		std::map<std::string, ShaderParameterDefinition> previousDefinitions;
		for (const ShaderParameterDefinition& definition : layer.parameterDefinitions)
			previousDefinitions[definition.id] = definition;

		std::map<std::string, ShaderParameterValue> nextValues;
		for (const ShaderParameterDefinition& nextDefinition : shaderPackage->parameters)
		{
			const auto previousDefinitionIt = previousDefinitions.find(nextDefinition.id);
			const auto previousValueIt = layer.parameterValues.find(nextDefinition.id);
			if (previousDefinitionIt != previousDefinitions.end()
				&& previousValueIt != layer.parameterValues.end()
				&& previousDefinitionIt->second.type == nextDefinition.type)
			{
				ShaderParameterValue preservedValue;
				JsonValue valueJson = ParameterValueToJson(previousDefinitionIt->second, previousValueIt->second);
				std::string normalizeError;
				if (NormalizeAndValidateParameterValue(nextDefinition, valueJson, preservedValue, normalizeError))
				{
					nextValues[nextDefinition.id] = preservedValue;
					continue;
				}
			}
			nextValues[nextDefinition.id] = DefaultValueForDefinition(nextDefinition);
		}

		layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
		layer.packageFingerprint = nextFingerprint;
		layer.parameterDefinitions = shaderPackage->parameters;
		layer.parameterValues = std::move(nextValues);
		buildsToStart.push_back({ layer.id, layer.shaderId });
	}

	error.clear();
	return true;
}
}
