#ifndef OPENMW_TEST_SUITE_SERIALIZATION_FORMAT_H
#define OPENMW_TEST_SUITE_SERIALIZATION_FORMAT_H

#include <components/serialization/format.hpp>

#include <cstdint>
#include <type_traits>
#include <utility>

namespace SerializationTesting
{
    struct Pod
    {
        int mInt = 42;
        double mDouble = 3.14;

        friend bool operator==(const Pod& l, const Pod& r)
        {
            const auto tuple = [](const Pod& v) { return std::tuple(v.mInt, v.mDouble); };
            return tuple(l) == tuple(r);
        }
    };

    enum Enum : std::int32_t
    {
        A,
        B,
        C,
    };

    struct Composite
    {
        short mFloatArray[3] = { 0 };
        std::vector<std::int32_t> mIntVector;
        std::vector<Enum> mEnumVector;
        std::vector<Pod> mPodVector;
        std::size_t mPodDataSize = 0;
        std::vector<Pod> mPodBuffer;
        std::size_t mCharDataSize = 0;
        std::vector<char> mCharBuffer;
    };

    template <Serialization::Mode mode>
    struct TestFormat : Serialization::Format<mode, TestFormat<mode>>
    {
        using Serialization::Format<mode, TestFormat<mode>>::operator();

        template <class Visitor, class T>
        auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t<std::is_same_v<std::decay_t<T>, Pod>>
        {
            visitor(*this, value.mInt);
            visitor(*this, value.mDouble);
        }

        template <class Visitor, class T>
        auto operator()(Visitor&& visitor, T& value) const
            -> std::enable_if_t<std::is_same_v<std::decay_t<T>, Composite>>
        {
            visitor(*this, value.mFloatArray);
            visitor(*this, value.mIntVector);
            visitor(*this, value.mEnumVector);
            visitor(*this, value.mPodVector);
            visitor(*this, value.mPodDataSize);
            if constexpr (mode == Serialization::Mode::Read)
                value.mPodBuffer.resize(value.mPodDataSize);
            visitor(*this, value.mPodBuffer.data(), value.mPodDataSize);
            visitor(*this, value.mCharDataSize);
            if constexpr (mode == Serialization::Mode::Read)
                value.mCharBuffer.resize(value.mCharDataSize);
            visitor(*this, value.mCharBuffer.data(), value.mCharDataSize);
        }
    };
}

#endif