#ifndef SIEVE_COMPARATORS_H
#define SIEVE_COMPARATORS_H

#include "sieve-common.h"
#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-objects.h"
#include "sieve-code.h"

/*
 * Core comparators
 */

enum sieve_comparator_code {
	SIEVE_COMPARATOR_I_OCTET,
	SIEVE_COMPARATOR_I_ASCII_CASEMAP,
	SIEVE_COMPARATOR_I_UNICODE_CASEMAP,
	SIEVE_COMPARATOR_CUSTOM
};

extern const struct sieve_comparator_def i_octet_comparator;
extern const struct sieve_comparator_def i_ascii_casemap_comparator;
extern const struct sieve_comparator_def i_unicode_casemap_comparator;

/*
 * Comparator flags
 */

enum sieve_comparator_flags {
	SIEVE_COMPARATOR_FLAG_ORDERING = (1 << 0),
	SIEVE_COMPARATOR_FLAG_EQUALITY = (1 << 1),
	SIEVE_COMPARATOR_FLAG_PREFIX_MATCH = (1 << 2),
	SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH = (1 << 3),
};

/*
 * Comparator definition
 */

struct sieve_comparator_def {
	struct sieve_object_def obj_def;

	unsigned int flags;

	/* Equality and ordering */

	int (*compare)(const struct sieve_comparator *cmp,
		       const char *val1, size_t val1_size,
		       const char *val2, size_t val2_size);

	/* Prefix and substring match */

	bool (*char_match)(const struct sieve_comparator *cmp,
			   const char **val, const char *val_end,
			   const char **key, const char *key_end);
	bool (*char_skip)(const struct sieve_comparator *cmp,
			  const char **val, const char *val_end);
};

/*
 * Comparator instance
 */

struct sieve_comparator {
	struct sieve_object object;

	const struct sieve_comparator_def *def;
};

#define SIEVE_COMPARATOR_DEFAULT(definition) \
	{ SIEVE_OBJECT_DEFAULT(definition), &(definition) }

#define sieve_comparator_name(cmp) \
	((cmp)->object.def->identifier)
#define sieve_comparator_is(cmp, definition) \
	((cmp)->def == &(definition))

static inline const struct sieve_comparator *
sieve_comparator_copy(pool_t pool, const struct sieve_comparator *cmp_orig)
{
	struct sieve_comparator *cmp = p_new(pool, struct sieve_comparator, 1);

	*cmp = *cmp_orig;
	return cmp;
}

/*
 * Comparator tagged argument
 */

extern const struct sieve_argument_def comparator_tag;

static inline bool
sieve_argument_is_comparator(struct sieve_ast_argument *arg)
{
	return (arg->argument != NULL &&
		(arg->argument->def == &comparator_tag));
}

void sieve_comparators_link_tag(struct sieve_validator *validator,
				struct sieve_command_registration *cmd_reg,
				int id_code);
bool sieve_comparator_tag_is(struct sieve_ast_argument *tag,
			     const struct sieve_comparator_def *cmp);
const struct sieve_comparator *
sieve_comparator_tag_get(struct sieve_ast_argument *tag);

void sieve_comparator_register(struct sieve_validator *validator,
			       const struct sieve_extension *ext,
			       const struct sieve_comparator_def *cmp);

/*
 * Comparator operand
 */

#define SIEVE_EXT_DEFINE_COMPARATOR(OP) SIEVE_EXT_DEFINE_OBJECT(OP)
#define SIEVE_EXT_DEFINE_COMPARATORS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS)

extern const struct sieve_operand_class sieve_comparator_operand_class;
extern const struct sieve_operand_def comparator_operand;

static inline void
sieve_opr_comparator_emit(struct sieve_binary_block *sblock,
			  const struct sieve_comparator *cmp)
{
	sieve_opr_object_emit(sblock, cmp->object.ext, cmp->object.def);
}
static inline bool
sieve_opr_comparator_dump(const struct sieve_dumptime_env *denv,
			  sieve_size_t *address)
{
	return sieve_opr_object_dump(denv, &sieve_comparator_operand_class,
				     address, NULL);
}

static inline int
sieve_opr_comparator_read(const struct sieve_runtime_env *renv,
			  sieve_size_t *address, struct sieve_comparator *cmp)
{
	if (!sieve_opr_object_read(renv, &sieve_comparator_operand_class,
				   address, &cmp->object))
		return SIEVE_EXEC_BIN_CORRUPT;

	cmp->def = (const struct sieve_comparator_def *)cmp->object.def;
	return SIEVE_EXEC_OK;
}

/*
 * Trivial/Common comparator method implementations
 */

bool sieve_comparator_octet_skip(const struct sieve_comparator *cmp ATTR_UNUSED,
				 const char **val, const char *val_end);

#endif
