#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include <gromox/common_types.hpp>
#include <gromox/defs.h>
#include <gromox/element_data.hpp>
#include <gromox/ext_buffer.hpp>
#include <gromox/idset.hpp>
#include <gromox/mapi_types.hpp>
#include <gromox/mapidefs.h>
#include <gromox/mapierr.hpp>
#include <gromox/notify_types.hpp>

enum class exmdb_response : uint8_t {
	success = 0x00,
	access_deny = 0x01,
	max_reached = 0x02,
	lack_memory = 0x03,
	misconfig_prefix = 0x04,
	misconfig_mode = 0x05,
	connect_incomplete = 0x06,
	pull_error = 0x07,
	dispatch_error = 0x08,
	push_error = 0x09,
	invalid = 0xff,
};

#define EDEF(t, id) t = (id),
#define EOBSOL(t, id)

enum class exmdb_callid : uint8_t {
	#include <gromox/exmdb_allcalls.hpp>
};

#undef EDEF
#undef EOBSOL

struct exreq {
	using view_t = exreq;
	exreq() = default; /* Prevent use of direct-list-init */
	virtual ~exreq() = default;
	exmdb_callid call_id{};
	char *dir = nullptr;
};

struct exreq_connect final : public exreq {
	using view_t = exreq_connect;
	char *prefix = nullptr, *remote_id = nullptr;
	BOOL b_private = true;
};

struct exreq_listen_notification final : public exreq {
	using view_t = exreq_listen_notification;
	char *remote_id = nullptr;
};

struct exreq_get_named_propids final : public exreq {
	using view_t = exreq_get_named_propids;
	BOOL b_create = false;
	PROPNAME_ARRAY *ppropnames = nullptr;
};

struct exreq_get_named_propnames final : public exreq {
	using view_t = exreq_get_named_propnames;
	PROPID_ARRAY ppropids{};
};

struct exreq_get_mapping_guid final : public exreq {
	using view_t = exreq_get_mapping_guid;
	uint16_t replid = 0;
};

struct exreq_get_mapping_replid final : public exreq {
	using view_t = exreq_get_mapping_replid;
	GUID guid{};
};

struct exreq_get_store_properties_v final : public exreq {
	cpid_t cpid{};
	proptag_cspan pproptags;
};

struct exreq_get_store_properties final : public exreq {
	using view_t = exreq_get_store_properties_v;
	operator view_t() const { view_t v; v.cpid = cpid; v.pproptags = pproptags; return v; }
	cpid_t cpid{};
	proptag_vector pproptags;
};

struct exreq_set_store_properties final : public exreq {
	using view_t = exreq_set_store_properties;
	cpid_t cpid{};
	TPROPVAL_ARRAY *ppropvals = nullptr;
};

/* @cpid fields unused, always CP_UTF8 */
using exreq_autoreply_getprop = exreq_get_store_properties;
using exreq_autoreply_setprop = exreq_set_store_properties;

struct exreq_remove_store_properties_v final : public exreq {
	proptag_cspan pproptags;
};

struct exreq_remove_store_properties final : public exreq {
	using view_t = exreq_remove_store_properties_v;
	proptag_vector pproptags;
	operator view_t() const { view_t v; v.pproptags = pproptags; return v; }
};

struct exreq_get_mbox_perm final : public exreq {
	using view_t = exreq_get_mbox_perm;
	char *username = nullptr;
};

struct exreq_get_folder_by_class final : public exreq {
	using view_t = exreq_get_folder_by_class;
	char *str_class = nullptr;
};

struct exreq_set_folder_by_class final : public exreq {
	using view_t = exreq_set_folder_by_class;
	uint64_t folder_id = 0;
	char *str_class = nullptr;
};

struct exreq_is_folder_present final : public exreq {
	using view_t = exreq_is_folder_present;
	uint64_t folder_id = 0;
};

struct exreq_is_folder_deleted final : public exreq {
	using view_t = exreq_is_folder_deleted;
	uint64_t folder_id = 0;
};

struct exreq_get_folder_by_name final : public exreq {
	using view_t = exreq_get_folder_by_name;
	uint64_t parent_id = 0;
	char *str_name = nullptr;
};

struct exreq_get_folder_perm final : public exreq {
	using view_t = exreq_get_folder_perm;
	uint64_t folder_id = 0;
	char *username = nullptr;
};

struct exreq_create_folder final : public exreq {
	using view_t = exreq_create_folder;
	cpid_t cpid{};
	TPROPVAL_ARRAY *pproperties;
};

struct exreq_delete_folder final : public exreq {
	using view_t = exreq_delete_folder;
	cpid_t cpid{};
	uint64_t folder_id = 0;
	BOOL b_hard = false;
};

struct exreq_get_folder_all_proptags final : public exreq {
	using view_t = exreq_get_folder_all_proptags;
	uint64_t folder_id = 0;
};

struct exreq_get_folder_properties_v final : public exreq {
	cpid_t cpid{};
	uint64_t folder_id = 0;
	proptag_cspan pproptags;
};

struct exreq_get_folder_properties final : public exreq {
	using view_t = exreq_get_folder_properties_v;
	cpid_t cpid{};
	uint64_t folder_id = 0;
	proptag_vector pproptags;
	operator view_t() const {
		view_t v;
		v.cpid = cpid;
		v.folder_id = folder_id;
		v.pproptags = pproptags;
		return v;
	}
};

struct exreq_set_folder_properties final : public exreq {
	using view_t = exreq_set_folder_properties;
	cpid_t cpid{};
	uint64_t folder_id = 0;
	TPROPVAL_ARRAY *pproperties = nullptr;
};

struct exreq_remove_folder_properties_v final : public exreq {
	uint64_t folder_id = 0;
	proptag_cspan pproptags;
};

struct exreq_remove_folder_properties final : public exreq {
	using view_t = exreq_remove_folder_properties_v;
	uint64_t folder_id = 0;
	proptag_vector pproptags;
	operator view_t() const { view_t v; v.folder_id = folder_id; v.pproptags = pproptags; return v; }
};

