/*
 * Ibis - IRCv3 Library
 * Copyright (C) Ibis Developers
 *
 * Ibis is the legal property of its developers, whose names are too numerous
 * to list here. Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>
#include <glib/gi18n.h>

#include <birb.h>

#include <ibis.h>

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_ibis_batch_properties(void) {
	IbisBatch *batch = NULL;
	char *batch_type = NULL;
	char *parent_tag = NULL;
	char *reference_tag = NULL;

	batch = g_object_new(
		IBIS_TYPE_BATCH,
		"batch-type", "batch",
		"parent-tag", "parent",
		"reference-tag", "batch1",
		NULL);

	g_assert_true(IBIS_IS_BATCH(batch));

	g_object_get(
		G_OBJECT(batch),
		"batch-type", &batch_type,
		"parent-tag", &parent_tag,
		"reference-tag", &reference_tag,
		NULL);

	g_assert_cmpstr(batch_type, ==, "batch");
	g_clear_pointer(&batch_type, g_free);

	g_assert_cmpstr(parent_tag, ==, "parent");
	g_clear_pointer(&parent_tag, g_free);

	g_assert_cmpstr(reference_tag, ==, "batch1");
	g_clear_pointer(&reference_tag, g_free);

	g_assert_finalize_object(batch);
}

static void
test_ibis_batch_parse_normal(void) {
	IbisBatch *batch = NULL;
	IbisMessage *message = NULL;
	GError *error = NULL;
	const char *value = NULL;

	message = ibis_message_new(IBIS_MSG_BATCH);
	ibis_message_set_params(message, "+1", "magic", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_no_error(error);
	g_assert_true(IBIS_IS_BATCH(batch));

	value = ibis_batch_get_batch_type(batch);
	g_assert_cmpstr(value, ==, "magic");

	value = ibis_batch_get_parent_tag(batch);
	g_assert_null(value);

	value = ibis_batch_get_reference_tag(batch);
	g_assert_cmpstr(value, ==, "1");

	g_assert_finalize_object(batch);
	g_assert_finalize_object(message);
}

static void
test_ibis_batch_parse_nested(void) {
	IbisBatch *batch = NULL;
	IbisMessage *message = NULL;
	IbisTags *tags = NULL;
	GError *error = NULL;
	const char *value = NULL;

	message = ibis_message_new(IBIS_MSG_BATCH);
	ibis_message_set_params(message, "+1", "magic", NULL);

	tags = ibis_message_get_tags(message);
	ibis_tags_add(tags, IBIS_TAG_BATCH, "parent");

	batch = ibis_batch_parse(message, &error);
	g_assert_no_error(error);
	g_assert_true(IBIS_IS_BATCH(batch));

	value = ibis_batch_get_batch_type(batch);
	g_assert_cmpstr(value, ==, "magic");

	value = ibis_batch_get_parent_tag(batch);
	g_assert_cmpstr(value, ==, "parent");

	value = ibis_batch_get_reference_tag(batch);
	g_assert_cmpstr(value, ==, "1");

	g_assert_finalize_object(batch);
	g_assert_finalize_object(message);
}

static void
test_ibis_batch_parse_minimum_parameters(void) {
	IbisBatch *batch = NULL;
	IbisMessage *message = NULL;
	GError *error = NULL;

	/* First test with no parameters. */
	message = ibis_message_new(IBIS_MSG_BATCH);

	batch = ibis_batch_parse(message, &error);
	g_assert_error(error,
	               IBIS_BATCH_PARSE_ERROR,
	               IBIS_BATCH_PARSE_ERROR_NOT_ENOUGH_PARAMS);
	g_clear_error(&error);
	g_assert_null(batch);

	/* Now add a single parameter. */
	ibis_message_set_params(message, "+foo", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_error(error,
	               IBIS_BATCH_PARSE_ERROR,
	               IBIS_BATCH_PARSE_ERROR_NOT_ENOUGH_PARAMS);
	g_clear_error(&error);
	g_assert_null(batch);

	/* Finally parse with two parameters. */
	ibis_message_set_params(message, "+bar", "batch-type", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_no_error(error);
	g_assert_true(IBIS_IS_BATCH(batch));
	g_assert_finalize_object(batch);

	g_assert_finalize_object(message);
}

static void
test_ibis_batch_parse_invalid_reference_tag(void) {
	IbisBatch *batch = NULL;
	IbisMessage *message = NULL;
	GError *error = NULL;

	message = ibis_message_new(IBIS_MSG_BATCH);

	/* Start with an empty reference tag. */
	ibis_message_set_params(message, "", "batch-type", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_error(error,
	               IBIS_BATCH_PARSE_ERROR,
	               IBIS_BATCH_PARSE_ERROR_INVALID_REFERENCE_TAG);
	g_clear_error(&error);
	g_assert_null(batch);

	/* Now a reference tag that doesn't start with a +. */
	ibis_message_set_params(message, "-", "batch-type", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_error(error,
	               IBIS_BATCH_PARSE_ERROR,
	               IBIS_BATCH_PARSE_ERROR_INVALID_REFERENCE_TAG);
	g_clear_error(&error);
	g_assert_null(batch);

	/* Finally with a valid reference tag. */
	ibis_message_set_params(message, "+foo", "batch-type", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_no_error(error);
	g_assert_true(IBIS_IS_BATCH(batch));
	g_assert_finalize_object(batch);

	g_assert_finalize_object(message);
}

static void
test_ibis_batch_parse_invalid_batch_type(void) {
	IbisBatch *batch = NULL;
	IbisMessage *message = NULL;
	GError *error = NULL;

	message = ibis_message_new(IBIS_MSG_BATCH);

	/* Start with an empty batch type. */
	ibis_message_set_params(message, "+foo", "", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_error(error,
	               IBIS_BATCH_PARSE_ERROR,
	               IBIS_BATCH_PARSE_ERROR_INVALID_BATCH_TYPE);
	g_clear_error(&error);
	g_assert_null(batch);

	/* Finally with a valid batch type. */
	ibis_message_set_params(message, "+foo", "batch-type", NULL);

	batch = ibis_batch_parse(message, &error);
	g_assert_no_error(error);
	g_assert_true(IBIS_IS_BATCH(batch));
	g_assert_finalize_object(batch);

	g_assert_finalize_object(message);
}

static void
test_ibis_batch_add_message_normal(void) {
	IbisBatch *batch = NULL;
	IbisMessage *batch_message = NULL;
	GError *error = NULL;
	guint counter = 0;

	batch_message = ibis_message_new(IBIS_MSG_BATCH);
	ibis_message_set_params(batch_message, "+foo", "testing", NULL);
	batch = ibis_batch_parse(batch_message, &error);
	g_assert_no_error(error);
	g_assert_finalize_object(batch_message);

	birb_count_list_model_items_changed(G_LIST_MODEL(batch), &counter);

	for(guint i = 0; i < 5; i++) {
		IbisMessage *message = NULL;
		IbisTags *tags = NULL;
		gboolean result = FALSE;
		guint n_items = 0;

		/* Reset the items changed counter. */
		counter = 0;

		message = ibis_message_new(IBIS_MSG_PRIVMSG);
		tags = ibis_message_get_tags(message);
		ibis_tags_add(tags, IBIS_TAG_BATCH, "foo");

		result = ibis_batch_add_message(batch, message, &error);
		g_assert_no_error(error);
		g_assert_true(result);

		/* Make sure the items changed signal got called. */
		g_assert_cmpuint(counter, ==, 1);

		/* Make sure the number of items is correct. */
		n_items = g_list_model_get_n_items(G_LIST_MODEL(batch));
		g_assert_cmpuint(n_items, ==, i + 1);

		/* The messages still exist in the batch, so we can't use
		 * g_assert_finalize_object here.
		 */
		g_clear_object(&message);
	}

	g_assert_finalize_object(batch);
}

static void
test_ibis_batch_add_message_invalid_reference_tag(void) {
	IbisBatch *batch = NULL;
	IbisMessage *batch_message = NULL;
	IbisMessage *message = NULL;
	IbisTags *tags = NULL;
	GError *error = NULL;
	guint counter = 0;
	guint n_items = 0;
	gboolean result = FALSE;

	batch_message = ibis_message_new(IBIS_MSG_BATCH);
	ibis_message_set_params(batch_message, "+foo", "testing", NULL);
	batch = ibis_batch_parse(batch_message, &error);
	g_assert_no_error(error);
	g_assert_finalize_object(batch_message);

	birb_count_list_model_items_changed(G_LIST_MODEL(batch), &counter);

	/* Attempt to add a message without the batch tag. */
	message = ibis_message_new(IBIS_MSG_PRIVMSG);
	counter = 0;

	result = ibis_batch_add_message(batch, message, &error);
	g_assert_error(error,
	               IBIS_BATCH_ADD_MESSAGE_ERROR,
	               IBIS_BATCH_ADD_MESSAGE_ERROR_INVALID_REFERENCE_TAG);
	g_clear_error(&error);
	g_assert_false(result);
	g_assert_cmpuint(counter, ==, 0);
	n_items = g_list_model_get_n_items(G_LIST_MODEL(batch));
	g_assert_cmpuint(n_items, ==, 0);
	g_clear_object(&message);

	/* Add a null batch tag. */
	message = ibis_message_new(IBIS_MSG_PRIVMSG);
	tags = ibis_message_get_tags(message);
	ibis_tags_add(tags, IBIS_TAG_BATCH, NULL);
	counter = 0;

	result = ibis_batch_add_message(batch, message, &error);
	g_assert_error(error,
	               IBIS_BATCH_ADD_MESSAGE_ERROR,
	               IBIS_BATCH_ADD_MESSAGE_ERROR_INVALID_REFERENCE_TAG);
	g_clear_error(&error);
	g_assert_false(result);
	g_assert_cmpuint(counter, ==, 0);
	n_items = g_list_model_get_n_items(G_LIST_MODEL(batch));
	g_assert_cmpuint(n_items, ==, 0);
	g_clear_object(&message);

	/* Add an empty batch tag. */
	message = ibis_message_new(IBIS_MSG_PRIVMSG);
	tags = ibis_message_get_tags(message);
	ibis_tags_add(tags, IBIS_TAG_BATCH, "");
	counter = 0;

	result = ibis_batch_add_message(batch, message, &error);
	g_assert_error(error,
	               IBIS_BATCH_ADD_MESSAGE_ERROR,
	               IBIS_BATCH_ADD_MESSAGE_ERROR_INVALID_REFERENCE_TAG);
	g_clear_error(&error);
	g_assert_false(result);
	g_assert_cmpuint(counter, ==, 0);
	n_items = g_list_model_get_n_items(G_LIST_MODEL(batch));
	g_assert_cmpuint(n_items, ==, 0);
	g_clear_object(&message);

	/* Add with the correct reference tag. */
	message = ibis_message_new(IBIS_MSG_PRIVMSG);
	tags = ibis_message_get_tags(message);
	ibis_tags_add(tags, IBIS_TAG_BATCH, "foo");
	counter = 0;

	result = ibis_batch_add_message(batch, message, &error);
	g_assert_no_error(error);
	g_assert_true(result);
	g_assert_cmpuint(counter, ==, 1);
	n_items = g_list_model_get_n_items(G_LIST_MODEL(batch));
	g_assert_cmpuint(n_items, ==, 1);
	g_clear_object(&message);

	g_assert_finalize_object(batch);
}

static void
test_ibis_batch_is_end(void) {
	IbisMessage *message = NULL;

	/* Make sure non batch messages return null. */
	message = ibis_message_new(IBIS_MSG_PRIVMSG);
	g_assert_null(ibis_batch_is_end(message));
	g_clear_object(&message);

	/* Make sure the wrong number of parameters returns null. */
	message = ibis_message_new(IBIS_MSG_BATCH);

	g_assert_null(ibis_batch_is_end(message));

	ibis_message_set_params(message, "one", "two", NULL);
	g_assert_null(ibis_batch_is_end(message));

	/* Make sure a bad reference tags return null. */
	ibis_message_set_params(message, "foo", NULL);
	g_assert_null(ibis_batch_is_end(message));

	ibis_message_set_params(message, "-", NULL);
	g_assert_null(ibis_batch_is_end(message));

	/* Finally make sure it works as expected. */
	ibis_message_set_params(message, "-foo", NULL);
	g_assert_cmpstr(ibis_batch_is_end(message), ==, "foo");

	g_assert_finalize_object(message);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_set_nonfatal_assertions();

	g_test_add_func("/ibis/batch/properties", test_ibis_batch_properties);

	g_test_add_func("/ibis/batch/parse/normal", test_ibis_batch_parse_normal);
	g_test_add_func("/ibis/batch/parse/nested", test_ibis_batch_parse_nested);
	g_test_add_func("/ibis/batch/parse/minimum-parameters",
	                test_ibis_batch_parse_minimum_parameters);
	g_test_add_func("/ibis/batch/parse/invalid-reference-tag",
	                test_ibis_batch_parse_invalid_reference_tag);
	g_test_add_func("/ibis/batch/parse/invalid-batch-type",
	                test_ibis_batch_parse_invalid_batch_type);

	g_test_add_func("/ibis/batch/add_message/normal",
	                test_ibis_batch_add_message_normal);
	g_test_add_func("/ibis/batch/add_message/invalid-reference-tag",
	                test_ibis_batch_add_message_invalid_reference_tag);

	g_test_add_func("/ibis/batch/is-end", test_ibis_batch_is_end);

	return g_test_run();
}
