/*
 * Copyright 2015 - 2025 gary@drinkingtea.net
 *
 * 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 <ox/std/memory.hpp>
#include <ox/std/serialize.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/typetraits.hpp>
#include <ox/std/types.hpp>
#include <ox/std/utility.hpp>
#include <ox/std/vector.hpp>

#include "def.hpp"
#include "desctypes.hpp"
#include "fieldcounter.hpp"
#include "metadata.hpp"
#include "optype.hpp"
#include "typenamecatcher.hpp"
#include "types.hpp"
#include "typestore.hpp"

namespace ox {

class ModelObject;
class ModelUnion;
class ModelValue;
class ModelValueArray;
class ModelValueVector;

class ModelValue {
	public:
		enum class Type {
			Undefined,
			Bool,
			UnsignedInteger8,
			UnsignedInteger16,
			UnsignedInteger32,
			UnsignedInteger64,
			SignedInteger8,
			SignedInteger16,
			SignedInteger32,
			SignedInteger64,
			String,
			Object,
			Union,
			Vector,
			InlineArray,
		};

	private:
		Type m_type = Type::Undefined;
		union {
			bool b;
			int8_t i8;
			uint8_t ui8;
			int16_t i16;
			uint16_t ui16;
			int32_t i32;
			uint32_t ui32;
			int64_t i64;
			uint64_t ui64 = 0;
			String *str;
			ModelObject *obj;
			ModelUnion *uni;
			ModelValueVector *vec;
			ModelValueArray *array;
		} m_data;

		template<typename T>
		static consteval Type getType() noexcept {
			using U = typename ox::remove_reference<T>::type;
			if constexpr(is_bool_v<U>) {
				return Type::Bool;
			} else if constexpr(is_integer_v<U>) {
				if constexpr(!is_signed_v<U> && sizeof(U) == 1) {
					return Type::UnsignedInteger8;
				} else if constexpr(!is_signed_v<U> && sizeof(U) == 2) {
					return Type::UnsignedInteger16;
				} else if constexpr(!is_signed_v<U> && sizeof(U) == 4) {
					return Type::UnsignedInteger32;
				} else if constexpr(!is_signed_v<U> && sizeof(U) == 8) {
					return Type::UnsignedInteger64;
				} else if constexpr(is_signed_v<U> && sizeof(U) == 1) {
					return Type::SignedInteger8;
				} else if constexpr(is_signed_v<U> && sizeof(U) == 2) {
					return Type::SignedInteger16;
				} else if constexpr(is_signed_v<U> && sizeof(U) == 4) {
					return Type::SignedInteger32;
				} else if constexpr(is_signed_v<U> && sizeof(U) == 8) {
					return Type::SignedInteger64;
				}
			} else if constexpr(is_same_v<U, ModelUnion>) {
				return Type::Union;
			} else if constexpr(is_same_v<U, ModelObject>) {
				return Type::Object;
			} else if constexpr(isBasicString_v<U> || isIString_v<U>) {
				return Type::String;
			} else if constexpr(is_same_v<U, ModelValueVector>) {
				return Type::Vector;
			} else if constexpr(is_same_v<U, ModelValueArray>) {
				return Type::InlineArray;
			} else {
				return Type::Undefined;
			}
		}

		template<Type type>
		static constexpr auto &getValue(auto &t) noexcept {
			if constexpr(type == Type::Bool) {
				return t.m_data.b;
			} else if constexpr(type == Type::UnsignedInteger8) {
				return t.m_data.ui8;
			} else if constexpr(type == Type::UnsignedInteger16) {
				return t.m_data.ui16;
			} else if constexpr(type == Type::UnsignedInteger32) {
				return t.m_data.ui32;
			} else if constexpr(type == Type::UnsignedInteger64) {
				return t.m_data.ui64;
			} else if constexpr(type == Type::SignedInteger8) {
				return t.m_data.i8;
			} else if constexpr(type == Type::SignedInteger16) {
				return t.m_data.i16;
			} else if constexpr(type == Type::SignedInteger32) {
				return t.m_data.i32;
			} else if constexpr(type == Type::SignedInteger64) {
				return t.m_data.i64;
			} else if constexpr(type == Type::String) {
				return *t.m_data.str;
			} else if constexpr(type == Type::Union) {
				return *t.m_data.uni;
			} else if constexpr(type == Type::Object) {
				return *t.m_data.obj;
			} else if constexpr(type == Type::Vector) {
				return *t.m_data.vec;
			} else if constexpr(type == Type::InlineArray) {
				return *t.m_data.array;
			} else {
				return t.m_data.i32;
			}
		}

	public:
		constexpr ModelValue() noexcept = default;

		constexpr ModelValue(const ModelValue &other) noexcept;

		constexpr ModelValue(ModelValue &&other) noexcept;

		template<typename T>
		explicit constexpr ModelValue(const T &val) noexcept
			requires(!ox::is_same_v<ox::remove_reference_t<T>, ModelValue>);

		template<typename T>
		explicit constexpr ModelValue(T &&val) noexcept
			requires(!ox::is_same_v<ox::remove_reference_t<T>, ModelValue>);

		constexpr ~ModelValue() noexcept;

		template<typename T>
		[[nodiscard]]
		constexpr const auto &get() const noexcept {
			constexpr auto type = getType<T>();
			if (m_type != type) [[unlikely]] {
				ox::panic("invalid cast");
			}
			return getValue<type>(*this);
		}

		template<typename T>
		[[nodiscard]]
		constexpr auto &get() noexcept {
			constexpr auto type = getType<T>();
			if (m_type != type) [[unlikely]] {
				ox::panic("invalid cast");
			}
			return getValue<type>(*this);
		}

		[[nodiscard]]
		constexpr Type type() const noexcept;

		constexpr Error setType(
				DescriptorType const *type,
				SubscriptStack const& = {},
				int subscriptLevels = 0) noexcept;

		template<typename T>
		constexpr Error setType() noexcept;

		template<typename T>
		constexpr Error set(const T &v) noexcept;

		template<typename T>
		constexpr Error set(T &&v) noexcept;

		constexpr ModelValue &operator=(const ModelValue &val) noexcept;

		constexpr ModelValue &operator=(ModelValue &&val) noexcept;

	private:
		constexpr void freeResources() noexcept;

};

class ModelValueArray {
	private:
		Vector<ModelValue> m_vec;
		const DescriptorType *m_type = nullptr;
		int m_typeSubscriptLevels = 0;
		SubscriptStack m_subscriptStack;
		String m_typeName;
		int m_typeVersion = 0;

	public:
		constexpr explicit ModelValueArray() noexcept = default;

		constexpr ModelValueArray(ModelValueArray const&other) noexcept {
			for (auto &v : other.m_vec) {
				m_vec.emplace_back(v);
			}
			m_typeName = other.m_typeName;
			m_typeVersion = other.m_typeVersion;
		}

		constexpr ModelValueArray(ModelValueArray &&other) noexcept {
			m_vec = std::move(other.m_vec);
			m_typeName = std::move(other.m_typeName);
			m_typeVersion = other.m_typeVersion;
		}

		constexpr ox::Error setSize(std::size_t sz) noexcept {
			const auto oldSz = m_vec.size();
			m_vec.resize(sz);
			if (sz > oldSz) {
				for (auto i = oldSz; i < sz; ++i) {
					OX_RETURN_ERROR(m_vec[i].setType(m_type, m_subscriptStack, m_typeSubscriptLevels));
				}
			}
			return {};
		}

		[[nodiscard]]
		constexpr auto data() const noexcept {
			return m_vec.data();
		}

		[[nodiscard]]
		constexpr auto data() noexcept {
			return m_vec.data();
		}

		constexpr static ox::Result<ModelValueArray> make(size_t sz) noexcept {
			ox::Result<ModelValueArray> out;
			out.error = out.value.setSize(sz);
			return out;
		}

		[[nodiscard]]
		constexpr auto &get() noexcept {
			return m_vec;
		}

		[[nodiscard]]
		constexpr auto const&get() const noexcept {
			return m_vec;
		}

		constexpr Error setType(
				DescriptorType const *type,
				SubscriptStack subscriptStack,
				int subscriptLevels) noexcept {
			oxAssert(subscriptLevels <= static_cast<int>(subscriptStack.size()), "subscript level mismatch");
			m_type = type;
			m_typeSubscriptLevels = subscriptLevels;
			m_subscriptStack = std::move(subscriptStack);
			return {};
		}

		[[nodiscard]]
		constexpr String const&typeName() const noexcept {
			return m_typeName;
		}

		[[nodiscard]]
		constexpr int typeVersion() const noexcept {
			return m_typeVersion;
		}

		[[nodiscard]]
		constexpr std::size_t size() const noexcept {
			return m_vec.size();
		}

		constexpr auto &operator[](std::size_t i) noexcept {
			return m_vec[i];
		}

		constexpr auto &operator[](std::size_t i) const noexcept {
			return m_vec[i];
		}

		[[nodiscard]]
		auto begin() noexcept {
			return m_vec.begin();
		}

		[[nodiscard]]
		auto begin() const noexcept {
			return m_vec.cbegin();
		}

		[[nodiscard]]
		auto cbegin() const noexcept {
			return m_vec.cbegin();
		}

		[[nodiscard]]
		auto rbegin() noexcept {
			return m_vec.rbegin();
		}

		[[nodiscard]]
		auto crbegin() const noexcept {
			return m_vec.crbegin();
		}

		[[nodiscard]]
		auto end() noexcept {
			return m_vec.end();
		}

		[[nodiscard]]
		auto end() const noexcept {
			return m_vec.cend();
		}

		[[nodiscard]]
		auto cend() const noexcept {
			return m_vec.cend();
		}

		[[nodiscard]]
		auto rend() noexcept {
			return m_vec.rend();
		}

		[[nodiscard]]
		auto crend() const noexcept {
			return m_vec.crend();
		}

};

template<>
[[nodiscard]]
consteval bool isArray(ModelValueArray*) noexcept {
	return true;
}

class ModelValueVector {
	private:
		Vector<ModelValue> m_vec;
		const DescriptorType *m_type = nullptr;
		int m_typeSubscriptLevels = 0;
		SubscriptStack m_subscriptStack;
		String m_typeName;
		int m_typeVersion = 0;

	public:
		constexpr ModelValueVector() noexcept = default;

		constexpr ModelValueVector(const ModelValueVector &other) noexcept;

		constexpr ModelValueVector(ModelValueVector &&other) noexcept;

		constexpr ModelValueVector &operator=(const ModelValueVector &other) noexcept;

		constexpr ModelValueVector &operator=(ModelValueVector &&other) noexcept;

		[[nodiscard]]
		constexpr auto data() const noexcept {
			return m_vec.data();
		}

		[[nodiscard]]
		constexpr auto data() noexcept {
			return m_vec.data();
		}

		constexpr ox::Error resize(std::size_t sz) noexcept {
			const auto oldSz = m_vec.size();
			m_vec.resize(sz);
			if (sz > oldSz) {
				for (auto i = oldSz; i < sz; ++i) {
					OX_RETURN_ERROR(m_vec[i].setType(m_type, m_subscriptStack, m_typeSubscriptLevels));
				}
			}
			return {};
		}

		[[nodiscard]]
		constexpr auto &get() noexcept {
			return m_vec;
		}

		[[nodiscard]]
		constexpr const auto &get() const noexcept {
			return m_vec;
		}

		constexpr Error setType(
				DescriptorType const *type,
				SubscriptStack subscriptStack,
				int subscriptLevels) noexcept {
			oxAssert(subscriptLevels <= static_cast<int>(subscriptStack.size()), "subscript level mismatch");
			m_type = type;
			m_typeSubscriptLevels = subscriptLevels;
			m_subscriptStack = std::move(subscriptStack);
			return {};
		}

		[[nodiscard]]
		constexpr const String &typeName() const noexcept {
			return m_typeName;
		}

		[[nodiscard]]
		constexpr int typeVersion() const noexcept {
			return m_typeVersion;
		}

		[[nodiscard]]
		constexpr std::size_t size() const noexcept {
			return m_vec.size();
		}

		constexpr auto &operator[](std::size_t i) noexcept {
			return m_vec[i];
		}

		constexpr auto &operator[](std::size_t i) const noexcept {
			return m_vec[i];
		}

		[[nodiscard]]
		auto begin() noexcept {
			return m_vec.begin();
		}

		[[nodiscard]]
		auto begin() const noexcept {
			return m_vec.cbegin();
		}

		[[nodiscard]]
		auto cbegin() const noexcept {
			return m_vec.cbegin();
		}

		[[nodiscard]]
		auto rbegin() noexcept {
			return m_vec.rbegin();
		}

		[[nodiscard]]
		auto crbegin() const noexcept {
			return m_vec.crbegin();
		}

		[[nodiscard]]
		auto end() noexcept {
			return m_vec.end();
		}

		[[nodiscard]]
		auto end() const noexcept {
			return m_vec.cend();
		}

		[[nodiscard]]
		auto cend() const noexcept {
			return m_vec.cend();
		}

		[[nodiscard]]
		auto rend() noexcept {
			return m_vec.rend();
		}

		[[nodiscard]]
		auto crend() const noexcept {
			return m_vec.crend();
		}

};

consteval bool isVector(const ModelValueVector*) noexcept {
	return true;
}

constexpr Error model(auto *h, CommonPtrWith<ModelObject> auto *obj) noexcept;
constexpr Error model(auto *h, CommonPtrWith<ModelUnion> auto *obj) noexcept;


class ModelObject {
	friend constexpr Error model(auto *h, CommonPtrWith<ModelObject> auto *obj) noexcept;
	public:
		struct Field {
			String name;
			ModelValue value;
		};
	protected:
		OX_MODEL_FRIEND(ModelObject);
		friend ModelValue;
		Vector<UPtr<Field>> m_fieldsOrder;
		HashMap<String, ModelValue*> m_fields;
		const DescriptorType *m_type = nullptr;

	public:
		constexpr ModelObject() noexcept = default;

		constexpr ModelObject(const ModelObject &other) noexcept {
			for (const auto &f : other.m_fieldsOrder) {
				auto &field = m_fieldsOrder.emplace_back(new Field{ox::String(f->name), f->value});
				m_fields[field->name] = &field->value;
			}
			m_type = other.m_type;
		}

		constexpr ModelObject(ModelObject &&other) noexcept {
			m_fields = std::move(other.m_fields);
			m_type = other.m_type;
			m_fieldsOrder = std::move(other.m_fieldsOrder);
		}

		[[nodiscard]]
		constexpr auto begin() noexcept {
			return m_fieldsOrder.begin();
		}

		[[nodiscard]]
		constexpr auto end() noexcept {
			return m_fieldsOrder.end();
		}

		[[nodiscard]]
		constexpr auto begin() const noexcept {
			return m_fieldsOrder.begin();
		}

		[[nodiscard]]
		constexpr auto end() const noexcept {
			return m_fieldsOrder.end();
		}

		[[nodiscard]]
		constexpr auto rbegin() noexcept {
			return m_fieldsOrder.rbegin();
		}

		[[nodiscard]]
		constexpr auto rend() noexcept {
			return m_fieldsOrder.rend();
		}

		[[nodiscard]]
		constexpr auto rbegin() const noexcept {
			return m_fieldsOrder.rbegin();
		}

		[[nodiscard]]
		constexpr auto rend() const noexcept {
			return m_fieldsOrder.rend();
		}

		[[nodiscard]]
		constexpr auto cbegin() const noexcept {
			return m_fieldsOrder.cbegin();
		}

		[[nodiscard]]
		constexpr auto cend() const noexcept {
			return m_fieldsOrder.cend();
		}

		[[nodiscard]]
		constexpr auto crbegin() const noexcept {
			return m_fieldsOrder.crbegin();
		}

		[[nodiscard]]
		constexpr auto crend() const noexcept {
			return m_fieldsOrder.crend();
		}

		constexpr auto &operator=(const ModelObject &other) noexcept {
			if (&other == this) [[unlikely]] {
				return *this;
			}
			for (const auto &f : other.m_fieldsOrder) {
				auto &field = m_fieldsOrder.emplace_back(new Field{ox::String(f->name), f->value});
				m_fields[field->name] = &field->value;
			}
			m_type = other.m_type;
			return *this;
		}

		constexpr auto &operator=(ModelObject &&other) noexcept {
			if (&other == this) [[unlikely]] {
				return *this;
			}
			m_type = other.m_type;
			m_fields = std::move(other.m_fields);
			m_fieldsOrder = std::move(other.m_fieldsOrder);
			return *this;
		}

		template<typename T>
		constexpr auto set(const std::size_t &i, T &&val) noexcept {
			auto &f = *m_fieldsOrder[i];
			f = val;
		}

		constexpr Result<const ModelValue*> get(StringView const&k) const noexcept {
			if (m_fields.contains(k)) {
				return *m_fields.at(k).value;
			}
			return ox::Error(1);
		}

		template<typename T>
		constexpr Error set(const String &k, T &&val) noexcept {
			OX_REQUIRE(t, m_fields.at(k));
			*t = ox::forward<T>(val);
			return {};
		}

		constexpr ox::Result<ModelValue*> at(StringView const&k) noexcept {
			OX_REQUIRE(v, m_fields.at(k));
			return *v;
		}

		constexpr auto &operator[](const std::size_t i) noexcept {
			return *m_fieldsOrder[i];
		}

		[[nodiscard]]
		constexpr StringCR typeName() const noexcept {
			return m_type->typeName;
		}

		[[nodiscard]]
		constexpr int typeVersion() const noexcept {
			return m_type->typeVersion;
		}

		[[nodiscard]]
		constexpr auto type() const noexcept {
			return m_type;
		}

		constexpr Error setType(const DescriptorType *type) noexcept {
			if (type->primitiveType != PrimitiveType::Struct && type->primitiveType != PrimitiveType::Union) {
				return ox::Error(1, "Cannot load a non-struct type to ModelObject");
			}
			m_type = type;
			for (const auto &f : type->fieldList) {
				auto field = make_unique<Field>();
				field->name = f.fieldName;
				OX_RETURN_ERROR(field->value.setType(f.type, f.subscriptStack, f.subscriptLevels));
				m_fields[field->name] = &field->value;
				m_fieldsOrder.emplace_back(std::move(field));
			}
			return {};
		}

};

class ModelUnion {
	protected:
		struct Field {
			int idx = -1;
			String name;
			ModelValue value;
		};
		friend constexpr Error model(auto *h, CommonPtrWith<ModelUnion> auto *obj) noexcept;
		friend ModelValue;
		Vector<UPtr<Field>> m_fieldsOrder;
		HashMap<String, Field*> m_fields;
		const DescriptorType *m_type = nullptr;
		int m_unionIdx = -1;

	private:
		constexpr ModelUnion() noexcept = default;

	public:
		constexpr ModelUnion(const ModelUnion &other) noexcept {
			for (auto i = 0; const auto &f : other.m_fieldsOrder) {
				auto &field = m_fieldsOrder.emplace_back(new Field{i, ox::String(f->name), f->value});
				m_fields[field->name] = field.get();
				++i;
			}
			m_type = other.m_type;
			m_unionIdx = other.m_unionIdx;
		}

		constexpr ModelUnion(ModelUnion &&other) noexcept {
			m_fieldsOrder = std::move(other.m_fieldsOrder);
			m_fields = std::move(other.m_fields);
			m_type = other.m_type;
			m_unionIdx = other.m_unionIdx;
		}

		static constexpr Result<UPtr<ModelUnion>> make(const DescriptorType *type) noexcept {
			UPtr<ModelUnion> out(new ModelUnion);
			OX_RETURN_ERROR(out->setType(type));
			return out;
		}

		static constexpr Result<UPtr<ModelUnion>> make(const ModelUnion &other) noexcept {
			return UPtr<ModelUnion>(new ModelUnion(other));
		}

		constexpr ox::Result<ModelValue*> at(StringView const&k) noexcept {
			OX_REQUIRE(v, m_fields.at(k));
			return &(*v)->value;
		}

		constexpr auto &operator[](const std::size_t i) noexcept {
			return m_fieldsOrder[i]->value;
		}

		constexpr void setActiveField(int i) noexcept {
			m_unionIdx = i;
		}

		constexpr auto set(int i, auto val) noexcept {
			m_unionIdx = i;
			return m_fieldsOrder[static_cast<std::size_t>(i)]->value.set(val);
		}

		constexpr void set(std::size_t i, auto val) noexcept {
			m_unionIdx = static_cast<int>(i);
			*m_fieldsOrder[i] = val;
		}

		[[nodiscard]]
		constexpr Result<const ModelValue*> get(std::size_t i) const noexcept {
			if (i < m_fieldsOrder.size()) {
				return &m_fieldsOrder[i]->value;
			}
			return {};
		}

		[[nodiscard]]
		constexpr Result<const ModelValue*> get(StringView const&k) const noexcept {
			OX_REQUIRE(t, m_fields.at(k));
			return &(*t)->value;
		}

		[[nodiscard]]
		constexpr int getKeyIdx(const auto &k) const noexcept {
			for (auto i = 0; const auto &f : m_fieldsOrder) {
				if (f->name == k) {
					return i;
				}
				++i;
			}
			return -1;
		}

		[[nodiscard]]
		constexpr const String &typeName() const noexcept {
			return m_type->typeName;
		}

		[[nodiscard]]
		constexpr int typeVersion() const noexcept {
			return m_type->typeVersion;
		}

		constexpr Error setType(const DescriptorType *type) noexcept {
			if (type->primitiveType != PrimitiveType::Struct && type->primitiveType != PrimitiveType::Union) {
				return ox::Error(1, "Cannot load a non-struct type to ModelUnion");
			}
			m_fields.clear();
			m_fieldsOrder.clear();
			m_type = type;
			for (auto i = 0; const auto &f : type->fieldList) {
				auto field = make_unique<Field>();
				field->name = f.fieldName;
				field->idx = i;
				OX_RETURN_ERROR(field->value.setType(f.type, SubscriptStack{static_cast<size_t>(f.subscriptLevels)}, f.subscriptLevels));
				m_fields[field->name] = field.get();
				m_fieldsOrder.emplace_back(std::move(field));
				++i;
			}
			return {};
		}

		[[nodiscard]]
		constexpr auto fieldCount() const noexcept {
			return m_fields.size();
		}

		[[nodiscard]]
		constexpr auto unionIdx() const noexcept {
			return m_unionIdx;
		}

};

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t sizeOf(ModelValueArray const *v) noexcept {
	return sizeOf<PlatSpec>(&(*v)[0]) * v->size();
}

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t sizeOf(const ModelValueVector*) noexcept {
	VectorMemMap<PlatSpec> v;
	return sizeOf<PlatSpec>(&v);
}

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t sizeOf(const ModelUnion*) noexcept {
	VectorMemMap<PlatSpec> v;
	return sizeOf<PlatSpec>(&v);
}

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t sizeOf(const ModelValue *t) noexcept {
	std::size_t size = 0;
	switch (t->type()) {
		case ModelValue::Type::Bool:
			size = sizeof(t->get<bool>());
			break;
		case ModelValue::Type::Undefined:
			size = 1;
			break;
		case ModelValue::Type::UnsignedInteger8:
			size = sizeof(t->get<uint8_t>());
			break;
		case ModelValue::Type::UnsignedInteger16:
			size = sizeof(t->get<uint16_t>());
			break;
		case ModelValue::Type::UnsignedInteger32:
			size = sizeof(t->get<uint32_t>());
			break;
		case ModelValue::Type::UnsignedInteger64:
			size = sizeof(t->get<uint64_t>());
			break;
		case ModelValue::Type::SignedInteger8:
			size = sizeof(t->get<int8_t>());
			break;
		case ModelValue::Type::SignedInteger16:
			size = sizeof(t->get<int16_t>());
			break;
		case ModelValue::Type::SignedInteger32:
			size = sizeof(t->get<int32_t>());
			break;
		case ModelValue::Type::SignedInteger64:
			size = sizeof(t->get<int64_t>());
			break;
		case ModelValue::Type::String:
			size = sizeOf<PlatSpec>(&t->get<ox::String>());
			break;
		case ModelValue::Type::Object:
			size = sizeOf<PlatSpec>(&t->get<ox::ModelObject>());
			break;
		case ModelValue::Type::Union:
			size = sizeOf<PlatSpec>(&t->get<ox::ModelUnion>());
			break;
		case ModelValue::Type::Vector:
			size = sizeOf<PlatSpec>(&t->get<ox::ModelValueVector>());
			break;
		case ModelValue::Type::InlineArray:
		{
			auto &list = t->get<ox::ModelValueArray>();
			size = sizeOf<PlatSpec>(&list[0]) * list.size();
			break;
		}
	}
	return size;
}

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t alignOf(ModelValueArray const&v) noexcept {
	return alignOf<PlatSpec>(v[0]);
}

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t alignOf(const ModelValueVector&) noexcept {
	VectorMemMap<PlatSpec> v;
	return alignOf<PlatSpec>(v);
}

template<typename PlatSpec>
[[nodiscard]]
constexpr std::size_t alignOf(const ModelValue &t) noexcept {
	std::size_t alignment = 0;
	switch (t.type()) {
		case ModelValue::Type::Bool:
			alignment = PlatSpec::alignOf(t.get<bool>());
			break;
		case ModelValue::Type::Undefined:
			alignment = 1;
			break;
		case ModelValue::Type::UnsignedInteger8:
			alignment = PlatSpec::alignOf(t.get<uint8_t>());
			break;
		case ModelValue::Type::UnsignedInteger16:
			alignment = PlatSpec::alignOf(t.get<uint16_t>());
			break;
		case ModelValue::Type::UnsignedInteger32:
			alignment = PlatSpec::alignOf(t.get<uint32_t>());
			break;
		case ModelValue::Type::UnsignedInteger64:
			alignment = PlatSpec::alignOf(t.get<uint64_t>());
			break;
		case ModelValue::Type::SignedInteger8:
			alignment = PlatSpec::alignOf(t.get<int8_t>());
			break;
		case ModelValue::Type::SignedInteger16:
			alignment = PlatSpec::alignOf(t.get<int16_t>());
			break;
		case ModelValue::Type::SignedInteger32:
			alignment = PlatSpec::alignOf(t.get<int32_t>());
			break;
		case ModelValue::Type::SignedInteger64:
			alignment = PlatSpec::alignOf(t.get<int64_t>());
			break;
		case ModelValue::Type::String:
			alignment = alignOf<PlatSpec>(t.get<ox::String>());
			break;
		case ModelValue::Type::Object:
			alignment = alignOf<PlatSpec>(t.get<ox::ModelObject>());
			break;
		case ModelValue::Type::Union:
			alignment = alignOf<PlatSpec>(t.get<ox::ModelUnion>());
			break;
		case ModelValue::Type::Vector:
			alignment = alignOf<PlatSpec>(t.get<ox::ModelValueVector>());
			break;
		case ModelValue::Type::InlineArray:
		{
			auto &list = t.get<ox::ModelValueArray>();
			alignment = alignOf<PlatSpec>(list[0]);
			break;
		}
	}
	return alignment;
}

constexpr Error model(auto *h, CommonPtrWith<ModelObject> auto *obj) noexcept {
	OX_RETURN_ERROR(h->template setTypeInfo<ModelObject>(
			obj->typeName().c_str(), obj->typeVersion(), {}, obj->m_fieldsOrder.size()));
	for (auto &f : obj->m_fieldsOrder) {
		OX_RETURN_ERROR(h->field(f->name.c_str(), &f->value));
	}
	return {};
}

constexpr Error model(auto *h, CommonPtrWith<ModelUnion> auto *obj) noexcept {
	OX_RETURN_ERROR(h->template setTypeInfo<ModelUnion>(
			obj->typeName().c_str(), obj->typeVersion(), {}, obj->m_fieldsOrder.size()));
	for (auto &f : obj->m_fieldsOrder) {
		OX_RETURN_ERROR(h->field(f->name.c_str(), &f->value));
	}
	return {};
}

constexpr ModelValue::ModelValue(const ModelValue &other) noexcept {
	m_type = other.m_type;
	switch (m_type) {
		case Type::Undefined:
		case Type::Bool:
		case Type::UnsignedInteger8:
		case Type::UnsignedInteger16:
		case Type::UnsignedInteger32:
		case Type::UnsignedInteger64:
		case Type::SignedInteger8:
		case Type::SignedInteger16:
		case Type::SignedInteger32:
		case Type::SignedInteger64:
			m_data = other.m_data;
			break;
		case Type::String:
			m_data.str = new String(other.get<String>());
			break;
		case Type::Union:
			m_data.uni = new ModelUnion(other.get<ModelUnion>());
			break;
		case Type::Object:
			m_data.obj = new ModelObject(other.get<ModelObject>());
			break;
		case Type::Vector:
			m_data.vec = new ModelValueVector(*other.m_data.vec);
			break;
		case Type::InlineArray:
			m_data.array = new ModelValueArray(*other.m_data.array);
			break;
	}
}

constexpr ModelValue::ModelValue(ModelValue &&other) noexcept {
	m_type = other.m_type;
	switch (m_type) {
		case Type::Undefined:
		case Type::Bool:
		case Type::UnsignedInteger8:
		case Type::UnsignedInteger16:
		case Type::UnsignedInteger32:
		case Type::UnsignedInteger64:
		case Type::SignedInteger8:
		case Type::SignedInteger16:
		case Type::SignedInteger32:
		case Type::SignedInteger64:
			m_data = other.m_data;
			other.m_data.ui64 = 0;
			break;
		case Type::String:
			m_data.str = other.m_data.str;
			other.m_data.str = new String;
			break;
		case Type::Union:
			m_data.uni = other.m_data.uni;
			other.m_data.uni = new ModelUnion;
			break;
		case Type::Object:
			m_data.obj = other.m_data.obj;
			other.m_data.obj = new ModelObject;
			break;
		case Type::Vector:
			m_data.vec = other.m_data.vec;
			other.m_data.vec = new ModelValueVector;
			break;
		case Type::InlineArray:
			m_data.array = other.m_data.array;
			other.m_data.array = new ModelValueArray();
			break;
	}
}

template<typename T>
constexpr ModelValue::ModelValue(const T &val) noexcept
	requires(!ox::is_same_v<ox::remove_reference_t<T>, ModelValue>) {
	std::ignore = set(val);
}

template<typename T>
constexpr ModelValue::ModelValue(T &&val) noexcept
	requires(!ox::is_same_v<ox::remove_reference_t<T>, ModelValue>) {
	std::ignore = set(ox::forward<T>(val));
}

constexpr ModelValue::~ModelValue() noexcept {
	freeResources();
}

constexpr ModelValue::Type ModelValue::type() const noexcept {
	return m_type;
}

constexpr Error ModelValue::setType(
		const DescriptorType *type,
		SubscriptStack const&subscriptStack,
		int subscriptLevels) noexcept {
	freeResources();
	oxAssert(subscriptLevels <= static_cast<int>(subscriptStack.size()), "subscript level mismatch");
	if (subscriptLevels) {
		auto const&subscript = subscriptStack[subscriptStack.size() - static_cast<size_t>(subscriptLevels)];
		if (subscript.subscriptType == Subscript::SubscriptType::InlineArray) {
			m_type = Type::InlineArray;
			m_data.array = new ModelValueArray;
			OX_RETURN_ERROR(m_data.array->setType(type, subscriptStack, subscriptLevels - 1));
			OX_RETURN_ERROR(m_data.array->setSize(static_cast<size_t>(subscript.length)));
		} else {
			m_type = Type::Vector;
			m_data.vec = new ModelValueVector;
			OX_RETURN_ERROR(m_data.vec->setType(type, subscriptStack, subscriptLevels - 1));
		}
		return {};
	} else if (type->typeName == types::Bool) {
		m_type = Type::Bool;
	} else if (type->typeName == types::BasicString ||
	           type->typeName == types::IString ||
	           type->typeName == types::String) {
		m_type = Type::String;
		m_data.str = new String;
	} else if (type->typeName == types::Uint8) {
		m_type = Type::UnsignedInteger8;
	} else if (type->typeName == types::Uint16) {
		m_type = Type::UnsignedInteger16;
	} else if (type->typeName == types::Uint32) {
		m_type = Type::UnsignedInteger32;
	} else if (type->typeName == types::Uint64) {
		m_type = Type::UnsignedInteger64;
	} else if (type->typeName == types::Int8) {
		m_type = Type::SignedInteger8;
	} else if (type->typeName == types::Int16) {
		m_type = Type::SignedInteger16;
	} else if (type->typeName == types::Int32) {
		m_type = Type::SignedInteger32;
	} else if (type->typeName == types::Int64) {
		m_type = Type::SignedInteger64;
	} else if (type->primitiveType == PrimitiveType::Struct) {
		m_type = Type::Object;
		m_data.obj = new ModelObject;
		OX_RETURN_ERROR(m_data.obj->setType(type));
	} else if (type->primitiveType == PrimitiveType::Union) {
		m_type = Type::Union;
		OX_REQUIRE_M(u, ModelUnion::make(type));
		m_data.uni = u.release();
		OX_RETURN_ERROR(m_data.uni->setType(type));
	}
	oxAssert(m_type != Type::Undefined, "No type set");
	return {};
}

template<typename T>
constexpr Error ModelValue::setType() noexcept {
	constexpr auto type = getType<T>();
	freeResources();
	m_type = type;
	// 2022.09.04: Clang retardedly requires initializing the union values directly,
	//             rather than using getValue<type>()
	if constexpr(type == Type::Object) {
		m_data.obj = new ModelObject;
		OX_RETURN_ERROR(m_data.obj->setType(type));
	} else if constexpr(type == Type::Union) {
		OX_REQUIRE_M(u, ModelUnion::make(type));
		m_data.uni = u.release();
		OX_RETURN_ERROR(m_data.uni->setType(type));
	} else if constexpr(type == Type::String) {
		m_data.str = new String;
	} else if constexpr(type == Type::Vector) {
		m_data.vec = new ModelValueVector;
	} else if constexpr(type == Type::Bool) {
		m_data.b = false;
	} else if constexpr(type == Type::SignedInteger8) {
		m_data.i8 = 0;
	} else if constexpr(type == Type::SignedInteger16) {
		m_data.i16 = 0;
	} else if constexpr(type == Type::SignedInteger32) {
		m_data.i32 = 0;
	} else if constexpr(type == Type::SignedInteger64) {
		m_data.i64 = 0;
	} else if constexpr(type == Type::UnsignedInteger8) {
		m_data.ui8 = 0;
	} else if constexpr(type == Type::UnsignedInteger16) {
		m_data.ui16 = 0;
	} else if constexpr(type == Type::UnsignedInteger32) {
		m_data.ui32 = 0;
	} else if constexpr(type == Type::UnsignedInteger64) {
		m_data.ui64 = 0;
	}
	return {};
}

template<typename T>
constexpr Error ModelValue::set(const T &v) noexcept {
	constexpr auto type = getType<T>();
	if (m_type != type) [[unlikely]] {
		return Error(1, "type mismatch");
	}
	auto &value = getValue<type>(*this);
	if constexpr(type == Type::Vector || type == Type::Object ||
	             type == Type::Union || type == Type::String || type == Type::InlineArray) {
		safeDelete(&value);
	}
	value = v;
	return {};
}

template<typename T>
constexpr Error ModelValue::set(T &&v) noexcept {
	constexpr auto type = getType<T>();
	if (m_type != type) [[unlikely]] {
		return ox::Error(1, "type mismatch");
	}
	auto &value = getValue<type>(*this);
	if constexpr(type == Type::Vector || type == Type::Object ||
	             type == Type::Union || type == Type::String || type == Type::InlineArray) {
		safeDelete(&value);
	}
	value = std::move(v);
	return {};
}

constexpr ModelValue &ModelValue::operator=(const ModelValue &other) noexcept {
	if (this == &other) [[unlikely]] {
		return *this;
	}
	freeResources();
	m_type = other.m_type;
	switch (m_type) {
		case Type::Undefined:
		case Type::Bool:
		case Type::UnsignedInteger8:
		case Type::UnsignedInteger16:
		case Type::UnsignedInteger32:
		case Type::UnsignedInteger64:
		case Type::SignedInteger8:
		case Type::SignedInteger16:
		case Type::SignedInteger32:
		case Type::SignedInteger64:
			m_data = other.m_data;
			break;
		case Type::String:
			m_data.str = new String(other.get<String>());
			break;
		case Type::Union:
			m_data.uni = new ModelUnion(other.get<ModelUnion>());
			break;
		case Type::Object:
			m_data.obj = new ModelObject(other.get<ModelObject>());
			break;
		case Type::Vector:
			m_data.vec = new ModelValueVector(*other.m_data.vec);
			break;
		case Type::InlineArray:
			m_data.array = new ModelValueArray(*other.m_data.array);
			break;
	}
	return *this;
}

constexpr ModelValue &ModelValue::operator=(ModelValue &&other) noexcept {
	if (this == &other) [[unlikely]] {
		return *this;
	}
	freeResources();
	m_type = other.m_type;
	switch (m_type) {
		case Type::Undefined:
		case Type::Bool:
		case Type::UnsignedInteger8:
		case Type::UnsignedInteger16:
		case Type::UnsignedInteger32:
		case Type::UnsignedInteger64:
		case Type::SignedInteger8:
		case Type::SignedInteger16:
		case Type::SignedInteger32:
		case Type::SignedInteger64:
			m_data = other.m_data;
			other.m_data = {};
			break;
		case Type::String:
			m_data.str = other.m_data.str;
			other.m_data.str = new String;
			break;
		case Type::Object:
			m_data.obj = other.m_data.obj;
			other.m_data.obj = new ModelObject;
			break;
		case Type::Union:
			m_data.uni = other.m_data.uni;
			other.m_data.uni = new ModelUnion;
			break;
		case Type::Vector:
			m_data.vec = other.m_data.vec;
			other.m_data.vec = new ModelValueVector;
			break;
		case Type::InlineArray:
			m_data.array = other.m_data.array;
			other.m_data.array = new ModelValueArray;
			break;
	}
	return *this;
}

constexpr void ModelValue::freeResources() noexcept {
	switch (m_type) {
		case Type::Undefined:
		case Type::Bool:
		case Type::UnsignedInteger8:
		case Type::UnsignedInteger16:
		case Type::UnsignedInteger32:
		case Type::UnsignedInteger64:
		case Type::SignedInteger8:
		case Type::SignedInteger16:
		case Type::SignedInteger32:
		case Type::SignedInteger64:
			break;
		case Type::String:
			safeDelete(m_data.str);
			break;
		case Type::Object:
			safeDelete(m_data.obj);
			break;
		case Type::Union:
			safeDelete(m_data.uni);
			break;
		case Type::Vector:
			safeDelete(m_data.vec);
			break;
		case Type::InlineArray:
			safeDelete(m_data.array);
			break;
	}
	m_type = Type::Undefined;
}

constexpr ModelValueVector::ModelValueVector(const ModelValueVector &other) noexcept {
	for (auto &v : other.m_vec) {
		m_vec.emplace_back(v);
	}
	m_typeName = other.m_typeName;
	m_typeVersion = other.m_typeVersion;
}

constexpr ModelValueVector::ModelValueVector(ModelValueVector &&other) noexcept {
	m_vec = std::move(other.m_vec);
	m_typeName = std::move(other.m_typeName);
	m_typeVersion = other.m_typeVersion;
}

constexpr ModelValueVector &ModelValueVector::operator=(const ModelValueVector &other) noexcept {
	if (this == &other) {
		return *this;
	}
	for (auto &v : other.m_vec) {
		m_vec.emplace_back(v);
	}
	m_typeName = other.m_typeName;
	m_typeVersion = other.m_typeVersion;
	return *this;
}

constexpr ModelValueVector &ModelValueVector::operator=(ModelValueVector &&other) noexcept {
	if (this == &other) {
		return *this;
	}
	m_vec = std::move(other.m_vec);
	m_typeName = std::move(other.m_typeName);
	m_typeVersion = other.m_typeVersion;
	return *this;
}

}