struct exreq_empty_folder final : public exreq {
	using view_t = exreq_empty_folder;
	cpid_t cpid{};
	char *username = nullptr;
	uint64_t folder_id = 0;
	uint32_t flags = 0;
};

struct exreq_is_descendant_folder final : public exreq {
	using view_t = exreq_is_descendant_folder;
	uint64_t parent_fid = 0, child_fid = 0;
};

struct exreq_copy_folder_internal final : public exreq {
	using view_t = exreq_copy_folder_internal;
	int32_t account_id = 0;
	cpid_t cpid{};
	char *username = nullptr;
	uint64_t src_fid = 0, dst_fid = 0;
	BOOL b_guest = false, b_normal = false, b_fai = false, b_sub = false;
};

struct exreq_get_search_criteria final : public exreq {
	using view_t = exreq_get_search_criteria;
	uint64_t folder_id = 0;
};

struct exreq_set_search_criteria final : public exreq {
	using view_t = exreq_set_search_criteria;
	cpid_t cpid{};
	uint64_t folder_id = 0;
	uint32_t search_flags = 0;
	RESTRICTION *prestriction = nullptr;
	EID_ARRAY *pfolder_ids = nullptr;
};

struct exreq_movecopy_message final : public exreq {
	using view_t = exreq_movecopy_message;
	cpid_t cpid{};
	uint64_t message_id = 0, dst_fid = 0, dst_id = 0;
	BOOL b_move = false;
};

struct exreq_movecopy_messages final : public exreq {
	using view_t = exreq_movecopy_messages;
	cpid_t cpid{};
	BOOL b_guest = false, b_copy = false;
	char *username = nullptr;
	uint64_t src_fid = 0, dst_fid = 0;
	EID_ARRAY *pmessage_ids = nullptr;
};

struct exreq_movecopy_folder final : public exreq {
	using view_t = exreq_movecopy_folder;
	cpid_t cpid{};
	BOOL b_guest = false, b_copy = false;
	char *username = nullptr, *str_new = nullptr;
	uint64_t src_pid = 0, src_fid = 0, dst_fid = 0;
};

struct exreq_delete_messages final : public exreq {
	using view_t = exreq_delete_messages;
	int32_t account_id = 0;
	cpid_t cpid{};
	char *username = nullptr;
	uint64_t folder_id = 0;
	EID_ARRAY *pmessage_ids = nullptr;
	BOOL b_hard = false;
};

struct exreq_get_message_brief final : public exreq {
	using view_t = exreq_get_message_brief;
	cpid_t cpid{};
	uint64_t message_id = 0;
};

struct exreq_sum_hierarchy final : public exreq {
	using view_t = exreq_sum_hierarchy;
	uint64_t folder_id = 0;
	char *username = nullptr;
	BOOL b_depth = false;
};

struct exreq_sum_content final : public exreq {
	using view_t = exreq_sum_content;
	uint64_t folder_id = 0;
	BOOL b_fai = false, b_deleted = false;
};

struct exreq_load_hierarchy_table final : public exreq {
	using view_t = exreq_load_hierarchy_table;
	uint64_t folder_id = 0;
	char *username = nullptr;
	uint8_t table_flags = 0;
	RESTRICTION *prestriction = nullptr;
};

struct exreq_load_content_table final : public exreq {
	using view_t = exreq_load_content_table;
	cpid_t cpid{};
	uint64_t folder_id = 0;
	char *username = nullptr;
	uint8_t table_flags = 0;
	RESTRICTION *prestriction = nullptr;
	SORTORDER_SET *psorts = nullptr;
};

struct exreq_reload_content_table final : public exreq {
	using view_t = exreq_reload_content_table;
	uint32_t table_id = 0;
};

struct exreq_load_permission_table final : public exreq {
	using view_t = exreq_load_permission_table;
	uint64_t folder_id = 0;
	uint32_t table_flags = 0;
};

struct exreq_load_rule_table final : public exreq {
	using view_t = exreq_load_rule_table;
	uint64_t folder_id = 0;
	uint8_t table_flags = 0;
	RESTRICTION *prestriction = nullptr;
};

struct exreq_unload_table final : public exreq {
	using view_t = exreq_unload_table;
	uint32_t table_id = 0;
};

struct exreq_sum_table final : public exreq {
	using view_t = exreq_sum_table;
	uint32_t table_id = 0;
};

struct exreq_query_table_v final : public exreq {
	const char *username;
	cpid_t cpid{};
	proptag_cspan pproptags;
	uint32_t table_id = 0, start_pos = 0;
	int32_t row_needed = 0;
};

struct exreq_query_table final : public exreq {
	using view_t = exreq_query_table_v;
	char *username = nullptr;
	cpid_t cpid{};
	proptag_vector pproptags;
	uint32_t table_id = 0, start_pos = 0;
	int32_t row_needed = 0;
	operator view_t() const {
		view_t v;
		v.username = username;
		v.cpid = cpid;
		v.table_id = table_id;
		v.pproptags = pproptags;
		v.start_pos = start_pos;
		v.row_needed = row_needed;
		return v;
	}
};

struct exreq_match_table_v final : public exreq {
	const char *username = nullptr;
	cpid_t cpid{};
	uint32_t table_id = 0, start_pos = 0;
	BOOL b_forward = false;
	RESTRICTION *pres = nullptr;
	proptag_cspan pproptags;
};

struct exreq_match_table final : public exreq {
	using view_t = exreq_match_table_v;
	char *username = nullptr;
	cpid_t cpid{};
	uint32_t table_id = 0, start_pos = 0;
	BOOL b_forward = false;
	RESTRICTION *pres = nullptr;
	proptag_vector pproptags;
	operator view_t() const {
		view_t v;
		v.username = username;
		v.cpid = cpid;
		v.table_id = table_id;
		v.b_forward = b_forward;
		v.start_pos = start_pos;
		v.pres = pres;
		v.pproptags = pproptags;
		return v;
	}
};

struct exreq_locate_table final : public exreq {
	using view_t = exreq_locate_table;
	uint32_t table_id = 0, inst_num = 0;
	uint64_t inst_id = 0;
};

struct exreq_read_table_row_v final : public exreq {
	const char *username;
	cpid_t cpid{};
	uint32_t table_id = 0, inst_num = 0;
	uint64_t inst_id = 0;
	proptag_cspan pproptags;
};

