/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * This library 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 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <purple.h>

/******************************************************************************
 * Test Protocol
 *****************************************************************************/
G_DECLARE_FINAL_TYPE(TestPurpleCommandsProtocol, test_purple_commands_protocol,
                     TEST_PURPLE, COMMANDS_PROTOCOL, PurpleProtocol)

struct _TestPurpleCommandsProtocol {
	PurpleProtocol parent;
};

static void
test_purple_commands_protocol_send_message_async(PurpleProtocolConversation *protocol,
                                                 PurpleConversation *conversation,
                                                 PurpleMessage *message,
                                                 GCancellable *cancellable,
                                                 GAsyncReadyCallback callback,
                                                 gpointer data)
{
	GTask *task = NULL;

	task = g_task_new(G_OBJECT(protocol), cancellable, callback, data);
	g_task_return_boolean(task, TRUE);
	g_clear_object(&task);

	purple_conversation_write_message(conversation, message);
}

static gboolean
test_purple_commands_protocol_send_message_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                                  GAsyncResult* result,
                                                  GError** error)
{
	return g_task_propagate_boolean(G_TASK(result), error);
}

static void
test_purple_commands_protocol_conversation_init(PurpleProtocolConversationInterface *iface)
{
	iface->send_message_async =
		test_purple_commands_protocol_send_message_async;
	iface->send_message_finish =
		test_purple_commands_protocol_send_message_finish;
}

G_DEFINE_FINAL_TYPE_WITH_CODE(TestPurpleCommandsProtocol,
                              test_purple_commands_protocol,
                              PURPLE_TYPE_PROTOCOL,
                              G_IMPLEMENT_INTERFACE(PURPLE_TYPE_PROTOCOL_CONVERSATION,
                                                    test_purple_commands_protocol_conversation_init))

static void
test_purple_commands_protocol_init(G_GNUC_UNUSED TestPurpleCommandsProtocol *protocol)
{
}

static void
test_purple_commands_protocol_class_init(G_GNUC_UNUSED TestPurpleCommandsProtocolClass *klass)
{
}

static PurpleProtocol *
test_purple_commands_protocol_new(void) {
	return g_object_new(
		test_purple_commands_protocol_get_type(),
		"id", "test",
		"name", "test",
		NULL);
}

/******************************************************************************
 * Helpers
 *****************************************************************************/
static PurpleConversation *
test_purple_commands_create_conversation(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;
	PurpleProtocol *protocol = NULL;

	protocol = test_purple_commands_protocol_new();
	account = g_object_new(PURPLE_TYPE_ACCOUNT,
		"protocol", protocol,
		NULL);
	g_clear_object(&protocol);

	conversation = purple_conversation_new(account,
	                                       PURPLE_CONVERSATION_TYPE_UNSET,
	                                       "test");

	g_clear_object(&account);

	return conversation;
}

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_purple_commands_say_execute(void) {
	PurpleCommand *command = NULL;
	PurpleConversation *conversation = NULL;
	PurpleMessage *message = NULL;
	GListModel *messages = NULL;
	const char *contents = NULL;

	conversation = test_purple_commands_create_conversation();

	command = purple_say_command_new();
	purple_command_execute(command, conversation, "my name");

	/* We have to iterate the main loop as this command calls an async method
	 * which holds an extra reference on the command. */
	g_main_context_iteration(NULL, FALSE);

	g_assert_cmpuint(purple_command_get_use_count(command), ==, 1);
	g_assert_finalize_object(command);

	messages = purple_conversation_get_messages(conversation);
	g_assert_true(G_IS_LIST_MODEL(messages));
	g_assert_cmpuint(g_list_model_get_n_items(messages), ==, 1);

	message = g_list_model_get_item(messages, 0);
	g_assert_true(PURPLE_IS_MESSAGE(message));

	contents = purple_message_get_contents(message);
	g_assert_cmpstr(contents, ==, "my name");

	/* The message still exists in the conversation, so we can only removed our
	 * reference which we got from g_list_model_get_item above.
	 */
	g_clear_object(&message);

	g_assert_finalize_object(conversation);
}

static void
test_purple_commands_action_execute(void) {
	PurpleCommand *command = NULL;
	PurpleConversation *conversation = NULL;
	PurpleMessage *message = NULL;
	GListModel *messages = NULL;
	const char *contents = NULL;

	conversation = test_purple_commands_create_conversation();

	command = purple_action_command_new();
	purple_command_execute(command, conversation, "sighs");

	/* We have to iterate the main loop as this command calls an async method
	 * which holds an extra reference on the command. */
	g_main_context_iteration(NULL, FALSE);

	g_assert_cmpuint(purple_command_get_use_count(command), ==, 1);
	g_assert_finalize_object(command);

	messages = purple_conversation_get_messages(conversation);
	g_assert_true(G_IS_LIST_MODEL(messages));
	g_assert_cmpuint(g_list_model_get_n_items(messages), ==, 1);

	message = g_list_model_get_item(messages, 0);
	g_assert_true(PURPLE_IS_MESSAGE(message));

	contents = purple_message_get_contents(message);
	g_assert_cmpstr(contents, ==, "sighs");

	g_assert_true(purple_message_get_action(message));

	/* The message still exists in the conversation, so we can only removed our
	 * reference which we got from g_list_model_get_item above.
	 */
	g_clear_object(&message);

	g_assert_finalize_object(conversation);
}

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

	g_test_add_func("/commands/say/execute", test_purple_commands_say_execute);
	g_test_add_func("/commands/action/execute",
	                test_purple_commands_action_execute);

	return g_test_run();
}
