#ifndef SIEVE_VALIDATOR_H
#define SIEVE_VALIDATOR_H

#include "lib.h"

#include "sieve-common.h"

/*
 * Types
 */

enum sieve_argument_type {
	SAT_NUMBER,
	SAT_CONST_STRING,
	SAT_VAR_STRING,
	SAT_STRING_LIST,

	SAT_COUNT
};

struct sieve_command_registration;

/*
 * Validator
 */

struct sieve_validator;

struct sieve_validator *
sieve_validator_create(struct sieve_ast *ast,
		       struct sieve_error_handler *ehandler,
		       enum sieve_compile_flags flags);
void sieve_validator_free(struct sieve_validator **valdtr);
pool_t sieve_validator_pool(struct sieve_validator *valdtr);

bool sieve_validator_run(struct sieve_validator *valdtr);

/*
 * Accessors
 */

struct sieve_error_handler *
sieve_validator_error_handler(struct sieve_validator *valdtr);
struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr);
struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr);
const char *sieve_validator_script_cause(struct sieve_validator *valdtr);
struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr);
enum sieve_compile_flags
sieve_validator_compile_flags(struct sieve_validator *valdtr);
bool sieve_validator_failed(struct sieve_validator *valdtr) ATTR_PURE;

/*
 * Command/Test registry
 */

void sieve_validator_register_command(struct sieve_validator *valdtr,
				      const struct sieve_extension *ext,
				      const struct sieve_command_def *command);

/*
 * Per-command tagged argument registry
 */

void sieve_validator_register_tag(struct sieve_validator *valdtr,
				  struct sieve_command_registration *cmd_reg,
				  const struct sieve_extension *ext,
				  const struct sieve_argument_def *tag_def,
				  int id_code);
void sieve_validator_register_external_tag(
	struct sieve_validator *valdtr, const char *command,
	const struct sieve_extension *ext,
	const struct sieve_argument_def *tag_def, int id_code);
void sieve_validator_register_persistent_tag(
	struct sieve_validator *valdtr, const char *command,
	const struct sieve_extension *ext,
	const struct sieve_argument_def *tag_def);

/*
 * Overriding the default literal arguments
 */

void sieve_validator_argument_override(
	struct sieve_validator *valdtr, enum sieve_argument_type type,
	const struct sieve_extension *ext,
	const struct sieve_argument_def *arg_def);
bool sieve_validator_argument_activate_super(
	struct sieve_validator *valdtr, struct sieve_command *cmd,
	struct sieve_ast_argument *arg, bool constant);

/*
 * Argument validation API
 */

bool sieve_validate_positional_argument(struct sieve_validator *valdtr,
					struct sieve_command *cmd,
					struct sieve_ast_argument *arg,
					const char *arg_name,
					unsigned int arg_pos,
					enum sieve_ast_argument_type req_type);
bool sieve_validator_argument_activate(struct sieve_validator *valdtr,
				       struct sieve_command *cmd,
				       struct sieve_ast_argument *arg,
				       bool constant);

bool sieve_validate_tag_parameter(struct sieve_validator *valdtr,
				  struct sieve_command *cmd,
				  struct sieve_ast_argument *tag,
				  struct sieve_ast_argument *param,
				  const char *arg_name, unsigned int arg_pos,
				  enum sieve_ast_argument_type req_type,
				  bool constant);

/*
 * Extension support
 */

struct sieve_validator_extension {
	const struct sieve_extension_def *ext;

	bool (*check_conflict)(const struct sieve_extension *ext,
			       struct sieve_validator *valdtr, void *context,
			       struct sieve_ast_argument *require_arg,
			       const struct sieve_extension *ext_other,
			       bool required);
	bool (*validate)(const struct sieve_extension *ext,
			 struct sieve_validator *valdtr, void *context,
			 struct sieve_ast_argument *require_arg, bool required);

	void (*free)(const struct sieve_extension *ext,
		     struct sieve_validator *valdtr, void *context);
};

bool sieve_validator_extension_load(struct sieve_validator *valdtr,
				    struct sieve_command *cmd,
				    struct sieve_ast_argument *ext_arg,
				    const struct sieve_extension *ext,
				    bool required) ATTR_NULL(2, 3);
const struct sieve_extension *
sieve_validator_extension_load_by_name(struct sieve_validator *valdtr,
				       struct sieve_command *cmd,
				       struct sieve_ast_argument *ext_arg,
				       const char *ext_name);
const struct sieve_extension *
sieve_validator_extension_load_implicit(struct sieve_validator *valdtr,
					const char *ext_name);

void sieve_validator_extension_register(
	struct sieve_validator *valdtr, const struct sieve_extension *ext,
	const struct sieve_validator_extension *valext, void *context);
bool sieve_validator_extension_loaded(struct sieve_validator *valdtr,
				      const struct sieve_extension *ext);

void sieve_validator_extension_set_context(struct sieve_validator *valdtr,
					   const struct sieve_extension *ext,
					   void *context);
void *sieve_validator_extension_get_context(struct sieve_validator *valdtr,
					    const struct sieve_extension *ext);

/*
 * Validator object registry
 */

struct sieve_validator_object_registry;

struct sieve_validator_object_registry *
sieve_validator_object_registry_get(struct sieve_validator *valdtr,
				    const struct sieve_extension *ext);
void sieve_validator_object_registry_add(
	struct sieve_validator_object_registry *regs,
	const struct sieve_extension *ext,
	const struct sieve_object_def *obj_def);
bool sieve_validator_object_registry_find(
	struct sieve_validator_object_registry *regs, const char *identifier,
	struct sieve_object *obj);
struct sieve_validator_object_registry *
sieve_validator_object_registry_create(struct sieve_validator *valdtr);
struct sieve_validator_object_registry *
sieve_validator_object_registry_init(struct sieve_validator *valdtr,
				     const struct sieve_extension *ext);

/*
 * Error handling
 */

void sieve_validator_error(struct sieve_validator *valdtr,
			   const char *csrc_filename, unsigned int csrc_linenum,
			   unsigned int source_line, const char *fmt, ...)
			   ATTR_FORMAT(5, 6);
#define sieve_validator_error(valdtr, ...) \
	sieve_validator_error(valdtr, __FILE__, __LINE__, __VA_ARGS__)
void sieve_validator_warning(struct sieve_validator *valdtr,
			     const char *csrc_filename,
			     unsigned int csrc_linenum,
			     unsigned int source_line, const char *fmt, ...)
			     ATTR_FORMAT(5, 6);
#define sieve_validator_warning(valdtr, ...) \
	sieve_validator_warning(valdtr, __FILE__, __LINE__, __VA_ARGS__)

#endif