struct exreq_read_table_row final : public exreq {
	using view_t = exreq_read_table_row_v;
	char *username = nullptr;
	proptag_vector pproptags;
	cpid_t cpid{};
	uint32_t table_id = 0, inst_num = 0;
	uint64_t inst_id = 0;
	operator view_t() const {
		view_t v;
		v.username = username;
		v.cpid = cpid;
		v.table_id = table_id;
		v.pproptags = pproptags;
		v.inst_id = inst_id;
		v.inst_num = inst_num;
		return v;
	}
};

struct exreq_mark_table final : public exreq {
	using view_t = exreq_mark_table;
	uint32_t table_id = 0, position = 0;
};

struct exreq_get_table_all_proptags final : public exreq {
	using view_t = exreq_get_table_all_proptags;
	uint32_t table_id = 0;
};

struct exreq_expand_table final : public exreq {
	using view_t = exreq_expand_table;
	uint32_t table_id = 0;
	uint64_t inst_id = 0;
};

struct exreq_collapse_table final : public exreq {
	using view_t = exreq_collapse_table;
	uint32_t table_id = 0;
	uint64_t inst_id = 0;
};

struct exreq_store_table_state final : public exreq {
	using view_t = exreq_store_table_state;
	uint32_t table_id = 0, inst_num = 0;
	uint64_t inst_id = 0;
};

struct exreq_restore_table_state final : public exreq {
	using view_t = exreq_restore_table_state;
	uint32_t table_id = 0, state_id = 0;
};

struct exreq_is_msg_present final : public exreq {
	using view_t = exreq_is_msg_present;
	uint64_t folder_id = 0, message_id = 0;
};

struct exreq_is_msg_deleted final : public exreq {
	using view_t = exreq_is_msg_deleted;
	uint64_t message_id = 0;
};

struct exreq_load_message_instance final : public exreq {
	using view_t = exreq_load_message_instance;
	char *username = nullptr;
	cpid_t cpid{};
	BOOL b_new = false;
	uint64_t folder_id = 0, message_id = 0;
};

struct exreq_load_embedded_instance final : public exreq {
	using view_t = exreq_load_embedded_instance;
	BOOL b_new = false;
	uint32_t attachment_instance_id = 0;
};

struct exreq_get_embedded_cn final : public exreq {
	using view_t = exreq_get_embedded_cn;
	uint32_t instance_id = 0;
};

struct exreq_reload_message_instance final : public exreq {
	using view_t = exreq_reload_message_instance;
	uint32_t instance_id = 0;
};

struct exreq_clear_message_instance final : public exreq {
	using view_t = exreq_clear_message_instance;
	uint32_t instance_id = 0;
};

struct exreq_read_message_instance final : public exreq {
	using view_t = exreq_read_message_instance;
	uint32_t instance_id = 0;
};

struct exreq_write_message_instance final : public exreq {
	using view_t = exreq_write_message_instance;
	uint32_t instance_id = 0;
	MESSAGE_CONTENT *pmsgctnt = nullptr;
	BOOL b_force = false;
};

struct exreq_load_attachment_instance final : public exreq {
	using view_t = exreq_load_attachment_instance;
	uint32_t message_instance_id = 0, attachment_num = 0;
};

struct exreq_create_attachment_instance final : public exreq {
	using view_t = exreq_create_attachment_instance;
	uint32_t message_instance_id = 0;
};

struct exreq_read_attachment_instance final : public exreq {
	using view_t = exreq_read_attachment_instance;
	uint32_t instance_id = 0;
};

struct exreq_write_attachment_instance final : public exreq {
	using view_t = exreq_write_attachment_instance;
	uint32_t instance_id = 0;
	ATTACHMENT_CONTENT *pattctnt = nullptr;
	BOOL b_force = false;
};

struct exreq_delete_message_instance_attachment final : public exreq {
	using view_t = exreq_delete_message_instance_attachment;
	uint32_t message_instance_id = 0, attachment_num = 0;
};

struct exreq_flush_instance final : public exreq {
	using view_t = exreq_flush_instance;
	uint32_t instance_id = 0;
};

struct exreq_unload_instance final : public exreq {
	using view_t = exreq_unload_instance;
	uint32_t instance_id = 0;
};

struct exreq_get_instance_all_proptags final : public exreq {
	using view_t = exreq_get_instance_all_proptags;
	uint32_t instance_id = 0;
};

struct exreq_get_instance_properties_v final : public exreq {
	uint32_t size_limit = 0, instance_id = 0;
	proptag_cspan pproptags;
};

struct exreq_get_instance_properties final : public exreq {
	using view_t = exreq_get_instance_properties_v;
	uint32_t size_limit = 0, instance_id = 0;
	proptag_vector pproptags;
	operator view_t() const {
		view_t v;
		v.size_limit = size_limit;
		v.instance_id = instance_id;
		v.pproptags = pproptags;
		return v;
	}
};

struct exreq_set_instance_properties final : public exreq {
	using view_t = exreq_set_instance_properties;
	uint32_t instance_id = 0;
	TPROPVAL_ARRAY *pproperties = nullptr;
};

struct exreq_remove_instance_properties_v final : public exreq {
	uint32_t instance_id = 0;
	proptag_cspan pproptags;
};

struct exreq_remove_instance_properties final : public exreq {
	using view_t = exreq_remove_instance_properties_v;
	uint32_t instance_id = 0;
	proptag_vector pproptags;
	operator view_t() const { view_t v; v.instance_id = instance_id; v.pproptags = pproptags; return v; }
};

struct exreq_is_descendant_instance final : public exreq {
	using view_t = exreq_is_descendant_instance;
	uint32_t parent_iid = 0, child_iid = 0;
};

struct exreq_empty_message_instance_rcpts final : public exreq {
	using view_t = exreq_empty_message_instance_rcpts;
	uint32_t instance_id = 0;
};

struct exreq_get_message_instance_rcpts_num final : public exreq {
	using view_t = exreq_get_message_instance_rcpts_num;
	uint32_t instance_id = 0;
};

