/*
 * Purple Zulip - Zulip Protocol Plugin
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * Purple Zulip is the legal property of its developers, whose names are too
 * numerous to list here. Please refer to the COPYRIGHT file distributed with
 * this source distribution.
 *
 * This plugin is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This plugin is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this plugin; if not, see <https://www.gnu.org/licenses/>.
 */

#include <json-glib/json-glib.h>

#include <birb.h>

#include "purplezulipresponses.h"

#include "purplezulipevents.h"
#include "purplezuliptypes.h"

#define PURPLE_ZULIP_COPY_STRING(sym) copy->sym = g_strdup(response->sym)
#define PURPLE_ZULIP_COPY_VALUE(sym) copy->sym = response->sym

#define PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(sym) G_STMT_START { \
	response->sym = json_object_get_boolean_member_with_default(object, \
	                                                            G_STRINGIFY_ARG(sym), \
	                                                            FALSE); \
} G_STMT_END
#define PURPLE_ZULIP_RESPONSE_GET_INT(sym) G_STMT_START { \
	response->sym = json_object_get_int_member_with_default(object, \
	                                                        G_STRINGIFY_ARG(sym), \
	                                                        G_GINT64_CONSTANT(0)); \
} G_STMT_END
#define PURPLE_ZULIP_RESPONSE_GET_STRING(sym) G_STMT_START { \
	value = json_object_get_string_member_with_default(object, \
	                                                   G_STRINGIFY_ARG(sym), \
	                                                   NULL); \
	response->sym = g_strdup(value); \
} G_STMT_END

#define	PURPLE_ZULIP_RESPONSE_GET_ARRAY(sym, TYPE, type, destroy) G_STMT_START { \
	if(json_object_has_member(object, G_STRINGIFY_ARG(sym))) { \
		JsonArray *array = NULL; \
		guint n_items = 0; \
		array = json_object_get_array_member(object, G_STRINGIFY_ARG(sym)); \
		n_items = json_array_get_length(array); \
		for(guint i = 0; i < n_items; i++) { \
			type *inst = NULL; \
			JsonNode *node = NULL; \
			node = json_array_get_element(array, i); \
			inst = json_boxed_deserialize(TYPE, node); \
			if(inst != NULL) { \
				g_ptr_array_add(response->sym, inst); \
			} \
		} \
	} \
} G_STMT_END

/******************************************************************************
 * BasicResponse
 *****************************************************************************/
PurpleZulipBasicResponse *
purple_zulip_basic_response_copy(PurpleZulipBasicResponse *response) {
	PurpleZulipBasicResponse *copy = NULL;

	g_return_val_if_fail(response != NULL, NULL);

	copy = g_new(PurpleZulipBasicResponse, 1);

	copy->code = g_strdup(response->code);
	copy->ignored_parameters = g_strdupv(response->ignored_parameters);
	copy->message = g_strdup(response->message);
	copy->parameters = g_strdup(response->parameters);
	copy->result = g_strdup(response->result);
	copy->retry_after = response->retry_after;
	copy->var_name = g_strdup(response->var_name);

	return copy;
}

void
purple_zulip_basic_response_free(PurpleZulipBasicResponse *response) {
	g_return_if_fail(response != NULL);

	g_free(response->code);
	g_strfreev(response->ignored_parameters);
	g_free(response->message);
	g_free(response->parameters);
	g_free(response->result);
	g_free(response->var_name);

	g_free(response);
}

