/*
 * Copyright 2015 - 2022 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/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/types.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 ModelValueVector;

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

	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;
			String *str = nullptr;
			ModelObject *obj;
			ModelUnion *uni;
			ModelValueVector *vec;
		} m_data;

		template<typename T>
		consteval static 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 (!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(is_same_v<U, String>) {
				return Type::String;
			} else if constexpr(is_same_v<U, ModelValueVector>) {
				return Type::Vector;
			} 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 {
				return t.m_data.i32;
			}
		}

	public:
		constexpr ModelValue() noexcept = default;

		constexpr ModelValue(const ModelValue &other) noexcept;

		constexpr ModelValue(ModelValue &other) noexcept;

		constexpr ModelValue(ModelValue &&other) noexcept;

		template<typename T>
		explicit constexpr ModelValue(const T &val) noexcept;

		template<typename T>
		explicit constexpr ModelValue(T &&val) noexcept;

		constexpr ~ModelValue() noexcept;

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

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

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

		constexpr Error setType(const DescriptorType *type, 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=(ModelValue &val) noexcept;

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

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

	private:
		constexpr void freeResources() noexcept;

};

class ModelValueVector {
	private:
		Vector<ModelValue> m_vec;
		ModelValue m_templateValue;
		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 void 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) {
					m_vec[i] = m_templateValue;
				}
			}
		}

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

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

		constexpr Error setType(const DescriptorType *type, int subscriptLevels) noexcept {
			return m_templateValue.setType(type, subscriptLevels);
		}

		[[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 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 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;
}

class ModelObject {
	protected:
		struct Field {
			String name;
			ModelValue value;
		};
		oxModelFriend(ModelObject);
		friend ModelValue;
		Vector<UniquePtr<Field>> m_fieldsOrder;
		HashMap<String, ModelValue*> m_fields;
		String m_typeName;
		int m_typeVersion = 0;

	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{f->name, f->value});
				m_fields[field->name] = &field->value;
			}
			m_typeName = other.m_typeName;
			m_typeVersion = other.m_typeVersion;
		}

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

		[[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{f->name, f->value});
				m_fields[field->name] = &field->value;
			}
			m_typeName = other.m_typeName;
			m_typeVersion = other.m_typeVersion;
			return *this;
		}

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

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

		constexpr auto &operator[](const String &k) noexcept {
			return *m_fields[k];
		}

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

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

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

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

};

class ModelUnion {
	protected:
		struct Field {
			int idx = -1;
			String name;
			ModelValue value;
		};
		oxModelFriend(ModelUnion);
		friend ModelValue;
		Vector<UniquePtr<Field>> m_fieldsOrder;
		HashMap<String, Field*> m_fields;
		String m_typeName;
		int m_typeVersion = 0;
		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, f->name, f->value});
				m_fields[field->name] = field.get();
				++i;
			}
			m_typeName = other.m_typeName;
			m_typeVersion = other.m_typeVersion;
			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_typeName = std::move(other.m_typeName);
			m_typeVersion = other.m_typeVersion;
			m_unionIdx = other.m_unionIdx;
		}

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

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

		constexpr auto &operator[](const String &k) noexcept {
			return m_fields[k]->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 ModelValue &get(std::size_t i) noexcept {
			return m_fieldsOrder[i]->value;
		}

		[[nodiscard]]
		constexpr ModelValue &get(const String &k) noexcept {
			return (*m_fields.at(k).value)->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_typeName;
		}

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

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

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

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

};

constexpr Error model(auto *h, CommonPtrWith<ModelObject> auto *obj) noexcept {
	h->setTypeInfo(obj->m_typeName.c_str(), obj->m_typeVersion, static_cast<int>(obj->m_fieldsOrder.size()));
	for (auto &f : obj->m_fieldsOrder) {
		oxReturnError(h->field(f->name.c_str(), &f->value));
	}
	return OxError(0);
}

constexpr Error model(auto *h, CommonPtrWith<ModelUnion> auto *obj) noexcept {
	h->setTypeInfo(obj->m_typeName.c_str(), obj->m_typeVersion, static_cast<int>(obj->m_fieldsOrder.size()));
	for (auto &f : obj->m_fieldsOrder) {
		oxReturnError(h->field(f->name.c_str(), &f->value));
	}
	return OxError(0);
}

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:
			ox_memcpy(&m_data, &other.m_data, sizeof(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;
	}
}

constexpr ModelValue::ModelValue(ModelValue &other) noexcept: ModelValue(const_cast<const ModelValue&>(other)) {
}

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:
			ox_memcpy(&m_data, &other.m_data, sizeof(m_data));
			ox_memset(&other.m_data, 0, sizeof(m_data));
			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;
	}
}

template<typename T>
constexpr ModelValue::ModelValue(const T &val) noexcept {
	set(val);
}

template<typename T>
constexpr ModelValue::ModelValue(T &&val) noexcept {
	set(ox::forward(val));
}

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

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

constexpr Error ModelValue::setType(const DescriptorType *type, int subscriptLevels) noexcept {
	freeResources();
	if (subscriptLevels) {
		m_type = Type::Vector;
		m_data.vec = new ModelValueVector;
		return m_data.vec->setType(type, subscriptLevels - 1);
	} else if (type->typeName == types::Bool) {
		m_type = Type::Bool;
	} else if (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;
		oxReturnError(m_data.obj->setType(type));
	} else if (type->primitiveType == PrimitiveType::Union) {
		m_type = Type::Union;
		oxRequireM(u, ModelUnion::make(type));
		m_data.uni = u.release();
		oxReturnError(m_data.uni->setType(type));
	}
	return OxError(0);
}

template<typename T>
constexpr Error ModelValue::setType() noexcept {
	constexpr auto type = getType<T>();
	return setType(type);
}

template<typename T>
constexpr Error ModelValue::set(const T &v) noexcept {
	constexpr auto type = getType<T>();
	if (m_type != type) [[unlikely]] {
		return OxError(1, "type mismatch");
	}
	auto &value = getValue<type>(*this);
	value = v;
	return OxError(0);
}

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

constexpr ModelValue &ModelValue::operator=(ModelValue &other) noexcept {
	return this->operator=(const_cast<const ModelValue&>(other));
}

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:
			ox_memcpy(&m_data, &other.m_data, sizeof(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;
	}
	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:
			ox_memcpy(&m_data, &other.m_data, sizeof(m_data));
			ox_memset(&other.m_data, 0, sizeof(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;
	}
	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;
	}
	m_type = Type::Undefined;
}

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

constexpr ModelValueVector::ModelValueVector(ModelValueVector &&other) noexcept {
	m_vec = std::move(other.m_vec);
	m_templateValue = std::move(other.m_templateValue);
	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_templateValue = other.m_templateValue;
	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_templateValue = std::move(other.m_templateValue);
	m_typeName = std::move(other.m_typeName);
	m_typeVersion = other.m_typeVersion;
	return *this;
}

}
