/*
 * 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/.
 */

#pragma once

#include "Base/UUID.hpp"
#include "Math/Math.hpp"
#include "Math/AABB.hpp"
#include "Math/Pose.hpp"
#include "Math/DenseVector3i.hpp"
#include "Math/Int24.hpp"
#include "Math/Range.hpp"
#include "Math/RGB10A2.hpp"
#include "Math/RGB565.hpp"
#include "Math/RGBA5551.hpp"
#include "Math/Timestamp.hpp"
#include <ryml.hpp>
#include <ryml_std.hpp>
#include <c4/format.hpp>

namespace c4
{
	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Vector2<T>& v)
	{ return ryml::format(buf, "({},{})", v.x, v.y); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Vector3<T>& v)
	{ return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Vector4<T>& v)
	{ return ryml::format(buf, "({},{},{},{})", v.x, v.y, v.z, v.w); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Vector2_SIMD<T>& v)
	{ return ryml::format(buf, "({},{})", v.x, v.y); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Vector3_SIMD<T>& v)
	{ return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Matrix3<T>& m)
	{
		return ryml::format(buf, "({},{},{}),({},{},{}),({},{},{})", m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Matrix3_SIMD<T>& m)
	{ return ryml::format(buf, "({},{},{}),({},{},{}),({},{},{})", m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Matrix4<T>& m)
	{
		return ryml::format(buf, "({},{},{},{}),({},{},{},{}),({},{},{},{}),({},{},{},{})", m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], m[1][2], m[2][2], m[3][2], m[0][3],
							m[1][3], m[2][3], m[3][3]);
	}

	inline size_t to_chars(ryml::substr buf, const OpenVulkano::Math::AABB& bbox)
	{ return ryml::format(buf, "({},{},{}),({},{},{})", bbox.min.x, bbox.min.y, bbox.min.z, bbox.max.x, bbox.max.y, bbox.max.z); }

	template<class T>
	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::Pose<T>& pose)
	{ return ryml::format(buf, "({},{},{}),({},{},{},{})", pose.GetPosition().x, pose.GetPosition().y, pose.GetPosition().z, pose.GetOrientation().x, pose.GetOrientation().y, pose.GetOrientation().z, pose.GetOrientation().w); }

	template<class T>
	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::Vector2<T>* v)
	{
		size_t ret = ryml::unformat(buf, "({},{})", v->x, v->y);
		return ret != ryml::yml::npos;
	}

	template<class T>
	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::Vector3<T>* v)
	{
		size_t ret = ryml::unformat(buf, "({},{},{})", v->x, v->y, v->z);
		return ret != ryml::yml::npos;
	}

	template<class T>
	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::Vector4<T>* v)
	{
		size_t ret = ryml::unformat(buf, "({},{},{},{})", v->x, v->y, v->z, v->w);
		return ret != ryml::yml::npos;
	}

	template<class T>
	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::Vector2_SIMD<T>* v)
	{
		size_t ret = ryml::unformat(buf, "({},{})", v->x, v->y);
		return ret != ryml::yml::npos;
	}

	template<class T>
	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::Vector3_SIMD<T>* v)
	{
		size_t ret = ryml::unformat(buf, "({},{},{})", v->x, v->y, v->z);
		return ret != ryml::yml::npos;
	}

	template<class T>
	size_t from_chars(ryml::csubstr buf, OpenVulkano::Math::Matrix3<T>* mp)
	{
		auto& m = *mp;
		size_t ret = ryml::unformat(buf, "({},{},{}),({},{},{}),({},{},{})", m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);
		return ret != ryml::yml::npos;
	}

	template<class T>
	size_t from_chars(ryml::csubstr buf, OpenVulkano::Math::Matrix3_SIMD<T>* mp)
	{
		auto& m = *mp;
		size_t ret = ryml::unformat(buf, "({},{},{}),({},{},{}),({},{},{})", m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);
		return ret != ryml::yml::npos;
	}

	template<class T>
	size_t from_chars(ryml::csubstr buf, OpenVulkano::Math::Matrix4<T>* mp)
	{
		auto& m = *mp;
		size_t ret = ryml::unformat(buf, "({},{},{},{}),({},{},{},{}),({},{},{},{}),({},{},{},{})", m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], m[1][2], m[2][2], m[3][2], m[0][3],
							m[1][3], m[2][3], m[3][3]);
		return ret != ryml::yml::npos;
	}

	inline bool from_chars(ryml::csubstr buf, OpenVulkano::Math::AABB* bbox)
	{
		size_t ret = ryml::unformat(buf, "({},{},{}),({},{},{})", bbox->min.x, bbox->min.y, bbox->min.z, bbox->max.x, bbox->max.y, bbox->max.z);
		return ret != ryml::yml::npos;
	}

	template<class T>
	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::Pose<T>* pose)
	{
		size_t ret = ryml::unformat(buf, "({},{},{}),({},{},{},{})", pose->GetPosition().x, pose->GetPosition().y, pose->GetPosition().z, pose->GetOrientation().x, pose->GetOrientation().y, pose->GetOrientation().z, pose->GetOrientation().w);
		return ret != ryml::yml::npos;
	}

	inline size_t to_chars(ryml::substr buf, const OpenVulkano::UUID& uuid)
	{
		return ryml::format(buf, "{}", uuid.string());
	}

	inline bool from_chars(ryml::csubstr buf, OpenVulkano::UUID* uuid)
	{
		uuid->assign(buf.str);
		return true;
	}

	template<typename T, bool REDUCE_Y_RESOLUTION, bool ASSERT_INPUT_VALUES, int BITS_PER_COMPONENT>
	inline size_t to_chars(
	ryml::substr buf,
	const OpenVulkano::Math::DenseVector3i<T, REDUCE_Y_RESOLUTION, ASSERT_INPUT_VALUES, BITS_PER_COMPONENT>& vec)
	{
		return ryml::format(buf, "({},{},{})", vec.X(), vec.Y(), vec.Z());
	}

	template<typename T, bool REDUCE_Y_RESOLUTION, bool ASSERT_INPUT_VALUES, int BITS_PER_COMPONENT>
	inline bool
	from_chars(ryml::csubstr buf,
	           OpenVulkano::Math::DenseVector3i<T, REDUCE_Y_RESOLUTION, ASSERT_INPUT_VALUES, BITS_PER_COMPONENT>* vec)
	{
		int x, y, z;
		size_t ret = ryml::unformat(buf, "({},{},{})", x, y, z);
		if (ret != ryml::yml::npos)
		{
			*vec = DenseVector3i<T, REDUCE_Y_RESOLUTION, ASSERT_INPUT_VALUES, BITS_PER_COMPONENT>(x, y, z);
			return true;
		}
		return false;
	}

	size_t to_chars(c4::substr buf, const OpenVulkano::int24& value)
	{
		int intValue = static_cast<int>(value);
		return ryml::format(buf, "{}", intValue);
	}

	bool from_chars(c4::csubstr buf, OpenVulkano::int24* value)
	{
		int intValue;
		size_t ret = ryml::unformat(buf, "{}", intValue);
		if (ret != ryml::yml::npos)
		{
			*value = OpenVulkano::int24(intValue);
			return true;
		}
		return false;
	}

	template<typename T> size_t to_chars(c4::substr buf, const OpenVulkano::Math::Range<T>& range)
	{
		return ryml::format(buf, "[{},{}]", range.GetMin(), range.GetMax());
	}

	template<typename T> bool from_chars(c4::csubstr buf, OpenVulkano::Math::Range<T>* range)
	{
		T minVal, maxVal;
		size_t ret = ryml::unformat(buf, "[{},{}]", minVal, maxVal);
		if (ret != ryml::yml::npos)
		{
			*range = Range<T>(minVal, maxVal);
			return true;
		}
		return false;
	}

	template<typename T> size_t to_chars(c4::substr buf, const OpenVulkano::Math::RGB10A2<T>& color)
	{
		return ryml::format(buf, "[{},{},{},{}]", color.r, color.g, color.b, color.a);
	}

	template<typename T> bool from_chars(c4::csubstr buf, OpenVulkano::Math::RGB10A2<T>* color)
	{
		T r, g, b, a;
		size_t ret = ryml::unformat(buf, "[{},{},{},{}]", r, g, b, a);
		if (ret != ryml::yml::npos)
		{
			color->r = r;
			color->g = g;
			color->b = b;
			color->a = a;
			return true;
		}
		return false;
	}

	size_t to_chars(ryml::substr buf, const OpenVulkano::Math::RGB565& rgb)
	{
		return ryml::format(buf, "[{},{},{}]", rgb.r, rgb.g, rgb.b);
	}

	bool from_chars(ryml::csubstr buf, OpenVulkano::Math::RGB565* rgb)
	{
		int r, g, b;
		size_t ret = ryml::unformat	(buf, "[{},{},{}]", r, g, b);
		rgb->r = r;
		rgb->g = g;
		rgb->b = b;
		return ret != ryml::yml::npos;
	}

	size_t to_chars(c4::substr buf, const OpenVulkano::Math::RGBA5551& color)
	{
		return ryml::format(buf, "[{},{},{},{}]", color.r, color.g, color.b, color.a);
	}

	bool from_chars(c4::csubstr buf, OpenVulkano::Math::RGBA5551* color)
	{
		int r, g, b, a;
		size_t ret = ryml::unformat(buf, "[{},{},{},{}]", r, g, b, a);
		if (ret != ryml::yml::npos)
		{
			color->r = r;
			color->g = g;
			color->b = b;
			color->a = a;
			return true;
		}
		return false;
	}

	size_t to_chars(c4::substr buf, const OpenVulkano::Math::Timestamp& ts)
	{
		return ryml::format(buf, "{}", ts.GetNanos());
	}

	bool from_chars(c4::csubstr buf, OpenVulkano::Math::Timestamp* ts)
	{
		uint64_t nanos;
		size_t ret = ryml::unformat(buf, "{}", nanos);
		if (ret != ryml::yml::npos)
		{
			*ts = OpenVulkano::Math::Timestamp(nanos);
			return true;
		}
		return false;
	}
}