// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.

decltype(ircd::m::membership_positive)
ircd::m::membership_positive
{
	"join"_sv,
	"invite"_sv,
};

decltype(ircd::m::membership_negative)
ircd::m::membership_negative
{
	"leave"_sv,
	"ban"_sv,
	"knock"_sv,
	""_sv,
};

bool
ircd::m::membership(const room &room,
                    const user::id &user_id,
                    const vector_view<const string_view> &membership)
{
	const auto event_idx
	{
		room.get(std::nothrow, "m.room.member", user_id)
	};

	return m::membership(event_idx, membership);
}

bool
ircd::m::membership(const event::idx &event_idx,
                    const vector_view<const string_view> &membership)
{
	bool ret;
	const auto closure
	{
		[&membership, &ret](const json::object &content)
		{
			const json::string &content_membership
			{
				content["membership"]
			};

			const auto it
			{
				std::find(begin(membership), end(membership), content_membership)
			};

			ret = content_membership && it != end(membership);
		}
	};

	// If the query was successful a membership state exists (even if the
	// string found was illegally empty) thus we must return the value of ret.
	if(m::get(std::nothrow, event_idx, "content", closure))
		return ret;

	// If the user included an empty string in the vector, they want us to
	// return true for non-membership (i.e a failed query).
	static const auto &empty{[](auto&& s) { return !s; }};
	if(std::any_of(begin(membership), end(membership), empty))
		return true;

	// If the membership vector itself was empty that's also a non-membership.
	if(membership.empty())
		return true;

	return false;
}

bool
ircd::m::membership(const m::event &event,
                    const vector_view<const string_view> &membership)
{
	const auto matching
	{
		m::membership(event)
	};

	const auto it
	{
		std::find(begin(membership), end(membership), matching)
	};

	return it != end(membership);
}

ircd::string_view
ircd::m::membership(const mutable_buffer &out,
                    const room &room,
                    const user::id &user_id)
{
	const auto event_idx
	{
		room.get(std::nothrow, "m.room.member", user_id)
	};

	return m::membership(out, event_idx);
}

ircd::string_view
ircd::m::membership(const mutable_buffer &out,
                    const event::idx &event_idx)
{
	return m::query(std::nothrow, event_idx, "content", [&out]
	(const json::object &content) -> string_view
	{
		const json::string &content_membership
		{
			content["membership"]
		};

		return strlcpy
		{
			out, content_membership
		};
	});
}

ircd::string_view
ircd::m::membership(const event &event)
{
	// The caller should check event type; empty is allowed if they only have content.
	assert(!json::get<"type"_>(event) || json::get<"type"_>(event) == "m.room.member");

	const auto &membership
	{
		json::get<"membership"_>(event)
	};

	if(likely(!membership))
	{
		const auto &content
		{
			json::get<"content"_>(event)
		};

		const json::string &membership
		{
			content.get("membership")
		};

		return membership;
	}

	return membership;
}
