/*
 * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
 */

#pragma once

#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/error.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>

namespace ox {

template<typename PlatSpec>
class UnionSizeCatcher: public ModelHandlerBase<UnionSizeCatcher<PlatSpec>, OpType::Reflect> {
	private:
		std::size_t m_size = 0;

	public:

		template<typename T>
		constexpr ox::Error setTypeInfo(
				const char* = T::TypeName,
				int = T::TypeVersion) noexcept {
			return {};
		}

		template<typename T>
		constexpr ox::Error setTypeInfo(
				const char*,
				int,
				const Vector<String>&,
				std::size_t) noexcept {
			return {};
		}

		template<typename T, bool force>
		constexpr ox::Error field(CRStringView, const UnionView<T, force> val) noexcept {
			UnionSizeCatcher<PlatSpec> sc;
			oxReturnError(model(sc.interface(), val.get()));
			m_size += sc.size();
			return {};
		}

		template<typename T>
		constexpr ox::Error field(CRStringView, const T *val) noexcept;

		template<typename T>
		constexpr ox::Error field(CRStringView, const T **val, std::size_t cnt) noexcept;

		[[nodiscard]]
		constexpr auto size() const noexcept {
			return m_size;
		}

	private:
		template<typename T, std::size_t SmallVecSize>
		constexpr ox::Error fieldStr(CRStringView, const ox::BasicString<SmallVecSize> *val) noexcept;

		template<typename T, std::size_t SmallVecSize>
		constexpr ox::Error fieldVector(CRStringView, const ox::Vector<T, SmallVecSize> *val) noexcept;

		constexpr void setSize(std::size_t sz) noexcept;
};

template<typename PlatSpec>
template<typename T>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::field(CRStringView, const T *val) noexcept {
	setSize(sizeOf<PlatSpec>(val));
	return {};
}

template<typename PlatSpec>
template<typename T>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::field(CRStringView, const T **val, std::size_t cnt) noexcept {
	for (std::size_t i = 0; i < cnt; ++i) {
		oxReturnError(field("", &val[i]));
	}
	return {};
}

template<typename PlatSpec>
template<typename T, std::size_t SmallStrSize>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::fieldStr(CRStringView, const ox::BasicString<SmallStrSize>*) noexcept {
	ox::VectorMemMap<PlatSpec> v;
	setSize(sizeOf<PlatSpec>(v));
	return {};
}

template<typename PlatSpec>
template<typename T, std::size_t SmallVecSize>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::fieldVector(CRStringView, const ox::Vector<T, SmallVecSize>*) noexcept {
	ox::VectorMemMap<PlatSpec> v;
	setSize(sizeOf<PlatSpec>(v));
	return {};
}

template<typename PlatSpec>
constexpr void UnionSizeCatcher<PlatSpec>::setSize(std::size_t sz) noexcept {
	m_size = ox::max(m_size, sz);
}

}