struct exreq_get_message_instance_rcpts_all_proptags final : public exreq {
	using view_t = exreq_get_message_instance_rcpts_all_proptags;
	uint32_t instance_id = 0;
};

struct exreq_get_message_instance_rcpts final : public exreq {
	using view_t = exreq_get_message_instance_rcpts;
	uint32_t instance_id = 0, row_id = 0;
	uint16_t need_count = 0;
};

struct exreq_update_message_instance_rcpts final : public exreq {
	using view_t = exreq_update_message_instance_rcpts;
	uint32_t instance_id = 0;
	TARRAY_SET *pset = nullptr;
};

struct exreq_copy_instance_rcpts final : public exreq {
	using view_t = exreq_copy_instance_rcpts;
	BOOL b_force = false;
	uint32_t src_instance_id = 0, dst_instance_id = 0;
};

struct exreq_empty_message_instance_attachments final : public exreq {
	using view_t = exreq_empty_message_instance_attachments;
	uint32_t instance_id = 0;
};

struct exreq_get_message_instance_attachments_num final : public exreq {
	using view_t = exreq_get_message_instance_attachments_num;
	uint32_t instance_id = 0;
};

struct exreq_get_message_instance_attachment_table_all_proptags final : public exreq {
	using view_t = exreq_get_message_instance_attachment_table_all_proptags;
	uint32_t instance_id = 0;
};

struct exreq_query_message_instance_attachment_table_v final : public exreq {
	proptag_cspan pproptags;
	uint32_t instance_id = 0, start_pos = 0;
	int32_t row_needed = 0;
};

struct exreq_query_message_instance_attachment_table final : public exreq {
	using view_t = exreq_query_message_instance_attachment_table_v;
	proptag_vector pproptags;
	uint32_t instance_id = 0, start_pos = 0;
	int32_t row_needed = 0;
	operator view_t() const {
		view_t v;
		v.instance_id = instance_id;
		v.pproptags = pproptags;
		v.start_pos = start_pos;
		v.row_needed = row_needed;
		return v;
	}
};

struct exreq_copy_instance_attachments final : public exreq {
	using view_t = exreq_copy_instance_attachments;
	BOOL b_force = false;
	uint32_t src_instance_id = 0, dst_instance_id = 0;
};

struct exreq_set_message_instance_conflict final : public exreq {
	using view_t = exreq_set_message_instance_conflict;
	uint32_t instance_id = 0;
	MESSAGE_CONTENT *pmsgctnt = nullptr;
};

struct exreq_get_message_rcpts final : public exreq {
	using view_t = exreq_get_message_rcpts;
	uint64_t message_id = 0;
};

struct exreq_get_message_properties_v final : public exreq {
	const char *username = nullptr;
	cpid_t cpid{};
	uint64_t message_id = 0;
	proptag_cspan pproptags;
};

struct exreq_get_message_properties final : public exreq {
	using view_t = exreq_get_message_properties_v;
	char *username = nullptr;
	cpid_t cpid{};
	uint64_t message_id = 0;
	proptag_vector pproptags;
	operator view_t() const {
		view_t v;
		v.username = username;
		v.cpid = cpid;
		v.message_id = message_id;
		v.pproptags = pproptags;
		return v;
	}
};

struct exreq_set_message_properties final : public exreq {
	using view_t = exreq_set_message_properties;
	char *username = nullptr;
	cpid_t cpid{};
	uint64_t message_id = 0;
	TPROPVAL_ARRAY *pproperties;
};

struct exreq_set_message_read_state final : public exreq {
	using view_t = exreq_set_message_read_state;
	char *username = nullptr;
	uint64_t message_id = 0;
	uint8_t mark_as_read = false;
};

struct exreq_remove_message_properties_v final : public exreq {
	cpid_t cpid{};
	uint64_t message_id = 0;
	proptag_cspan pproptags;
};

struct exreq_remove_message_properties final : public exreq {
	using view_t = exreq_remove_message_properties_v;
	cpid_t cpid{};
	uint64_t message_id = 0;
	proptag_vector pproptags;
	operator view_t() const {
		view_t v;
		v.cpid = cpid;
		v.message_id = message_id;
		v.pproptags = pproptags;
		return v;
	}
};

struct exreq_allocate_message_id final : public exreq {
	using view_t = exreq_allocate_message_id;
	uint64_t folder_id = 0;
};

struct exreq_mark_modified final : public exreq {
	using view_t = exreq_mark_modified;
	uint64_t message_id = 0;
};

struct exreq_try_mark_submit final : public exreq {
	using view_t = exreq_try_mark_submit;
	uint64_t message_id = 0;
};

struct exreq_clear_submit final : public exreq {
	using view_t = exreq_clear_submit;
	uint64_t message_id = 0;
	BOOL b_unsent = false;
};

struct exreq_link_message final : public exreq {
	using view_t = exreq_link_message;
	cpid_t cpid{};
	uint64_t folder_id = 0, message_id = 0;
};

struct exreq_unlink_message final : public exreq {
	using view_t = exreq_unlink_message;
	cpid_t cpid{};
	uint64_t folder_id = 0, message_id = 0;
};

struct exreq_rule_new_message final : public exreq {
	using view_t = exreq_rule_new_message;
	char *username = nullptr;
	cpid_t cpid{};
	uint64_t folder_id = 0, message_id = 0;
};

struct exreq_set_message_timer final : public exreq {
	using view_t = exreq_set_message_timer;
	uint64_t message_id = 0;
	uint32_t timer_id = 0;
};

struct exreq_get_message_timer final : public exreq {
	using view_t = exreq_get_message_timer;
	uint64_t message_id = 0;
};

struct exreq_empty_folder_permission final : public exreq {
	using view_t = exreq_empty_folder_permission;
	uint64_t folder_id = 0;
};

struct exreq_update_folder_permission final : public exreq {
	using view_t = exreq_update_folder_permission;
	uint64_t folder_id = 0;
	BOOL b_freebusy = false;
	uint16_t count = 0;
	PERMISSION_DATA *prow = nullptr;
};

struct exreq_empty_folder_rule final : public exreq {
	using view_t = exreq_empty_folder_rule;
	uint64_t folder_id = 0;
};

