/*
 * 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 "array.hpp"
#include "buffer.hpp"
#include "types.hpp"
#include "typetraits.hpp"
#include "writer.hpp"

namespace ox {

template<typename PlatSpec>
struct VectorMemMap {
	size_t const smallVecSize = 0; // not a map value
	typename PlatSpec::size_t size = 0;
	typename PlatSpec::size_t cap = 0;
	typename PlatSpec::PtrType items = 0;
};

template<typename PlatSpec>
[[nodiscard]]
constexpr auto sizeOf(VectorMemMap<PlatSpec> const *t) noexcept {
	constexpr auto padding = [](size_t const size, size_t const al) {
		return size % al;
	};
	size_t size = 0;
	if (t->smallVecSize) {
		size += t->smallVecSize;
		size += padding(size, PlatSpec::alignOf(t->size));
	}
	size += sizeof(t->size);
	size += padding(size, PlatSpec::alignOf(t->cap));
	size += sizeof(t->cap);
	size += padding(size, PlatSpec::alignOf(t->items));
	size += sizeof(t->items);
	return size;
}

template<typename PlatSpec, size_t SmallVecSize = 0>
[[nodiscard]]
constexpr auto alignOf(VectorMemMap<PlatSpec> const&) noexcept {
	typename PlatSpec::size_t const i = 0;
	return PlatSpec::alignOf(i);
}

template<typename PlatSpec, typename T>
constexpr Error pad(Writer_c auto &w, T const *v) noexcept {
	auto const a = PlatSpec::alignOf(*v);
	auto const excess = w.tellp() % a;
	if (excess) {
		return w.write(nullptr, a - excess);
	} else {
		return {};
	}
}

template<typename PlatSpec>
constexpr Error serialize(Writer_c auto &w, VectorMemMap<PlatSpec> const &vm) noexcept {
	OX_RETURN_ERROR(w.write(nullptr, vm.smallVecSize));
	OX_RETURN_ERROR(serialize(w, PlatSpec::correctEndianness(vm.size)));
	OX_RETURN_ERROR(serialize(w, PlatSpec::correctEndianness(vm.cap)));
	OX_RETURN_ERROR(serialize(w, PlatSpec::correctEndianness(vm.items)));
	return {};
}

constexpr Error serialize(Writer_c auto &w, Integral_c auto val) noexcept {
	Array<char, sizeof(val)> tmp;
	for (auto i = 0u; i < sizeof(val); ++i) {
		tmp[i] = static_cast<char>((val >> i * 8) & 255);
	}
	return w.write(tmp.data(), tmp.size());
}

template<typename T>
constexpr Result<Array<char, sizeof(T)>> serialize(T const &in) noexcept {
	Array<char, sizeof(T)> out = {};
	CharBuffWriter w(out);
	OX_RETURN_ERROR(serialize(w, in));
	return out;
}

}
