#pragma once

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/variadic_seq_to_seq.hpp>
#include <boost/preprocessor/stringize.hpp>

#define psemek_declare_struct_detail_field(r, data, value) BOOST_PP_TUPLE_ELEM(0, value) BOOST_PP_TUPLE_ELEM(1, value);

#define psemek_declare_struct_detail_for_each(r, data, value) f(x.BOOST_PP_TUPLE_ELEM(1, value) );

#define psemek_declare_struct_detail_print(r, data, index, value) \
	if (index > 0) s << ", "; \
	s << BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, value)) << " = " << x.BOOST_PP_TUPLE_ELEM(1, value);

#define psemek_declare_struct_impl(name, values) \
	struct name { \
		BOOST_PP_SEQ_FOR_EACH(psemek_declare_struct_detail_field, _, values) \
	}; \
	template <typename F> \
	void for_each(name & x, F && f) { \
		BOOST_PP_SEQ_FOR_EACH(psemek_declare_struct_detail_for_each, _, values) \
	} \
	template <typename F> \
	void for_each(name const & x, F && f) { \
		BOOST_PP_SEQ_FOR_EACH(psemek_declare_struct_detail_for_each, _, values) \
	} \
	template <typename Stream> \
	Stream & operator << (Stream & s, name const & x) { \
		s << '{'; \
		BOOST_PP_SEQ_FOR_EACH_I(psemek_declare_struct_detail_print, _, values) \
		s << '}'; \
		return s; \
	} \
	template <typename Stream> \
	void write(Stream & s, name const & x) { \
		for_each(x, [&](auto const & v){ write(s, v); }); \
	} \
	template <typename Stream> \
	void read(Stream & s, name & x) { \
		for_each(x, [&](auto & v){ read(s, v); }); \
	}

#define psemek_declare_struct(name, values) psemek_declare_struct_impl(name, BOOST_PP_VARIADIC_SEQ_TO_SEQ(values))