struct exreq_update_folder_rule final : public exreq {
	using view_t = exreq_update_folder_rule;
	uint64_t folder_id = 0;
	uint16_t count = 0;
	RULE_DATA *prow = nullptr;
};

enum delivery_flags {
	DELIVERY_DO_RULES_SV = 0x1U,
	DELIVERY_DO_NOTIF_SV = 0x2U,
	DELIVERY_DO_MRAUTOPROC = 0x4U,
	DELIVERY_FORCE_JUNK = 0x8U,
	DELIVERY_DO_RULES_CL = 0x10U,
	DELIVERY_DO_NOTIF_CL = 0x20U,
};

struct exreq_deliver_message final : public exreq {
	using view_t = exreq_deliver_message;
	char *from_address = nullptr, *account = nullptr;
	MESSAGE_CONTENT *pmsg = nullptr;
	char *pdigest = nullptr;
	cpid_t cpid{};
	uint32_t dlflags = 0;
};

struct exreq_write_message final : public exreq {
	using view_t = exreq_write_message;
	cpid_t cpid{};
	uint64_t folder_id = 0;
	MESSAGE_CONTENT *pmsgctnt = nullptr;
	std::string digest;
};

struct exreq_read_message final : public exreq {
	using view_t = exreq_read_message;
	char *username = nullptr;
	cpid_t cpid{};
	uint64_t message_id = 0;
};

struct exreq_get_content_sync final : public exreq {
	using view_t = exreq_get_content_sync;
	uint64_t folder_id = 0;
	char *username = nullptr;
	gromox::idset *pgiven = nullptr, *pseen = nullptr, *pseen_fai = nullptr, *pread = nullptr;
	RESTRICTION *prestriction = nullptr;
	cpid_t cpid{};
	BOOL b_ordered = false;
};

struct exreq_get_hierarchy_sync final : public exreq {
	using view_t = exreq_get_hierarchy_sync;
	uint64_t folder_id = 0;
	char *username = nullptr;
	gromox::idset *pgiven = nullptr, *pseen = nullptr;
};

struct exreq_allocate_ids final : public exreq {
	using view_t = exreq_allocate_ids;
	uint32_t count = 0;
};

struct exreq_subscribe_notification final : public exreq {
	using view_t = exreq_subscribe_notification;
	uint16_t notification_type = 0;
	BOOL b_whole = false;
	uint64_t folder_id = 0, message_id = 0;
};

struct exreq_unsubscribe_notification final : public exreq {
	using view_t = exreq_unsubscribe_notification;
	uint32_t sub_id = 0;
};

struct exreq_transport_new_mail final : public exreq {
	using view_t = exreq_transport_new_mail;
	uint64_t folder_id = 0, message_id = 0;
	uint32_t message_flags = 0;
	char *pstr_class = nullptr;
};

struct exreq_check_contact_address final : public exreq {
	using view_t = exreq_check_contact_address;
	char *paddress = nullptr;
};

struct exreq_get_public_folder_unread_count final : public exreq {
	using view_t = exreq_get_public_folder_unread_count;
	char *username = nullptr;
	uint64_t folder_id = 0;
};

struct exreq_store_eid_to_user final : public exreq {
	using view_t = exreq_store_eid_to_user;
	STORE_ENTRYID *store_eid = nullptr;
};

struct exreq_purge_softdelete final : public exreq {
	using view_t = exreq_purge_softdelete;
	char *username = nullptr;
	uint64_t folder_id = 0;
	uint32_t del_flags = 0;
	gromox::mapitime_t cutoff = 0;
};

struct exreq_autoreply_tsquery final : public exreq {
	using view_t = exreq_autoreply_tsquery;
	char *peer = nullptr;
	uint64_t window = 0;
};

struct exreq_autoreply_tsupdate final : public exreq {
	using view_t = exreq_autoreply_tsupdate;
	char *peer = nullptr;
};

struct exreq_recalc_store_size final : public exreq {
	using view_t = exreq_recalc_store_size;
	uint32_t flags = 0;
};

struct exreq_imapfile_read final : public exreq {
	using view_t = exreq_imapfile_read;
	std::string type, mid;
};

struct exreq_imapfile_write final : public exreq {
	using view_t = exreq_imapfile_write;
	std::string type, mid, data;
};

enum class db_maint_mode {
	usable, hold, reject, hold_waitforexcl, reject_waitforexcl,
};

struct exreq_set_maintenance final : public exreq {
	using view_t = exreq_set_maintenance;
	uint32_t mode = 0;
};

struct exreq_write_delegates final : public exreq {
	using view_t = exreq_write_delegates;
	uint32_t mode = 0;
	std::vector<std::string> userlist;
};

/**
 * FOLDERS:     process folders
 * MESSAGES:    process messages
 * ZERO_LASTCN: reset all CNs, start from 0 (implies FOLDERS|MESSAGES)
 */
enum cgkreset_flags {
	CGKRESET_FOLDERS     = 0x1U,
	CGKRESET_MESSAGES    = 0x2U,
	CGKRESET_ZERO_LASTCN = 0x4U,
};

using exreq_imapfile_delete = exreq_imapfile_read;
using exreq_cgkreset = exreq_recalc_store_size;

struct exresp {
	using view_t = exresp;
	exresp() = default; /* Prevent use of direct-init-list */
	virtual ~exresp() = default;
	exmdb_callid call_id{};
};

struct exresp_error final : public exresp {
	using view_t = exresp_error;
	ec_error_t e_result{};
};

struct exresp_get_all_named_propids final : public exresp {
	using view_t = exresp_get_all_named_propids;
	PROPID_ARRAY propids{};
};

struct exresp_get_named_propids final : public exresp {
	using view_t = exresp_get_named_propids;
	PROPID_ARRAY propids{};
};

struct exresp_get_named_propnames final : public exresp {
	using view_t = exresp_get_named_propnames;
	PROPNAME_ARRAY propnames{};
};

struct exresp_get_mapping_guid final : public exresp {
	using view_t = exresp_get_mapping_guid;
	BOOL b_found = false;
	GUID guid{};
};

