/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
 */

/* Match-type ':count'
 */

#include "lib.h"
#include "str.h"
#include "str-sanitize.h"

#include "sieve-common.h"
#include "sieve-ast.h"
#include "sieve-stringlist.h"
#include "sieve-code.h"
#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-comparators.h"
#include "sieve-match-types.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-interpreter.h"
#include "sieve-runtime-trace.h"
#include "sieve-match.h"

#include "ext-relational-common.h"

/*
 * Forward declarations
 */

static int
mcht_count_match(struct sieve_match_context *mctx,
		 struct sieve_stringlist *value_list,
		 struct sieve_stringlist *key_list);

/*
 * Match-type objects
 */

const struct sieve_match_type_def count_match_type = {
	SIEVE_OBJECT("count",
		&rel_match_type_operand, RELATIONAL_COUNT),
	.validate = mcht_relational_validate,
};

#define COUNT_MATCH_TYPE(name, rel_match)                           \
const struct sieve_match_type_def rel_match_count_ ## name = {      \
	SIEVE_OBJECT("count-" #name,                                \
		     &rel_match_type_operand,                       \
		     REL_MATCH_INDEX(RELATIONAL_COUNT, rel_match)), \
	.match = mcht_count_match,                                  \
}

COUNT_MATCH_TYPE(gt, REL_MATCH_GREATER);
COUNT_MATCH_TYPE(ge, REL_MATCH_GREATER_EQUAL);
COUNT_MATCH_TYPE(lt, REL_MATCH_LESS);
COUNT_MATCH_TYPE(le, REL_MATCH_LESS_EQUAL);
COUNT_MATCH_TYPE(eq, REL_MATCH_EQUAL);
COUNT_MATCH_TYPE(ne, REL_MATCH_NOT_EQUAL);

/*
 * Match-type implementation
 */

static int
mcht_count_match(struct sieve_match_context *mctx,
		 struct sieve_stringlist *value_list,
		 struct sieve_stringlist *key_list)
{
	const struct sieve_runtime_env *renv = mctx->runenv;
	bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
	int count;
	string_t *key_item;
	int match, ret;

	count = sieve_stringlist_get_length(value_list);
	if (count < 0) {
		mctx->exec_status = value_list->exec_status;
		return -1;
	}

	sieve_stringlist_reset(key_list);

	string_t *value = t_str_new(20);
	str_printfa(value, "%d", count);

	if (trace) {
		sieve_runtime_trace(renv, 0, "matching count value '%s'",
				    str_sanitize(str_c(value), 80));
	}

	sieve_runtime_trace_descend(renv);

	/* Match to all key values */
	key_item = NULL;
	match = 0;
	while (match == 0 &&
	       (ret = sieve_stringlist_next_item(key_list, &key_item)) > 0)
	{
		match = mcht_value_match_key(
			mctx, str_c(value), str_len(value),
			str_c(key_item), str_len(key_item));

		if (trace) {
			sieve_runtime_trace(renv, 0, "with key '%s' => %d",
					    str_sanitize(str_c(key_item), 80),
					    ret);
		}
	}

	sieve_runtime_trace_ascend(renv);

	if (ret < 0) {
		mctx->exec_status = key_list->exec_status;
		match = -1;
	}
	return match;
}
