#include "FontAtlasBuilder.h"

#include "RuntimeJson.h"

#include <fstream>
#include <sstream>

namespace RenderCadenceCompositor
{
namespace
{
std::string ReadTextFile(const std::filesystem::path& path)
{
	std::ifstream input(path, std::ios::binary);
	if (!input)
		return std::string();
	std::ostringstream buffer;
	buffer << input.rdbuf();
	return buffer.str();
}

const JsonValue* FindObjectValue(const JsonValue& object, const std::string& key)
{
	return object.isObject() ? object.find(key) : nullptr;
}

double NumberMember(const JsonValue& object, const std::string& key, double fallback = 0.0)
{
	const JsonValue* value = FindObjectValue(object, key);
	return value && value->isNumber() ? value->asNumber(fallback) : fallback;
}
}

bool FontAtlasBuilder::LoadAtlasJson(FontAtlasBuildOutput& output, std::string& error) const
{
	const std::string jsonText = ReadTextFile(output.jsonPath);
	if (jsonText.empty())
	{
		error = "Could not read font atlas json: " + output.jsonPath.string();
		return false;
	}

	JsonValue root;
	if (!ParseJson(jsonText, root, error))
		return false;

	const JsonValue* metrics = FindObjectValue(root, "metrics");
	if (metrics)
	{
		output.ascender = NumberMember(*metrics, "ascender", output.ascender);
		output.descender = NumberMember(*metrics, "descender", output.descender);
		output.lineHeight = NumberMember(*metrics, "lineHeight", output.lineHeight);
	}

	const JsonValue* glyphs = FindObjectValue(root, "glyphs");
	if (!glyphs || !glyphs->isArray())
	{
		error = "Font atlas json has no glyph array: " + output.jsonPath.string();
		return false;
	}

	output.glyphsByCodepoint.clear();
	for (const JsonValue& glyphJson : glyphs->asArray())
	{
		if (!glyphJson.isObject())
			continue;

		const unsigned codepoint = static_cast<unsigned>(NumberMember(glyphJson, "unicode", 0.0));
		FontAtlasBuildOutput::Glyph glyph;
		glyph.advance = NumberMember(glyphJson, "advance", 0.0);

		const JsonValue* planeBounds = FindObjectValue(glyphJson, "planeBounds");
		const JsonValue* atlasBounds = FindObjectValue(glyphJson, "atlasBounds");
		if (planeBounds && atlasBounds)
		{
			glyph.planeLeft = NumberMember(*planeBounds, "left", 0.0);
			glyph.planeTop = NumberMember(*planeBounds, "top", 0.0);
			glyph.planeRight = NumberMember(*planeBounds, "right", 0.0);
			glyph.planeBottom = NumberMember(*planeBounds, "bottom", 0.0);
			glyph.atlasLeft = NumberMember(*atlasBounds, "left", 0.0);
			glyph.atlasTop = NumberMember(*atlasBounds, "top", 0.0);
			glyph.atlasRight = NumberMember(*atlasBounds, "right", 0.0);
			glyph.atlasBottom = NumberMember(*atlasBounds, "bottom", 0.0);
			glyph.hasBounds = true;
		}

		output.glyphsByCodepoint[codepoint] = glyph;
	}

	error.clear();
	return true;
}
}