struct exresp_get_mapping_replid final : public exresp {
	using view_t = exresp_get_mapping_replid;
	uint16_t replid = 0;
	ec_error_t e_result = ecSuccess;
};

struct exresp_get_store_all_proptags final : public exresp {
	using view_t = exresp_get_store_all_proptags;
	PROPTAG_ARRAY proptags{};
};

struct exresp_get_store_properties final : public exresp {
	using view_t = exresp_get_store_properties;
	TPROPVAL_ARRAY propvals{};
};

struct exresp_set_store_properties final : public exresp {
	using view_t = exresp_set_store_properties;
	PROBLEM_ARRAY problems{};
};

using exresp_autoreply_getprop = exresp_get_store_properties;
using exresp_autoreply_setprop = exresp_set_store_properties;

struct exresp_get_mbox_perm final : public exresp {
	using view_t = exresp_get_mbox_perm;
	uint32_t permission = 0;
};

struct exresp_get_folder_by_class final : public exresp {
	using view_t = exresp_get_folder_by_class;
	uint64_t id = 0;
	std::string str_explicit;
};

struct exresp_set_folder_by_class final : public exresp {
	using view_t = exresp_set_folder_by_class;
	BOOL b_result = false;
};

struct exresp_get_folder_class_table final : public exresp {
	using view_t = exresp_get_folder_class_table;
	TARRAY_SET table{};
};

struct exresp_is_folder_present final : public exresp {
	using view_t = exresp_is_folder_present;
	BOOL b_exist = false;
};

struct exresp_is_folder_deleted final : public exresp {
	using view_t = exresp_is_folder_deleted;
	BOOL b_del = false;
};

struct exresp_get_folder_by_name final : public exresp {
	using view_t = exresp_get_folder_by_name;
	uint64_t folder_id = 0;
};

struct exresp_get_folder_perm final : public exresp {
	using view_t = exresp_get_folder_perm;
	uint32_t permission = 0;
};

struct exresp_create_folder_v1 final : public exresp {
	using view_t = exresp_create_folder_v1;
	uint64_t folder_id = 0;
};

struct exresp_create_folder final : public exresp {
	using view_t = exresp_create_folder;
	uint64_t folder_id = 0;
	ec_error_t e_result{};
};

struct exresp_get_folder_all_proptags final : public exresp {
	using view_t = exresp_get_folder_all_proptags;
	PROPTAG_ARRAY proptags{};
};

struct exresp_get_folder_properties final : public exresp {
	using view_t = exresp_get_folder_properties;
	TPROPVAL_ARRAY propvals{};
};

struct exresp_set_folder_properties final : public exresp {
	using view_t = exresp_set_folder_properties;
	PROBLEM_ARRAY problems{};
};

struct exresp_delete_folder final : public exresp {
	using view_t = exresp_delete_folder;
	BOOL b_result = false;
};

struct exresp_empty_folder final : public exresp {
	using view_t = exresp_empty_folder;
	BOOL b_partial = false;
};

struct exresp_is_descendant_folder final : public exresp {
	using view_t = exresp_is_descendant_folder;
	BOOL b_included = false;
};

struct exresp_copy_folder_internal final : public exresp {
	using view_t = exresp_copy_folder_internal;
	BOOL b_collid = false, b_partial = false;
};

struct exresp_get_search_criteria final : public exresp {
	using view_t = exresp_get_search_criteria;
	uint32_t search_status = 0;
	RESTRICTION *prestriction = nullptr;
	EID_ARRAY folder_ids{};
};

struct exresp_set_search_criteria final : public exresp {
	using view_t = exresp_set_search_criteria;
	BOOL b_result = false;
};

struct exresp_movecopy_message final : public exresp {
	using view_t = exresp_movecopy_message;
	BOOL b_result = false;
};

struct exresp_movecopy_messages final : public exresp {
	using view_t = exresp_movecopy_messages;
	BOOL b_partial = false;
};

struct exresp_delete_messages final : public exresp {
	using view_t = exresp_delete_messages;
	BOOL b_partial = false;
};

struct exresp_get_message_brief final : public exresp {
	using view_t = exresp_get_message_brief;
	MESSAGE_CONTENT *pbrief = nullptr;
};

struct exresp_sum_hierarchy final : public exresp {
	using view_t = exresp_sum_hierarchy;
	uint32_t count = 0;
};

struct exresp_load_hierarchy_table final : public exresp {
	using view_t = exresp_load_hierarchy_table;
	uint32_t table_id = 0, row_count = 0;
};

struct exresp_sum_content final : public exresp {
	using view_t = exresp_sum_content;
	uint32_t count = 0;
};

struct exresp_load_content_table final : public exresp {
	using view_t = exresp_load_content_table;
	uint32_t table_id = 0, row_count = 0;
};

struct exresp_load_permission_table final : public exresp {
	using view_t = exresp_load_permission_table;
	uint32_t table_id = 0, row_count = 0;
};

struct exresp_load_rule_table final : public exresp {
	using view_t = exresp_load_rule_table;
	uint32_t table_id = 0, row_count = 0;
};

struct exresp_sum_table final : public exresp {
	using view_t = exresp_sum_table;
	uint32_t rows = 0;
};

struct exresp_query_table final : public exresp {
	using view_t = exresp_query_table;
	TARRAY_SET set{};
};

struct exresp_match_table final : public exresp {
	using view_t = exresp_match_table;
	int32_t position = 0;
	TPROPVAL_ARRAY propvals{};
};

struct exresp_locate_table final : public exresp {
	using view_t = exresp_locate_table;
	int32_t position = 0;
	uint32_t row_type = 0;
};

struct exresp_read_table_row final : public exresp {
	using view_t = exresp_read_table_row;
	TPROPVAL_ARRAY propvals{};
};

struct exresp_mark_table final : public exresp {
	using view_t = exresp_mark_table;
	uint64_t inst_id = 0;
	uint32_t inst_num = 0, row_type = 0;
};

struct exresp_get_table_all_proptags final : public exresp {
	using view_t = exresp_get_table_all_proptags;
	PROPTAG_ARRAY proptags{};
};

