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

#include "imap-common.h"
#include "buffer.h"
#include "imap-commands.h"
#include "imap-search-args.h"
#include "imap-search.h"

struct sort_name {
	enum mail_sort_type type;
	const char *name;
};

static struct sort_name sort_names[] = {
	{ MAIL_SORT_ARRIVAL,		"arrival" },
	{ MAIL_SORT_CC,			"cc" },
	{ MAIL_SORT_DATE,		"date" },
	{ MAIL_SORT_FROM,		"from" },
	{ MAIL_SORT_SIZE,		"size" },
	{ MAIL_SORT_SUBJECT,		"subject" },
	{ MAIL_SORT_TO,			"to" },
	{ MAIL_SORT_RELEVANCY,		"x-score" }, /* FIXME: obsolete */
	{ MAIL_SORT_RELEVANCY,		"relevancy" },
	{ MAIL_SORT_DISPLAYFROM,	"displayfrom" },
	{ MAIL_SORT_DISPLAYTO,		"displayto" },

	{ MAIL_SORT_END,		NULL }
};

static int
get_sort_program(struct client_command_context *cmd,
		 const struct imap_arg *args,
		 enum mail_sort_type program[MAX_SORT_PROGRAM_SIZE])
{
	enum mail_sort_type mask = 0;
	const char *arg;
	unsigned int i, pos;
	bool reverse, last_reverse;

	if (IMAP_ARG_IS_EOL(args)) {
		/* empty list */
		client_send_command_error(cmd, "Empty sort program.");
		return -1;
	}

	pos = 0; reverse = last_reverse = FALSE;
	for (; imap_arg_get_astring(args, &arg); args++) {
		last_reverse = strcasecmp(arg, "reverse") == 0;
		if (last_reverse) {
			reverse = !reverse;
			continue;
		}

		for (i = 0; sort_names[i].type != MAIL_SORT_END; i++) {
			if (strcasecmp(arg, sort_names[i].name) == 0)
				break;
		}

		if (sort_names[i].type == MAIL_SORT_END) {
			client_send_command_error(cmd, t_strconcat(
				"Unknown sort argument: ", arg, NULL));
			return -1;
		}

		if ((mask & sort_names[i].type) != 0)
			continue;
		mask |= sort_names[i].type;

		/* @UNSAFE: mask check should prevent us from ever
		   overflowing */
		i_assert(pos < MAX_SORT_PROGRAM_SIZE-1);
		program[pos++] = sort_names[i].type |
			(reverse ? MAIL_SORT_FLAG_REVERSE : 0);
		reverse = FALSE;
	}
	if (last_reverse) {
		client_send_command_error(cmd, "Sort list ends with REVERSE.");
		return -1;
	}
	program[pos] = MAIL_SORT_END;

	if (!IMAP_ARG_IS_EOL(args)) {
		client_send_command_error(cmd,
					  "Invalid sort list argument.");
		return -1;
	}

	return 0;
}

bool cmd_sort(struct client_command_context *cmd)
{
	struct imap_search_context *ctx;
	struct mail_search_args *sargs;
	enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
	const struct imap_arg *args, *list_args;
	const char *charset;
	int ret;

	if (!client_read_args(cmd, 0, 0, &args))
		return FALSE;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	ctx = p_new(cmd->pool, struct imap_search_context, 1);
	ctx->cmd = cmd;

	if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
		/* error / waiting for unambiguity */
		return ret < 0;
	}

	/* sort program */
	if (!imap_arg_get_list(args, &list_args)) {
		client_send_command_error(cmd, "Invalid sort argument.");
		imap_search_context_free(ctx);
		return TRUE;
	}

	if (get_sort_program(cmd, list_args, sort_program) < 0) {
		imap_search_context_free(ctx);
		return TRUE;
	}
	args++;

	/* charset */
	if (!imap_arg_get_astring(args, &charset)) {
		client_send_command_error(cmd, "Invalid charset argument.");
		imap_search_context_free(ctx);
		return TRUE;
	}
	args++;

	ret = imap_search_args_build(cmd, args, charset, &sargs);
	if (ret <= 0) {
		imap_search_context_free(ctx);
		return ret < 0;
	}

	return imap_search_start(ctx, sargs, sort_program);
}