static gpointer
purple_zulip_basic_response_deserialize(JsonNode *node) {
	PurpleZulipBasicResponse *response = NULL;
	JsonObject *object = NULL;
	const char *value = NULL;

	response = g_new(PurpleZulipBasicResponse, 1);

	object = json_node_get_object(node);

	value = json_object_get_string_member_with_default(object, "code", NULL);
	response->code = g_strdup(value);

	if(json_object_has_member(object, "ignored_parameters")) {
		GStrvBuilder *builder = NULL;
		JsonArray *array = NULL;
		guint n_items = 0;

		builder = g_strv_builder_new();

		array = json_object_get_array_member(object, "ignored_parameters");
		n_items = json_array_get_length(array);

		for(guint i = 0; i < n_items; i++) {
			const char *str = NULL;

			str = json_array_get_string_element(array, i);
			g_strv_builder_add(builder, str);
		}

		response->ignored_parameters = g_strv_builder_unref_to_strv(builder);
	} else {
		response->ignored_parameters = NULL;
	}

	value = json_object_get_string_member_with_default(object, "msg", NULL);
	response->message = g_strdup(value);

	value = json_object_get_string_member_with_default(object, "parameters",
	                                                   NULL);
	response->parameters = g_strdup(value);

	value = json_object_get_string_member_with_default(object, "result", NULL);
	response->result = g_strdup(value);

	response->retry_after = json_object_get_double_member_with_default(object,
	                                                                   "retry_after",
	                                                                   0.0);

	value = json_object_get_string_member_with_default(object, "var_name",
	                                                   NULL);
	response->var_name = g_strdup(value);

	return response;
}

G_DEFINE_BOXED_TYPE_WITH_CODE(PurpleZulipBasicResponse,
                              purple_zulip_basic_response,
                              purple_zulip_basic_response_copy,
                              purple_zulip_basic_response_free,
                              json_boxed_register_deserialize_func(g_define_type_id,
                                                                   JSON_NODE_OBJECT,
                                                                   purple_zulip_basic_response_deserialize))

/******************************************************************************
 * GetEventsResponse
 *****************************************************************************/
static gpointer
purple_zulip_get_events_response_copy_event(gconstpointer source,
                                            G_GNUC_UNUSED gpointer data)
{
	return purple_zulip_event_copy((PurpleZulipEvent *)source);
}

PurpleZulipGetEventsResponse *
purple_zulip_get_events_response_copy(PurpleZulipGetEventsResponse *response) {
	PurpleZulipGetEventsResponse *copy = NULL;

	g_return_val_if_fail(response != NULL, NULL);

	copy = g_new(PurpleZulipGetEventsResponse, 1);

	copy->events = g_list_copy_deep(response->events,
	                                purple_zulip_get_events_response_copy_event,
	                                NULL);

	return copy;
}

void
purple_zulip_get_events_response_free(PurpleZulipGetEventsResponse *response) {
	g_return_if_fail(response != NULL);

	g_list_free_full(response->events,
	                 (GDestroyNotify)purple_zulip_event_free);

	g_free(response);
}

static gpointer
purple_zulip_get_events_reponse_deserialize(JsonNode *node) {
	PurpleZulipGetEventsResponse *response = NULL;
	JsonArray *array = NULL;
	JsonObject *object = NULL;
	guint n_items = 0;

	response = g_new0(PurpleZulipGetEventsResponse, 1);

	object = json_node_get_object(node);

	array = json_object_get_array_member(object, "events");
	n_items = json_array_get_length(array);
	for(guint i = 0; i < n_items; i++) {
		PurpleZulipEvent *event = NULL;
		JsonNode *element = NULL;

		element = json_array_get_element(array, i);

		event = json_boxed_deserialize(PURPLE_ZULIP_TYPE_EVENT, element);

		response->events = g_list_append(response->events, event);
	}

	return response;
}

G_DEFINE_BOXED_TYPE_WITH_CODE(PurpleZulipGetEventsResponse,
                              purple_zulip_get_events_response,
                              purple_zulip_get_events_response_copy,
                              purple_zulip_get_events_response_free,
                              json_boxed_register_deserialize_func(g_define_type_id,
                                                                   JSON_NODE_OBJECT,
                                                                   purple_zulip_get_events_reponse_deserialize))

/******************************************************************************
 * FetchApiKeyResponse
 *****************************************************************************/
PurpleZulipFetchApiKeyResponse *
purple_zulip_fetch_api_key_response_copy(PurpleZulipFetchApiKeyResponse *response) {
	PurpleZulipFetchApiKeyResponse *copy = NULL;

	g_return_val_if_fail(response != NULL, NULL);

	copy = g_new(PurpleZulipFetchApiKeyResponse, 1);

	copy->api_key = g_strdup(response->api_key);
	copy->email = g_strdup(response->email);
	copy->user_id = g_strdup(response->user_id);

	return copy;
}