struct exresp_expand_table final : public exresp {
	using view_t = exresp_expand_table;
	BOOL b_found = false;
	int32_t position = 0;
	uint32_t row_count = 0;
};

struct exresp_collapse_table final : public exresp {
	using view_t = exresp_collapse_table;
	BOOL b_found = false;
	int32_t position = 0;
	uint32_t row_count = 0;
};

struct exresp_store_table_state final : public exresp {
	using view_t = exresp_store_table_state;
	uint32_t state_id = 0;
};

struct exresp_restore_table_state final : public exresp {
	using view_t = exresp_restore_table_state;
	int32_t position = 0;
};

struct exresp_is_msg_present final : public exresp {
	using view_t = exresp_is_msg_present;
	BOOL b_exist = false;
};

struct exresp_is_msg_deleted final : public exresp {
	using view_t = exresp_is_msg_deleted;
	BOOL b_del = false;
};

struct exresp_load_message_instance final : public exresp {
	using view_t = exresp_load_message_instance;
	uint32_t instance_id = 0;
};

struct exresp_load_embedded_instance final : public exresp {
	using view_t = exresp_load_embedded_instance;
	uint32_t instance_id = 0;
};

struct exresp_get_embedded_cn final : public exresp {
	using view_t = exresp_get_embedded_cn;
	uint64_t *pcn = nullptr;
};

struct exresp_reload_message_instance final : public exresp {
	using view_t = exresp_reload_message_instance;
	BOOL b_result = false;
};

struct exresp_read_message_instance final : public exresp {
	using view_t = exresp_read_message_instance;
	MESSAGE_CONTENT msgctnt{};
};

struct exresp_write_message_instance final : public exresp {
	using view_t = exresp_write_message_instance;
	PROPTAG_ARRAY proptags{};
	PROBLEM_ARRAY problems{};
};

struct exresp_load_attachment_instance final : public exresp {
	using view_t = exresp_load_attachment_instance;
	uint32_t instance_id = 0;
};

struct exresp_create_attachment_instance final : public exresp {
	using view_t = exresp_create_attachment_instance;
	uint32_t instance_id = 0, attachment_num = 0;
};

struct exresp_read_attachment_instance final : public exresp {
	using view_t = exresp_read_attachment_instance;
	ATTACHMENT_CONTENT attctnt{};
};

struct exresp_write_attachment_instance final : public exresp {
	using view_t = exresp_write_attachment_instance;
	PROBLEM_ARRAY problems{};
};

struct exresp_get_instance_all_proptags final : public exresp {
	using view_t = exresp_get_instance_all_proptags;
	PROPTAG_ARRAY proptags{};
};

struct exresp_get_instance_properties final : public exresp {
	using view_t = exresp_get_instance_properties;
	TPROPVAL_ARRAY propvals{};
};

struct exresp_set_instance_properties final : public exresp {
	using view_t = exresp_set_instance_properties;
	PROBLEM_ARRAY problems{};
};

struct exresp_remove_instance_properties final : public exresp {
	using view_t = exresp_remove_instance_properties;
	PROBLEM_ARRAY problems{};
};

struct exresp_is_descendant_instance final : public exresp {
	using view_t = exresp_is_descendant_instance;
	BOOL b_included = false;
};

struct exresp_get_message_instance_rcpts_num final : public exresp {
	using view_t = exresp_get_message_instance_rcpts_num;
	uint16_t num = 0;
};

struct exresp_get_message_instance_rcpts_all_proptags final : public exresp {
	using view_t = exresp_get_message_instance_rcpts_all_proptags;
	PROPTAG_ARRAY proptags{};
};

struct exresp_get_message_instance_rcpts final : public exresp {
	using view_t = exresp_get_message_instance_rcpts;
	TARRAY_SET set{};
};

struct exresp_copy_instance_rcpts final : public exresp {
	using view_t = exresp_copy_instance_rcpts;
	BOOL b_result = false;
};

struct exresp_get_message_instance_attachments_num final : public exresp {
	using view_t = exresp_get_message_instance_attachments_num;
	uint16_t num = 0;
};

struct exresp_get_message_instance_attachment_table_all_proptags final : public exresp {
	using view_t = exresp_get_message_instance_attachment_table_all_proptags;
	PROPTAG_ARRAY proptags{};
};

struct exresp_query_message_instance_attachment_table final : public exresp {
	using view_t = exresp_query_message_instance_attachment_table;
	TARRAY_SET set{};
};

struct exresp_copy_instance_attachments final : public exresp {
	using view_t = exresp_copy_instance_attachments;
	BOOL b_result = false;
};

struct exresp_get_message_rcpts final : public exresp {
	using view_t = exresp_get_message_rcpts;
	TARRAY_SET set{};
};

struct exresp_get_message_properties final : public exresp {
	using view_t = exresp_get_message_properties;
	TPROPVAL_ARRAY propvals{};
};

struct exresp_set_message_properties final : public exresp {
	using view_t = exresp_set_message_properties;
	PROBLEM_ARRAY problems{};
};

struct exresp_set_message_read_state final : public exresp {
	using view_t = exresp_set_message_read_state;
	uint64_t read_cn = 0;
};

struct exresp_allocate_message_id final : public exresp {
	using view_t = exresp_allocate_message_id;
	uint64_t message_id = 0;
};

struct exresp_allocate_cn final : public exresp {
	using view_t = exresp_allocate_cn;
	uint64_t cn = 0;
};

struct exresp_try_mark_submit final : public exresp {
	using view_t = exresp_try_mark_submit;
	BOOL b_marked = false;
};

struct exresp_link_message final : public exresp {
	using view_t = exresp_link_message;
	BOOL b_result = false;
};

struct exresp_get_message_timer final : public exresp {
	using view_t = exresp_get_message_timer;
	uint32_t *ptimer_id = nullptr;
};

struct exresp_update_folder_rule final : public exresp {
	using view_t = exresp_update_folder_rule;
	BOOL b_exceed = false;
};

enum deliver_message_result {
	result_ok = 0,
	/* mailbox_full (unused) = 1, */
	result_error = 2,
	mailbox_full_bysize = 3,
	mailbox_full_bymsg = 4,
	partial_completion = 5,
};

