#ifndef SIEVE_ADDRESS_PARTS_H
#define SIEVE_ADDRESS_PARTS_H

#include "message-address.h"

#include "sieve-common.h"
#include "sieve-match.h"
#include "sieve-extensions.h"
#include "sieve-objects.h"

/*
 * Address part definition
 */

struct sieve_address_part_def {
	struct sieve_object_def obj_def;

	const char *
	(*extract_from)(const struct sieve_address_part *addrp,
			const struct smtp_address *address);
};

/*
 * Address part instance
 */

struct sieve_address_part {
	struct sieve_object object;

	const struct sieve_address_part_def *def;
};

#define SIEVE_ADDRESS_PART_DEFAULT(definition) \
	{ SIEVE_OBJECT_DEFAULT(definition), &(definition) };

#define sieve_address_part_name(addrp) \
	( (addrp)->object.def->identifier )
#define sieve_address_part_is(addrp, definition) \
	( (addrp)->def == &(definition) )

/*
 * Core address parts
 */

enum sieve_address_part_code {
	SIEVE_ADDRESS_PART_ALL,
	SIEVE_ADDRESS_PART_LOCAL,
	SIEVE_ADDRESS_PART_DOMAIN,
	SIEVE_ADDRESS_PART_CUSTOM
};

extern const struct sieve_address_part_def all_address_part;
extern const struct sieve_address_part_def local_address_part;
extern const struct sieve_address_part_def domain_address_part;

/*
 * Address part tagged argument
 */

extern const struct sieve_argument_def address_part_tag;

void sieve_address_parts_link_tags(struct sieve_validator *valdtr,
				   struct sieve_command_registration *cmd_reg,
				   int id_code);

/*
 * Address part registry
 */

void sieve_address_part_register(struct sieve_validator *valdtr,
				 const struct sieve_extension *ext,
				 const struct sieve_address_part_def *addrp);

/*
 * Address part operand
 */

extern const struct sieve_operand_def address_part_operand;
extern const struct sieve_operand_class sieve_address_part_operand_class;

#define SIEVE_EXT_DEFINE_ADDRESS_PART(OP) SIEVE_EXT_DEFINE_OBJECT(OP)
#define SIEVE_EXT_DEFINE_ADDRESS_PARTS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS)

static inline void
sieve_opr_address_part_emit(struct sieve_binary_block *sblock,
			    const struct sieve_address_part *addrp)
{
	sieve_opr_object_emit(sblock, addrp->object.ext, addrp->object.def);
}

static inline bool
sieve_opr_address_part_dump(const struct sieve_dumptime_env *denv,
			    sieve_size_t *address)
{
	return sieve_opr_object_dump(denv, &sieve_address_part_operand_class,
				     address, NULL);
}

static inline int
sieve_opr_address_part_read(const struct sieve_runtime_env *renv,
			    sieve_size_t *address,
			    struct sieve_address_part *addrp)
{
	if (!sieve_opr_object_read(renv, &sieve_address_part_operand_class,
				   address, &addrp->object))
		return SIEVE_EXEC_BIN_CORRUPT;

	addrp->def = (const struct sieve_address_part_def *)addrp->object.def;
	return SIEVE_EXEC_OK;
}

/*
 * Address-part string list
 */

struct sieve_stringlist *
sieve_address_part_stringlist_create(const struct sieve_runtime_env *renv,
				     const struct sieve_address_part *addrp,
				     struct sieve_address_list *addresses);

/*
 * Match utility
 */

enum sieve_addrmatch_opt_operand {
	SIEVE_AM_OPT_ADDRESS_PART = SIEVE_MATCH_OPT_LAST,
	SIEVE_AM_OPT_LAST
};

int sieve_addrmatch_opr_optional_dump(const struct sieve_dumptime_env *denv,
				      sieve_size_t *address,
				      signed int *opt_code);

int sieve_addrmatch_opr_optional_read(const struct sieve_runtime_env *renv,
				      sieve_size_t *address,
				      signed int *opt_code, int *exec_status,
				      struct sieve_address_part *addrp,
				      struct sieve_match_type *mtch,
				      struct sieve_comparator *cmp);

#endif
