#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_CUSTOM
};

extern const struct sieve_comparator_def i_octet_comparator;
extern const struct sieve_comparator_def i_ascii_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
