#include "RuntimeStateJson.h"

#include "RuntimeParameterUtils.h"

namespace
{
std::string ShaderParameterTypeToString(ShaderParameterType type)
{
	switch (type)
	{
	case ShaderParameterType::Float: return "float";
	case ShaderParameterType::Vec2: return "vec2";
	case ShaderParameterType::Color: return "color";
	case ShaderParameterType::Boolean: return "bool";
	case ShaderParameterType::Enum: return "enum";
	case ShaderParameterType::Text: return "text";
	case ShaderParameterType::Trigger: return "trigger";
	}
	return "unknown";
}
}

JsonValue RuntimeStateJson::SerializeLayerStack(const LayerStackStore& layerStack, const ShaderPackageCatalog& shaderCatalog)
{
	std::map<std::string, ShaderPackage> packagesById;
	for (const std::string& packageId : shaderCatalog.PackageOrder())
	{
		ShaderPackage shaderPackage;
		if (shaderCatalog.CopyPackage(packageId, shaderPackage))
			packagesById[packageId] = shaderPackage;
	}
	return SerializeLayerStack(layerStack.Layers(), packagesById);
}

JsonValue RuntimeStateJson::SerializeLayerStack(const std::vector<LayerStackStore::LayerPersistentState>& layerStates, const std::map<std::string, ShaderPackage>& packagesById)
{
	JsonValue layersValue = JsonValue::MakeArray();
	for (const LayerStackStore::LayerPersistentState& layer : layerStates)
	{
		auto shaderIt = packagesById.find(layer.shaderId);
		if (shaderIt == packagesById.end())
			continue;
		const ShaderPackage& shaderPackage = shaderIt->second;

		JsonValue layerValue = JsonValue::MakeObject();
		layerValue.set("id", JsonValue(layer.id));
		layerValue.set("shaderId", JsonValue(layer.shaderId));
		layerValue.set("shaderName", JsonValue(shaderPackage.displayName));
		layerValue.set("bypass", JsonValue(layer.bypass));
		if (shaderPackage.temporal.enabled)
		{
			JsonValue temporal = JsonValue::MakeObject();
			temporal.set("enabled", JsonValue(true));
			temporal.set("historySource", JsonValue(TemporalHistorySourceToString(shaderPackage.temporal.historySource)));
			temporal.set("requestedHistoryLength", JsonValue(static_cast<double>(shaderPackage.temporal.requestedHistoryLength)));
			temporal.set("effectiveHistoryLength", JsonValue(static_cast<double>(shaderPackage.temporal.effectiveHistoryLength)));
			layerValue.set("temporal", temporal);
		}
		if (shaderPackage.feedback.enabled)
		{
			JsonValue feedback = JsonValue::MakeObject();
			feedback.set("enabled", JsonValue(true));
			feedback.set("writePass", JsonValue(shaderPackage.feedback.writePassId));
			layerValue.set("feedback", feedback);
		}

		JsonValue parameters = JsonValue::MakeArray();
		for (const ShaderParameterDefinition& definition : shaderPackage.parameters)
		{
			JsonValue parameter = JsonValue::MakeObject();
			parameter.set("id", JsonValue(definition.id));
			parameter.set("label", JsonValue(definition.label));
			if (!definition.description.empty())
				parameter.set("description", JsonValue(definition.description));
			parameter.set("type", JsonValue(ShaderParameterTypeToString(definition.type)));
			parameter.set("defaultValue", SerializeParameterValue(definition, DefaultValueForDefinition(definition)));

			if (!definition.minNumbers.empty())
			{
				JsonValue minValue = JsonValue::MakeArray();
				for (double number : definition.minNumbers)
					minValue.pushBack(JsonValue(number));
				parameter.set("min", minValue);
			}
			if (!definition.maxNumbers.empty())
			{
				JsonValue maxValue = JsonValue::MakeArray();
				for (double number : definition.maxNumbers)
					maxValue.pushBack(JsonValue(number));
				parameter.set("max", maxValue);
			}
			if (!definition.stepNumbers.empty())
			{
				JsonValue stepValue = JsonValue::MakeArray();
				for (double number : definition.stepNumbers)
					stepValue.pushBack(JsonValue(number));
				parameter.set("step", stepValue);
			}
			if (definition.type == ShaderParameterType::Enum)
			{
				JsonValue options = JsonValue::MakeArray();
				for (const ShaderParameterOption& option : definition.enumOptions)
				{
					JsonValue optionValue = JsonValue::MakeObject();
					optionValue.set("value", JsonValue(option.value));
					optionValue.set("label", JsonValue(option.label));
					options.pushBack(optionValue);
				}
				parameter.set("options", options);
			}
			if (definition.type == ShaderParameterType::Text)
			{
				parameter.set("maxLength", JsonValue(static_cast<double>(definition.maxLength)));
				if (!definition.fontId.empty())
					parameter.set("font", JsonValue(definition.fontId));
			}

			ShaderParameterValue value = DefaultValueForDefinition(definition);
			auto valueIt = layer.parameterValues.find(definition.id);
			if (valueIt != layer.parameterValues.end())
				value = valueIt->second;
			parameter.set("value", SerializeParameterValue(definition, value));
			parameters.pushBack(parameter);
		}

		layerValue.set("parameters", parameters);
		layersValue.pushBack(layerValue);
	}
	return layersValue;
}

JsonValue RuntimeStateJson::SerializeParameterValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
{
	switch (definition.type)
	{
	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());
	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;
	}
	}
	return JsonValue();
}

std::string RuntimeStateJson::TemporalHistorySourceToString(TemporalHistorySource source)
{
	switch (source)
	{
	case TemporalHistorySource::Source:
		return "source";
	case TemporalHistorySource::PreLayerInput:
		return "preLayerInput";
	case TemporalHistorySource::None:
	default:
		return "none";
	}
}