void
purple_zulip_fetch_api_key_response_free(PurpleZulipFetchApiKeyResponse *response) {
	g_return_if_fail(response != NULL);

	g_free(response->api_key);
	g_free(response->email);
	g_free(response->user_id);

	g_free(response);
}

static gpointer
purple_zulip_fetch_api_key_response_deserialize(JsonNode *node) {
	PurpleZulipFetchApiKeyResponse *response = NULL;
	JsonObject *object = NULL;
	const char *value = NULL;

	object = json_node_get_object(node);

	response = g_new(PurpleZulipFetchApiKeyResponse, 1);

	value = json_object_get_string_member_with_default(object, "api_key",
	                                                   NULL);
	response->api_key = g_strdup(value);

	value = json_object_get_string_member_with_default(object, "email", NULL);
	response->email = g_strdup(value);

	value = json_object_get_string_member_with_default(object, "user_id",
	                                                   NULL);
	response->user_id = g_strdup(value);

	return response;
}

G_DEFINE_BOXED_TYPE_WITH_CODE(PurpleZulipFetchApiKeyResponse,
                              purple_zulip_fetch_api_key_response,
                              purple_zulip_fetch_api_key_response_copy,
                              purple_zulip_fetch_api_key_response_free,
                              json_boxed_register_deserialize_func(g_define_type_id,
                                                                   JSON_NODE_OBJECT,
                                                                   purple_zulip_fetch_api_key_response_deserialize))

/******************************************************************************
 * RegisterEventQueueResponse
 *****************************************************************************/
static gpointer
purple_zulip_register_event_queue_response_copy_realm_user(gconstpointer src,
                                                           G_GNUC_UNUSED gpointer data)
{
	return purple_zulip_user_copy((gpointer)src);
}