struct exresp_deliver_message final : public exresp {
	using view_t = exresp_deliver_message;
	uint64_t folder_id = 0, message_id = 0;
	uint32_t result = 0;
};

struct exresp_read_message final : public exresp {
	using view_t = exresp_read_message;
	MESSAGE_CONTENT *pmsgctnt = nullptr;
};

struct exresp_get_content_sync final : public exresp {
	using view_t = exresp_get_content_sync;
	uint32_t fai_count = 0, normal_count = 0;
	uint64_t fai_total = 0, normal_total = 0;
	uint64_t last_cn = 0, last_readcn = 0;
	EID_ARRAY updated_mids{}, chg_mids{};
	EID_ARRAY given_mids{}, deleted_mids{}, nolonger_mids{}, read_mids{}, unread_mids{};
};

struct exresp_get_hierarchy_sync final : public exresp {
	using view_t = exresp_get_hierarchy_sync;
	FOLDER_CHANGES fldchgs{};
	uint64_t last_cn = 0;
	EID_ARRAY given_fids{}, deleted_fids{};
};

struct exresp_allocate_ids final : public exresp {
	using view_t = exresp_allocate_ids;
	uint64_t begin_eid = 0;
};

struct exresp_subscribe_notification final : public exresp {
	using view_t = exresp_subscribe_notification;
	uint32_t sub_id = 0;
};

struct exresp_check_contact_address final : public exresp {
	using view_t = exresp_check_contact_address;
	BOOL b_found = false;
};

struct exresp_get_public_folder_unread_count final : public exresp {
	using view_t = exresp_get_public_folder_unread_count;
	uint32_t count = 0;
};

struct exresp_store_eid_to_user final : public exresp {
	using view_t = exresp_store_eid_to_user;
	char *maildir = nullptr;
	unsigned int user_id = 0, domain_id = 0;
};

struct exresp_autoreply_tsquery final : public exresp {
	using view_t = exresp_autoreply_tsquery;
	uint64_t tdiff = 0;
};

struct exresp_write_message final : public exresp {
	using view_t = exresp_write_message;
	uint64_t outmid = 0, outcn = 0;
	ec_error_t e_result{};
};

struct exresp_imapfile_read final : public exresp {
	using view_t = exresp_imapfile_read;
	std::string data;
};

struct exresp_purge_softdelete final : public exresp {
	using view_t = exresp_purge_softdelete;
	uint32_t cnt_folders = 0, cnt_messages = 0;
	uint64_t sz_normal = 0, sz_fai = 0;
};

struct exresp_read_delegates final : public exresp {
	using view_t = exresp_read_delegates;
	std::vector<std::string> userlist;
};

using exreq_ping_store = exreq;
using exreq_get_all_named_propids = exreq;
using exreq_get_store_all_proptags = exreq;
using exreq_get_folder_class_table = exreq;
using exreq_allocate_cn = exreq;
using exreq_vacuum = exreq;
using exreq_unload_store = exreq;
using exreq_purge_datafiles = exreq;
using exreq_create_folder_v1 = exreq_create_folder;
using exreq_read_delegates = exreq_set_maintenance;
using exresp_connect = exresp;
using exresp_listen_notification = exresp;
using exresp_remove_folder_properties = exresp;
using exresp_reload_content_table = exresp;
using exresp_unload_table = exresp;
using exresp_clear_message_instance = exresp;
using exresp_delete_message_instance_attachment = exresp;
using exresp_unload_instance = exresp;
using exresp_empty_message_instance_rcpts = exresp;
using exresp_update_message_instance_rcpts = exresp;
using exresp_empty_message_instance_attachments = exresp;
using exresp_set_message_instance_conflict = exresp;
using exresp_remove_message_properties = exresp;
using exresp_remove_store_properties = exresp;
using exresp_mark_modified = exresp;
using exresp_clear_submit = exresp;
using exresp_unlink_message = exresp;
using exresp_rule_new_message = exresp;
using exresp_set_message_timer = exresp;
using exresp_empty_folder_permission = exresp;
using exresp_update_folder_permission = exresp;
using exresp_empty_folder_rule = exresp;
using exresp_unsubscribe_notification = exresp;
using exresp_transport_new_mail = exresp;
using exresp_vacuum = exresp;
using exresp_unload_store = exresp;
using exresp_ping_store = exresp;

using exresp_purge_datafiles = exresp;
using exresp_autoreply_tsupdate = exresp;
using exresp_recalc_store_size = exresp;
using exresp_flush_instance = exresp_error;
using exresp_movecopy_folder = exresp_error;
using exresp_imapfile_write = exresp;
using exresp_imapfile_delete = exresp;
using exresp_cgkreset = exresp;
using exresp_set_maintenance = exresp;
using exresp_write_delegates = exresp;

struct DB_NOTIFY_DATAGRAM {
	char *dir = nullptr;
	BOOL b_table = false;
	std::vector<uint32_t> id_array;
	DB_NOTIFY db_notify{};
};

extern GX_EXPORT pack_result exmdb_ext_pull_request(std::string_view, std::unique_ptr<exreq> &alloc_by_callee);
extern GX_EXPORT pack_result exmdb_ext_push_request(const exreq *, BINARY *);
extern GX_EXPORT pack_result exmdb_ext_pull_response(std::string_view, exresp *partial_fill_by_caller);
extern GX_EXPORT pack_result exmdb_ext_push_response(const exresp *presponse, BINARY *);
extern GX_EXPORT pack_result exmdb_ext_pull_db_notify(std::string_view, DB_NOTIFY_DATAGRAM *);
extern GX_EXPORT pack_result exmdb_ext_push_db_notify(const DB_NOTIFY_DATAGRAM *, BINARY *);
extern GX_EXPORT const char *exmdb_rpc_strerror(exmdb_response);
extern GX_EXPORT bool exmdb_client_read_socket(int, std::string &, long timeout = -1);
extern GX_EXPORT BOOL exmdb_client_write_socket(int, std::string_view, long timeout = -1);

extern GX_EXPORT void *(*exmdb_rpc_alloc)(size_t);
extern GX_EXPORT void (*exmdb_rpc_free)(void *);
