/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "FontAtlasGeneratorBase.hpp"
#include "FontAtlas.hpp"
#include "Base/Logger.hpp"
#include <filesystem>

namespace OpenVulkano::Scene
{
	std::pair<FtLibraryRecPtr, FtFaceRecPtr> 
		FontAtlasGeneratorBase::InitFreetype(const std::variant<std::string, Array<char>>& source)
	{
		FT_Library library;
		auto error = FT_Init_FreeType(&library);
		if (error)
		{
			throw std::runtime_error(fmt::format("Could not initalize freetype library. {}", GetFreetypeErrorDescription(error)));
		}
		FT_Face face;
		if (std::holds_alternative<std::string>(source))
		{
			error = FT_New_Face(library, std::get<0>(source).c_str(), 0, &face);
		}
		else
		{
			auto& arr = std::get<1>(source);
			error = FT_New_Memory_Face(library, (const FT_Byte*) (arr.Data()), arr.Size(), 0, &face);
		}
		if (error == FT_Err_Unknown_File_Format)
		{
			throw std::runtime_error("Unknown font file format\n");
		}
		else if (error)
		{
			throw std::runtime_error(fmt::format("Font file could not be opened or read or it's corrupted. {}", GetFreetypeErrorDescription(error)));
		}

		// some fancy font without unicode charmap
		if (face->charmap == nullptr)
		{
			throw std::runtime_error("Selected font doesn't contain unicode charmap");
		}
		return { FtLibraryRecPtr(library), FtFaceRecPtr(face) };
	}

	std::string FontAtlasGeneratorBase::GetFreetypeErrorDescription(FT_Error error)
	{
		if (const char* s = FT_Error_String(error))
		{
			return s;
		}
		return fmt::format("Error code is {}", error);
	}

	void FontAtlasGeneratorBase::SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size,
	                                          const Math::AABB& aabb, double advance)
	{
		const auto& resolution = m_atlasData->GetTexture()->resolution;
		const double l = aabb.min.x;
		const double r = aabb.max.x;
		const double t = aabb.max.y;
		const double b = aabb.min.y;

		info.pos[0].x = bearing.x;
		info.pos[0].y = size.y - bearing.y;
		info.uv[0].x = l / resolution.x;
		info.uv[0].y = b / resolution.y;

		info.pos[1].x = bearing.x + size.x;
		info.pos[1].y = size.y - bearing.y;
		info.uv[1].x = r / resolution.x;
		info.uv[1].y = b / resolution.y;

		info.pos[2].x = bearing.x + size.x;
		info.pos[2].y = bearing.y;
		info.uv[2].x = r / resolution.x;
		info.uv[2].y = t / resolution.y;

		info.pos[3].x = bearing.x;
		info.pos[3].y = bearing.y;
		info.uv[3].x = l / resolution.x;
		info.uv[3].y = t / resolution.y;

		info.advance = advance;
	}

	std::set<uint32_t> FontAtlasGeneratorBase::LoadAllGlyphs(const std::variant<std::string, Array<char>>& data)
	{
		const auto& [lib, face] = InitFreetype(data);
		std::set<uint32_t> s;
		FT_UInt glyphIndex;
		FT_ULong unicode = FT_Get_First_Char(face.get(), &glyphIndex);
		while (glyphIndex != 0)
		{
			s.insert(unicode);
			unicode = FT_Get_Next_Char(face.get(), unicode, &glyphIndex);
		}
		return s;
	}

}