PurpleZulipRegisterEventQueueResponse *
purple_zulip_register_event_queue_response_copy(PurpleZulipRegisterEventQueueResponse *response)
{
	PurpleZulipRegisterEventQueueResponse *copy = NULL;

	g_return_val_if_fail(response != NULL, NULL);

	copy = g_new(PurpleZulipRegisterEventQueueResponse, 1);

	PURPLE_ZULIP_COPY_STRING(avatar_source);
	PURPLE_ZULIP_COPY_STRING(avatar_url);
	PURPLE_ZULIP_COPY_STRING(avatar_url_medium);
	PURPLE_ZULIP_COPY_VALUE(can_create_private_streams);
	PURPLE_ZULIP_COPY_VALUE(can_create_public_streams);
	PURPLE_ZULIP_COPY_VALUE(can_create_streams);
	PURPLE_ZULIP_COPY_VALUE(can_create_web_public_streams);
	PURPLE_ZULIP_COPY_VALUE(can_invite_others_to_realm);
	PURPLE_ZULIP_COPY_VALUE(can_subscribe_other_users);
	PURPLE_ZULIP_COPY_STRING(delivery_email);
	PURPLE_ZULIP_COPY_VALUE(demo_organization_scheduled_deletion_date);
	PURPLE_ZULIP_COPY_VALUE(development_environment);
	PURPLE_ZULIP_COPY_STRING(email);
	PURPLE_ZULIP_COPY_VALUE(event_queue_longpoll_timeout_seconds);
	PURPLE_ZULIP_COPY_STRING(full_name);
	PURPLE_ZULIP_COPY_STRING(giphy_api_key);
	PURPLE_ZULIP_COPY_VALUE(has_zoom_token);
	PURPLE_ZULIP_COPY_VALUE(is_admin);
	PURPLE_ZULIP_COPY_VALUE(is_guest);
	PURPLE_ZULIP_COPY_VALUE(is_moderator);
	PURPLE_ZULIP_COPY_VALUE(is_owner);
	PURPLE_ZULIP_COPY_STRING(jitsi_server_url);
	PURPLE_ZULIP_COPY_VALUE(last_event_id);
	PURPLE_ZULIP_COPY_VALUE(max_avatar_file_size_mib);
	PURPLE_ZULIP_COPY_VALUE(max_bulk_new_subscription_messages);
	PURPLE_ZULIP_COPY_VALUE(max_channel_folder_description_length);
	PURPLE_ZULIP_COPY_VALUE(max_channel_folder_name_length);
	PURPLE_ZULIP_COPY_VALUE(max_file_upload_size_mib);
	PURPLE_ZULIP_COPY_VALUE(max_icon_file_size_mib);
	PURPLE_ZULIP_COPY_VALUE(max_logo_file_size_mib);
	PURPLE_ZULIP_COPY_VALUE(max_message_id);
	PURPLE_ZULIP_COPY_VALUE(max_message_length);
	PURPLE_ZULIP_COPY_VALUE(max_reminder_note_length);
	PURPLE_ZULIP_COPY_VALUE(max_stream_description_length);
	PURPLE_ZULIP_COPY_VALUE(max_stream_name_length);
	PURPLE_ZULIP_COPY_VALUE(max_topic_length);
	PURPLE_ZULIP_COPY_STRING(navigation_tour_video_url);
	PURPLE_ZULIP_COPY_VALUE(password_max_length);
	PURPLE_ZULIP_COPY_VALUE(password_min_guesses);
	PURPLE_ZULIP_COPY_VALUE(password_min_length);
	PURPLE_ZULIP_COPY_VALUE(presence_last_update_id);
	PURPLE_ZULIP_COPY_STRING(queue_id);
	PURPLE_ZULIP_COPY_VALUE(realm_allow_edit_history);
	PURPLE_ZULIP_COPY_VALUE(realm_allow_message_editing);
	PURPLE_ZULIP_COPY_VALUE(realm_avatar_changes_disabled);
	PURPLE_ZULIP_COPY_STRING(realm_bot_domain);
	PURPLE_ZULIP_COPY_VALUE(realm_create_public_stream_policy);
	PURPLE_ZULIP_COPY_VALUE(realm_create_private_stream_policy);
	PURPLE_ZULIP_COPY_VALUE(realm_create_web_public_stream_policy);
	PURPLE_ZULIP_COPY_VALUE(realm_date_created);
	PURPLE_ZULIP_COPY_STRING(realm_default_avatar_source);
	PURPLE_ZULIP_COPY_STRING(realm_default_code_block_language);
	PURPLE_ZULIP_COPY_STRING(realm_default_language);
	PURPLE_ZULIP_COPY_STRING(realm_description);
	PURPLE_ZULIP_COPY_VALUE(realm_digest_emails_enabled);
	PURPLE_ZULIP_COPY_VALUE(realm_digest_weekday);
	PURPLE_ZULIP_COPY_VALUE(realm_disallow_disposable_email_addresses);
	PURPLE_ZULIP_COPY_VALUE(realm_email_auth_enabled);
	PURPLE_ZULIP_COPY_VALUE(realm_email_changes_disabled);
	PURPLE_ZULIP_COPY_VALUE(realm_emails_restricted_to_domains);
	PURPLE_ZULIP_COPY_STRING(realm_empty_topic_display_name);
	PURPLE_ZULIP_COPY_VALUE(realm_enable_guest_user_dm_warning);
	PURPLE_ZULIP_COPY_VALUE(realm_enable_guest_user_indicator);
	PURPLE_ZULIP_COPY_VALUE(realm_enable_read_receipts);
	PURPLE_ZULIP_COPY_VALUE(realm_enable_spectator_access);
	PURPLE_ZULIP_COPY_VALUE(realm_gif_rating_policy);
	PURPLE_ZULIP_COPY_STRING(realm_icon_source);
	PURPLE_ZULIP_COPY_STRING(realm_icon_url);
	PURPLE_ZULIP_COPY_VALUE(realm_inline_image_preview);
	PURPLE_ZULIP_COPY_VALUE(realm_inline_url_embed_preview);
	PURPLE_ZULIP_COPY_VALUE(realm_invite_required);
	PURPLE_ZULIP_COPY_STRING(realm_jitsi_server_url);
	PURPLE_ZULIP_COPY_STRING(realm_logo_source);
	PURPLE_ZULIP_COPY_STRING(realm_logo_url);
	PURPLE_ZULIP_COPY_VALUE(realm_media_preview_size);
	PURPLE_ZULIP_COPY_VALUE(realm_message_content_allowed_in_email_notifications);
	PURPLE_ZULIP_COPY_VALUE(realm_message_content_delete_limit_seconds);
	PURPLE_ZULIP_COPY_VALUE(realm_message_content_edit_limit_seconds);
	PURPLE_ZULIP_COPY_VALUE(realm_message_edit_history_visibility_policy);
	PURPLE_ZULIP_COPY_VALUE(realm_message_retention_days);
	PURPLE_ZULIP_COPY_VALUE(realm_moderation_request_channel_id);
	PURPLE_ZULIP_COPY_VALUE(realm_move_messages_between_streams_limit_seconds);
	PURPLE_ZULIP_COPY_VALUE(realm_move_messages_within_stream_limit_seconds);
	PURPLE_ZULIP_COPY_STRING(realm_name);
	PURPLE_ZULIP_COPY_VALUE(realm_name_changes_disabled);
	PURPLE_ZULIP_COPY_VALUE(realm_new_stream_announcements_stream_id);
	PURPLE_ZULIP_COPY_STRING(realm_night_logo_source);
	PURPLE_ZULIP_COPY_STRING(realm_night_logo_url);
	PURPLE_ZULIP_COPY_VALUE(realm_org_type);
	PURPLE_ZULIP_COPY_VALUE(realm_owner_full_content_access);
	PURPLE_ZULIP_COPY_VALUE(realm_password_auth_enabled);
	PURPLE_ZULIP_COPY_VALUE(realm_plan_type);
	PURPLE_ZULIP_COPY_VALUE(realm_presence_disabled);
	PURPLE_ZULIP_COPY_VALUE(realm_push_notifications_enabled);
	PURPLE_ZULIP_COPY_VALUE(realm_push_notifications_enabled_end_timestamp);
	PURPLE_ZULIP_COPY_VALUE(realm_require_e2ee_push_notifications);
	PURPLE_ZULIP_COPY_VALUE(realm_require_unique_names);
	PURPLE_ZULIP_COPY_VALUE(realm_send_channel_events_messages);
	PURPLE_ZULIP_COPY_VALUE(realm_send_welcome_emails);
	PURPLE_ZULIP_COPY_VALUE(realm_signup_announcements_stream_id);
	PURPLE_ZULIP_COPY_STRING(realm_topics_policy);
	PURPLE_ZULIP_COPY_VALUE(realm_upload_quota_mib);
	PURPLE_ZULIP_COPY_STRING(realm_uri);
	PURPLE_ZULIP_COPY_STRING(realm_url);
	copy->realm_users = g_ptr_array_copy(response->realm_users,
	                                     purple_zulip_register_event_queue_response_copy_realm_user,
	                                     NULL);
	PURPLE_ZULIP_COPY_STRING(realm_uuid);
	PURPLE_ZULIP_COPY_VALUE(realm_video_chat_provider);
	PURPLE_ZULIP_COPY_VALUE(realm_waiting_period_threshold);
	PURPLE_ZULIP_COPY_VALUE(realm_want_advertise_in_communities_directory);
	PURPLE_ZULIP_COPY_STRING(realm_welcome_message_custom_text);
	PURPLE_ZULIP_COPY_VALUE(realm_wildcard_mention_policy);
	PURPLE_ZULIP_COPY_VALUE(realm_zulip_update_announcements_stream_id);
	PURPLE_ZULIP_COPY_VALUE(receives_typing_notifications);
	PURPLE_ZULIP_COPY_VALUE(server_avatar_changes_disabled);
	PURPLE_ZULIP_COPY_VALUE(server_can_summarize_topics);
	PURPLE_ZULIP_COPY_STRING(server_emoji_data_url);
	PURPLE_ZULIP_COPY_VALUE(server_generation);
	PURPLE_ZULIP_COPY_VALUE(server_inline_image_preview);
	PURPLE_ZULIP_COPY_VALUE(server_inline_url_embed_preview);
	PURPLE_ZULIP_COPY_STRING(server_jitsi_server_url);
	PURPLE_ZULIP_COPY_VALUE(server_min_deactivated_realm_deletion_days);
	PURPLE_ZULIP_COPY_VALUE(server_name_changes_disabled);
	PURPLE_ZULIP_COPY_VALUE(server_needs_upgrade);
	PURPLE_ZULIP_COPY_VALUE(server_presence_offline_threshold_seconds);
	PURPLE_ZULIP_COPY_VALUE(server_presence_ping_interval_seconds);
	PURPLE_ZULIP_COPY_VALUE(server_timestamp);
	PURPLE_ZULIP_COPY_VALUE(server_typing_started_expiry_period_milliseconds);
	PURPLE_ZULIP_COPY_VALUE(server_typing_started_wait_period_milliseconds);
	PURPLE_ZULIP_COPY_VALUE(server_typing_stopped_wait_period_milliseconds);
	PURPLE_ZULIP_COPY_VALUE(server_web_public_streams_enabled);
	PURPLE_ZULIP_COPY_VALUE(settings_send_digest_emails);
	PURPLE_ZULIP_COPY_STRING(upgrade_text_for_wide_organization_logo);
	PURPLE_ZULIP_COPY_VALUE(user_id);
	PURPLE_ZULIP_COPY_VALUE(zulip_feature_level);
	PURPLE_ZULIP_COPY_STRING(zulip_merge_base);
	PURPLE_ZULIP_COPY_VALUE(zulip_plan_is_not_limited);
	PURPLE_ZULIP_COPY_STRING(zulip_version);

	return copy;
}

