/*
 * 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 "ArSessionMetadata.hpp"
#include <pugixml.hpp>
#include <yaml-cpp/yaml.h>
#include <sstream>
#include <filesystem>

namespace OpenVulkano::AR
{
	ArSessionMetadata ArSessionMetadata::FromXML(const std::string& filePath)
	{
		pugi::xml_document doc;
		pugi::xml_parse_result result = doc.load_file(filePath.c_str());
		if (!result) throw std::runtime_error("Failed to read metadata xml file.");
		pugi::xml_node metaNode = doc.child("arPlatformInfo");
		const char* type = metaNode.child("type").text().as_string();
		int minRange = metaNode.child("minConfidence").text().as_int();
		int maxRange = metaNode.child("maxConfidence").text().as_int();
		const char* depthType = metaNode.child("depthType").text().as_string();
		uint32_t resX = 1920, resY = 1440;
		auto resNode = metaNode.child("imageResolution");
		if (!resNode.empty())
		{
			resX = resNode.child("width").text().as_uint();
			resY = resNode.child("height").text().as_uint();
		}
		float frameRate = metaNode.child("frameRate").text().as_float(60);
		return { ArType::GetFromHumanReadableName(type),
				 ArDepthFormat::GetFromAltName(depthType).value_or(ArDepthFormat::UNAVAILABLE),
				 Math::Range<uint8_t>(minRange, maxRange),
				 Math::Vector2ui(resX, resY),
				 frameRate
		};
	}

	ArSessionMetadata ArSessionMetadata::FromYaml(const std::string& filePath)
	{
		YAML::Node meta = YAML::LoadFile(filePath);

		return {
				ArType::GetFromName(meta["Type"].Scalar()),
				ArDepthFormat::GetFromName(meta["DepthType"].Scalar()).value_or(ArDepthFormat::UNAVAILABLE),
				Math::Range<uint8_t>(meta["MinConfidence"].as<uint8_t>(), meta["MaxConfidence"].as<uint8_t>()),
				Math::Vector2ui(1), // TODO load
				meta["FrameRate"].as<float>(60)
		};
	}

	ArSessionMetadata::ArSessionMetadata(const std::string& dirPath)
	{
		std::optional<ArSessionMetadata> metaFromFile;
		std::filesystem::path rPath(dirPath);
		if (!is_directory(rPath)) throw std::runtime_error("Ar recording path must be a directory!");
		std::filesystem::path xmlInfoPath(dirPath + "/arType.info");
		if (std::filesystem::exists(xmlInfoPath))
		{
			metaFromFile = FromXML(xmlInfoPath.string());
		}
		xmlInfoPath = { dirPath + "/ArRecording.xml" };
		if (std::filesystem::exists(xmlInfoPath))
		{
			metaFromFile = FromXML(xmlInfoPath.string());
		}
		std::filesystem::path ymlInfoPath(dirPath + "/ArRecording.yml");
		if (std::filesystem::exists(ymlInfoPath))
		{
			metaFromFile = FromYaml(ymlInfoPath.string());
		}
		if (!metaFromFile) throw std::runtime_error("Ar recording dir is missing platform metadata file!");
		type = metaFromFile->type;
		depthFormat = metaFromFile->depthFormat;
		confidenceRange = metaFromFile->confidenceRange;
		playback = true;
	}

	std::string ArSessionMetadata::ToXML() const
	{
		std::stringstream ss;
		ss << "<arPlatformInfo>\n\t<type>" << type.GetHumanReadableName() << "</type>\n\t<minConfidence>";
		ss << static_cast<int>(confidenceRange.min) << "</minConfidence>\n\t<maxConfidence>";
		ss << static_cast<int>(confidenceRange.max) << "</maxConfidence>\n\t<depthType>";
		ss << depthFormat.GetAltName() << "</depthType>";
		ss << "\n\t<imageResolution><width>" << imageResolution.x << "</width><height>" << imageResolution.y << "</height></imageResolution>";
		ss << "\n\t<frameRate>" << frameRate << "</frameRate>";
		ss << "\n</arPlatformInfo>";
		return ss.str();
	}

	std::string ArSessionMetadata::ToYaml() const
	{
		std::stringstream ss;
		ss << "Type: " << type.GetName() << "\nDepthType: " << depthFormat.GetName();
		ss << "\nMinConfidence: " << static_cast<int>(confidenceRange.min);
		ss << "\nMaxConfidence: " << static_cast<int>(confidenceRange.max);
		ss << "\nResolution: \"(" << imageResolution.x << "," << imageResolution.y << ")\"";
		ss << "\nFrameRate: " << frameRate;
		return ss.str();
	}
}