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

#include "lib.h"
#include "ostream.h"
#include "str.h"

#include "sieve-common.h"
#include "sieve-error.h"
#include "sieve-stringlist.h"
#include "sieve-actions.h"
#include "sieve-interpreter.h"
#include "sieve-result.h"

#include "testsuite-common.h"
#include "testsuite-log.h"
#include "testsuite-message.h"
#include "testsuite-mailstore.h"

#include "testsuite-result.h"

struct sieve_execute_env testsuite_execute_env;

static pool_t testsuite_execute_pool = NULL;
static struct sieve_result *_testsuite_result = NULL;
static struct sieve_result_execution *_testsuite_rexec = NULL;

void testsuite_result_init(void)
{
	struct sieve_instance *svinst = testsuite_sieve_instance;

	testsuite_execute_pool = pool_alloconly_create("sieve execution", 4096);

	sieve_execute_init(&testsuite_execute_env, testsuite_sieve_instance,
			   testsuite_execute_pool, &testsuite_msgdata,
			   testsuite_scriptenv, 0);

	_testsuite_result = sieve_result_create(svinst, testsuite_execute_pool,
						&testsuite_execute_env);
}

void testsuite_result_deinit(void)
{
	sieve_result_execution_destroy(&_testsuite_rexec);
	if (_testsuite_result != NULL)
		sieve_result_unref(&_testsuite_result);
	sieve_execute_deinit(&testsuite_execute_env);
	pool_unref(&testsuite_execute_pool);
}

void testsuite_result_reset(const struct sieve_runtime_env *renv)
{
	struct sieve_instance *svinst = testsuite_sieve_instance;

	if (_testsuite_result != NULL) {
		sieve_result_execution_destroy(&_testsuite_rexec);
		sieve_result_unref(&_testsuite_result);
		pool_unref(&testsuite_execute_pool);
	}

	testsuite_message_flush();
	testsuite_mailstore_flush();
	i_zero(testsuite_execute_env.exec_status);

	testsuite_execute_pool = pool_alloconly_create("sieve execution", 4096);
	_testsuite_result = sieve_result_create(svinst, testsuite_execute_pool,
						&testsuite_execute_env);
	sieve_interpreter_set_result(renv->interp, _testsuite_result);
}

struct sieve_result *testsuite_result_get(void)
{
	return _testsuite_result;
}

struct sieve_result_iterate_context *testsuite_result_iterate_init(void)
{
	if (_testsuite_result == NULL)
		return NULL;

	return sieve_result_iterate_init(_testsuite_result);
}

bool testsuite_result_execute(const struct sieve_runtime_env *renv)
{
	int ret;

	if (_testsuite_result == NULL) {
		sieve_runtime_error(renv, NULL,	"testsuite: "
				    "trying to execute result, "
				    "but no result evaluated yet");
		return FALSE;
	}

	testsuite_log_clear_messages();

	if (_testsuite_rexec == NULL) {
		_testsuite_rexec = sieve_result_execution_create(
			_testsuite_result, testsuite_execute_pool);
	}

	/* Execute the result */
	ret = sieve_result_execute(_testsuite_rexec, SIEVE_EXEC_OK, TRUE,
				   testsuite_log_ehandler, NULL);

	return (ret > 0);
}

void testsuite_result_print(const struct sieve_runtime_env *renv)
{
	const struct sieve_execute_env *eenv = renv->exec_env;
	struct ostream *out;

	out = o_stream_create_fd(1, 0);
	o_stream_set_no_error_handling(out, TRUE);

	o_stream_nsend_str(out, "\n--");
	sieve_result_print(_testsuite_result, eenv->scriptenv, out, NULL);
	o_stream_nsend_str(out, "--\n\n");

	o_stream_destroy(&out);
}

/*
 * Result stringlist
 */

/* Forward declarations */

static int
testsuite_result_stringlist_next_item(struct sieve_stringlist *_strlist,
				      string_t **str_r);
static void
testsuite_result_stringlist_reset(struct sieve_stringlist *_strlist);

/* Stringlist object */

struct testsuite_result_stringlist {
	struct sieve_stringlist strlist;

	struct sieve_result_iterate_context *result_iter;
	int pos, index;
};

struct sieve_stringlist *
testsuite_result_stringlist_create(const struct sieve_runtime_env *renv,
				   int index)
{
	struct testsuite_result_stringlist *strlist;

	strlist = t_new(struct testsuite_result_stringlist, 1);
	strlist->strlist.runenv = renv;
	strlist->strlist.exec_status = SIEVE_EXEC_OK;
	strlist->strlist.next_item = testsuite_result_stringlist_next_item;
	strlist->strlist.reset = testsuite_result_stringlist_reset;

	strlist->result_iter = testsuite_result_iterate_init();
 	strlist->index = index;
	strlist->pos = 0;

	return &strlist->strlist;
}

static int
testsuite_result_stringlist_next_item(struct sieve_stringlist *_strlist,
				      string_t **str_r)
{
	struct testsuite_result_stringlist *strlist =
		(struct testsuite_result_stringlist *)_strlist;
	const struct sieve_action *action;
	const char *act_name;
	bool keep;

	*str_r = NULL;

	if (strlist->index > 0 && strlist->pos > 0)
		return 0;

	do {
		if ((action = sieve_result_iterate_next(strlist->result_iter,
							&keep)) == NULL)
			return 0;

		strlist->pos++;
	} while (strlist->pos < strlist->index);

	if (keep)
		act_name = "keep";
	else {
		act_name = ((action == NULL || action->def == NULL ||
			     action->def->name == NULL) ?
			    "" : action->def->name);
	}

	*str_r = t_str_new_const(act_name, strlen(act_name));
	return 1;
}

static void testsuite_result_stringlist_reset(struct sieve_stringlist *_strlist)
{
	struct testsuite_result_stringlist *strlist =
		(struct testsuite_result_stringlist *)_strlist;

	strlist->result_iter = testsuite_result_iterate_init();
	strlist->pos = 0;
}