void
purple_zulip_register_event_queue_response_free(PurpleZulipRegisterEventQueueResponse *response)
{
	g_return_if_fail(response != NULL);

	g_free(response->avatar_source);
	g_free(response->avatar_url);
	g_free(response->avatar_url_medium);
	g_free(response->delivery_email);
	g_free(response->email);
	g_free(response->full_name);
	g_free(response->giphy_api_key);
	g_free(response->jitsi_server_url);
	g_free(response->navigation_tour_video_url);
	g_free(response->queue_id);
	g_free(response->realm_bot_domain);
	g_free(response->realm_default_code_block_language);
	g_free(response->realm_description);
	g_free(response->realm_default_avatar_source);
	g_free(response->realm_default_language);
	g_free(response->realm_empty_topic_display_name);
	g_free(response->realm_icon_source);
	g_free(response->realm_icon_url);
	g_free(response->realm_jitsi_server_url);
	g_free(response->realm_logo_source);
	g_free(response->realm_logo_url);
	g_free(response->realm_name);
	g_free(response->realm_night_logo_source);
	g_free(response->realm_night_logo_url);
	g_free(response->realm_topics_policy);
	g_free(response->realm_uri);
	g_free(response->realm_url);
	g_ptr_array_unref(response->realm_users);
	g_free(response->realm_uuid);
	g_free(response->realm_welcome_message_custom_text);
	g_free(response->server_emoji_data_url);
	g_free(response->server_jitsi_server_url);
	g_free(response->upgrade_text_for_wide_organization_logo);
	g_free(response->zulip_merge_base);
	g_free(response->zulip_version);

	g_free(response);
}

static gpointer
purple_zulip_register_event_queue_response_deserialize(JsonNode *node) {
	PurpleZulipRegisterEventQueueResponse *response = NULL;
	JsonObject *object = NULL;
	const char *value = NULL;

	response = g_new0(PurpleZulipRegisterEventQueueResponse, 1);

	object = json_node_get_object(node);

	PURPLE_ZULIP_RESPONSE_GET_STRING(avatar_source);
	PURPLE_ZULIP_RESPONSE_GET_STRING(avatar_url);
	PURPLE_ZULIP_RESPONSE_GET_STRING(avatar_url_medium);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(can_create_private_streams);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(can_create_public_streams);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(can_create_streams);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(can_create_web_public_streams);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(can_invite_others_to_realm);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(can_subscribe_other_users);
	PURPLE_ZULIP_RESPONSE_GET_STRING(delivery_email);
	PURPLE_ZULIP_RESPONSE_GET_INT(demo_organization_scheduled_deletion_date);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(development_environment);
	PURPLE_ZULIP_RESPONSE_GET_STRING(email);
	PURPLE_ZULIP_RESPONSE_GET_INT(event_queue_longpoll_timeout_seconds);
	PURPLE_ZULIP_RESPONSE_GET_STRING(full_name);
	PURPLE_ZULIP_RESPONSE_GET_STRING(giphy_api_key);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(has_zoom_token);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(is_admin);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(is_guest);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(is_moderator);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(is_owner);
	PURPLE_ZULIP_RESPONSE_GET_STRING(jitsi_server_url);
	PURPLE_ZULIP_RESPONSE_GET_INT(last_event_id);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_avatar_file_size_mib);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_bulk_new_subscription_messages);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_channel_folder_description_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_channel_folder_name_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_file_upload_size_mib);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_icon_file_size_mib);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_logo_file_size_mib);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_message_id);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_message_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_reminder_note_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_stream_description_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_stream_name_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(max_topic_length);
	PURPLE_ZULIP_RESPONSE_GET_STRING(navigation_tour_video_url);
	PURPLE_ZULIP_RESPONSE_GET_INT(password_max_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(password_min_guesses);
	PURPLE_ZULIP_RESPONSE_GET_INT(password_min_length);
	PURPLE_ZULIP_RESPONSE_GET_INT(presence_last_update_id);
	PURPLE_ZULIP_RESPONSE_GET_STRING(queue_id);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_allow_edit_history);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_allow_message_editing);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_avatar_changes_disabled);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_bot_domain);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_create_public_stream_policy);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_create_private_stream_policy);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_create_web_public_stream_policy);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_date_created);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_default_avatar_source);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_default_code_block_language);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_default_language);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_description);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_digest_emails_enabled);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_digest_weekday);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_disallow_disposable_email_addresses);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_email_auth_enabled);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_email_changes_disabled);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_emails_restricted_to_domains);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_empty_topic_display_name);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_enable_guest_user_dm_warning);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_enable_guest_user_indicator);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_enable_read_receipts);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_enable_spectator_access);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_gif_rating_policy);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_icon_source);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_icon_url);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_inline_image_preview);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_inline_url_embed_preview);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_invite_required);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_jitsi_server_url);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_logo_source);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_logo_url);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_media_preview_size);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_message_content_allowed_in_email_notifications);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_message_content_delete_limit_seconds);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_message_content_edit_limit_seconds);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_message_edit_history_visibility_policy);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_message_retention_days);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_moderation_request_channel_id);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_move_messages_between_streams_limit_seconds);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_move_messages_within_stream_limit_seconds);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_name);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_name_changes_disabled);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_new_stream_announcements_stream_id);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_night_logo_source);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_night_logo_url);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_org_type);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_owner_full_content_access);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_password_auth_enabled);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_plan_type);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_presence_disabled);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_push_notifications_enabled);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_push_notifications_enabled_end_timestamp);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_require_e2ee_push_notifications);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_require_unique_names);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_send_channel_events_messages);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_send_welcome_emails);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_signup_announcements_stream_id);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_topics_policy);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_upload_quota_mib);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_uri);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_url);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_uuid);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_video_chat_provider);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_waiting_period_threshold);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(realm_want_advertise_in_communities_directory);
	PURPLE_ZULIP_RESPONSE_GET_STRING(realm_welcome_message_custom_text);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_wildcard_mention_policy);
	PURPLE_ZULIP_RESPONSE_GET_INT(realm_zulip_update_announcements_stream_id);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(receives_typing_notifications);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_avatar_changes_disabled);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_can_summarize_topics);
	PURPLE_ZULIP_RESPONSE_GET_STRING(server_emoji_data_url);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_generation);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_inline_image_preview);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_inline_url_embed_preview);
	PURPLE_ZULIP_RESPONSE_GET_STRING(server_jitsi_server_url);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_min_deactivated_realm_deletion_days);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_name_changes_disabled);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_needs_upgrade);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_presence_offline_threshold_seconds);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_presence_ping_interval_seconds);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_timestamp);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_typing_started_expiry_period_milliseconds);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_typing_started_wait_period_milliseconds);
	PURPLE_ZULIP_RESPONSE_GET_INT(server_typing_stopped_wait_period_milliseconds);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(server_web_public_streams_enabled);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(settings_send_digest_emails);
	PURPLE_ZULIP_RESPONSE_GET_STRING(upgrade_text_for_wide_organization_logo);
	PURPLE_ZULIP_RESPONSE_GET_INT(user_id);
	PURPLE_ZULIP_RESPONSE_GET_INT(zulip_feature_level);
	PURPLE_ZULIP_RESPONSE_GET_STRING(zulip_merge_base);
	PURPLE_ZULIP_RESPONSE_GET_BOOLEAN(zulip_plan_is_not_limited);
	PURPLE_ZULIP_RESPONSE_GET_STRING(zulip_version);

	response->realm_users = g_ptr_array_new_full(1,
	                                             (GDestroyNotify)purple_zulip_user_free);
	PURPLE_ZULIP_RESPONSE_GET_ARRAY(realm_users, PURPLE_ZULIP_TYPE_USER,
	                                PurpleZulipUser, purple_zulip_user_free);

	return response;
}

G_DEFINE_BOXED_TYPE_WITH_CODE(PurpleZulipRegisterEventQueueResponse,
                              purple_zulip_register_event_queue_response,
                              purple_zulip_register_event_queue_response_copy,
                              purple_zulip_register_event_queue_response_free,
                              json_boxed_register_deserialize_func(g_define_type_id,
                                                                   JSON_NODE_OBJECT,
                                                                   purple_zulip_register_event_queue_response_deserialize))

/******************************************************************************
 * ServerSettingsResponse
 *****************************************************************************/
PurpleZulipServerSettingsResponse *
purple_zulip_server_settings_response_copy(PurpleZulipServerSettingsResponse *response)
{
	PurpleZulipServerSettingsResponse *copy = NULL;

	g_return_val_if_fail(response != NULL, NULL);

	copy = g_new(PurpleZulipServerSettingsResponse, 1);

	copy->email_auth_enabled = response->email_auth_enabled;
	copy->is_incompatible = response->is_incompatible;
	copy->push_notifications_enabled = response->push_notifications_enabled;
	copy->zulip_merge_base = g_strdup(response->zulip_merge_base);
	copy->zulip_version = g_strdup(response->zulip_version);

	return copy;
}

void
purple_zulip_server_settings_response_free(PurpleZulipServerSettingsResponse *response)
{
	g_return_if_fail(response != NULL);

	g_free(response->zulip_merge_base);
	g_free(response->zulip_version);

	g_free(response);
}

static gpointer
purple_zulip_server_settings_response_deserialize(JsonNode *node) {
	PurpleZulipServerSettingsResponse *response = NULL;
	JsonObject *object = NULL;
	const char *value = NULL;

	response = g_new(PurpleZulipServerSettingsResponse, 1);

	object = json_node_get_object(node);

	response->email_auth_enabled = json_object_get_boolean_member_with_default(object,
	                                                                           "email_auth_enabled",
	                                                                           FALSE);

	response->is_incompatible = json_object_get_boolean_member_with_default(object,
	                                                                        "is_incompatible",
	                                                                        FALSE);

	response->push_notifications_enabled = json_object_get_boolean_member_with_default(object,
	                                                                                   "push_notifications_enabled",
	                                                                                   FALSE);

	value = json_object_get_string_member_with_default(object, "zulip_merge_base",
	                                                   NULL);
	response->zulip_merge_base = g_strdup(value);

	value = json_object_get_string_member_with_default(object, "zulip_version",
	                                                   NULL);
	response->zulip_version = g_strdup(value);

	return response;
}

G_DEFINE_BOXED_TYPE_WITH_CODE(PurpleZulipServerSettingsResponse,
                              purple_zulip_server_settings_response,
                              purple_zulip_server_settings_response_copy,
                              purple_zulip_server_settings_response_free,
                              json_boxed_register_deserialize_func(g_define_type_id,
                                                                   JSON_NODE_OBJECT,
                                                                   purple_zulip_server_settings_response_deserialize))
