/*
 * 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 <birb.h>

#include <purple.h>

/******************************************************************************
 * Callbacks
 *****************************************************************************/
static void
test_purple_account_setting_changed_cb(PurpleAccountSetting *setting,
                                       gpointer data)
{
	guint *counter = data;

	g_assert_true(PURPLE_IS_ACCOUNT_SETTING(setting));

	*counter = *counter + 1;
}

/******************************************************************************
 * Properties Test
 *****************************************************************************/
static void
test_purple_account_setting_properties(void) {
	PurpleAccountSetting *setting = NULL;
	char *hint = NULL;
	char *id = NULL;
	char *label = NULL;
	int weight = 0;
	gboolean advanced = FALSE;

	setting = g_object_new(
		PURPLE_TYPE_ACCOUNT_SETTING,
		"advanced", TRUE,
		"hint", "hint",
		"id", "id",
		"label", "label",
		"weight", 42,
		NULL);

	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING);

	g_object_get(
		G_OBJECT(setting),
		"advanced", &advanced,
		"hint", &hint,
		"id", &id,
		"label", &label,
		"weight", &weight,
		NULL);

	g_assert_true(advanced);

	g_assert_cmpstr(hint, ==, "hint");
	g_clear_pointer(&hint, g_free);

	g_assert_cmpstr(id, ==, "id");
	g_clear_pointer(&id, g_free);

	g_assert_cmpstr(label, ==, "label");
	g_clear_pointer(&label, g_free);

	g_assert_cmpint(weight, ==, 42);

	g_assert_finalize_object(setting);
}

/******************************************************************************
 * Changed Tests
 *****************************************************************************/
static void
test_purple_account_setting_changed(void) {
	PurpleAccountSetting *setting = NULL;
	guint counter = 0;

	setting = g_object_new(
		PURPLE_TYPE_ACCOUNT_SETTING,
		"id", "test-setting",
		"label", "Test Setting",
		NULL);

	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING);

	g_signal_connect(setting, "changed",
	                 G_CALLBACK(test_purple_account_setting_changed_cb),
	                 &counter);

	counter = 0;
	purple_account_setting_changed(setting);
	g_assert_cmpuint(counter, ==, 1);

	g_assert_finalize_object(setting);
}

/******************************************************************************
 * Boolean Tests
 *****************************************************************************/
static void
test_purple_account_setting_boolean(void) {
	PurpleAccountSetting *setting = NULL;
	guint changed_counter = 0;
	guint property_counter = 0;
	gboolean value = FALSE;

	setting = purple_account_setting_boolean_new("id", "Label", TRUE);
	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING_BOOLEAN);

	value = purple_account_setting_boolean_get_value(PURPLE_ACCOUNT_SETTING_BOOLEAN(setting));
	g_assert_true(value);

	g_signal_connect(setting, "changed",
	                 G_CALLBACK(test_purple_account_setting_changed_cb),
	                 &changed_counter);
	birb_count_property_changed(G_OBJECT(setting), "value", &property_counter);

	changed_counter = 0;
	property_counter = 0;
	purple_account_setting_boolean_set_value(PURPLE_ACCOUNT_SETTING_BOOLEAN(setting),
	                                         FALSE);

	value = purple_account_setting_boolean_get_value(PURPLE_ACCOUNT_SETTING_BOOLEAN(setting));
	g_assert_false(value);

	g_assert_cmpuint(changed_counter, ==, 1);
	g_assert_cmpuint(property_counter, ==, 1);

	g_assert_finalize_object(setting);
}

/******************************************************************************
 * Int Tests
 *****************************************************************************/
static void
test_purple_account_setting_int(void) {
	PurpleAccountSetting *setting = NULL;
	guint changed_counter = 0;
	guint property_counter = 0;
	int value = 0;

	setting = purple_account_setting_int_new("id", "Label", 1337);
	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING_INT);

	value = purple_account_setting_int_get_value(PURPLE_ACCOUNT_SETTING_INT(setting));
	g_assert_cmpint(value, ==, 1337);

	g_signal_connect(setting, "changed",
	                 G_CALLBACK(test_purple_account_setting_changed_cb),
	                 &changed_counter);
	birb_count_property_changed(G_OBJECT(setting), "value", &property_counter);

	changed_counter = 0;
	property_counter = 0;
	purple_account_setting_int_set_value(PURPLE_ACCOUNT_SETTING_INT(setting),
	                                     42);

	value = purple_account_setting_int_get_value(PURPLE_ACCOUNT_SETTING_INT(setting));
	g_assert_cmpint(value, ==, 42);

	g_assert_cmpuint(changed_counter, ==, 1);
	g_assert_cmpuint(property_counter, ==, 1);

	g_assert_finalize_object(setting);
}

/******************************************************************************
 * String Tests
 *****************************************************************************/
static void
test_purple_account_setting_string(void) {
	PurpleAccountSetting *setting = NULL;
	guint changed_counter = 0;
	guint property_counter = 0;
	const char *value = NULL;

	setting = purple_account_setting_string_new("id", "Label", "default");
	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING_STRING);

	value = purple_account_setting_string_get_value(PURPLE_ACCOUNT_SETTING_STRING(setting));
	g_assert_cmpstr(value, ==, "default");

	g_signal_connect(setting, "changed",
	                 G_CALLBACK(test_purple_account_setting_changed_cb),
	                 &changed_counter);
	birb_count_property_changed(G_OBJECT(setting), "value", &property_counter);

	changed_counter = 0;
	property_counter = 0;
	purple_account_setting_string_set_value(PURPLE_ACCOUNT_SETTING_STRING(setting),
	                                        "new-value");

	value = purple_account_setting_string_get_value(PURPLE_ACCOUNT_SETTING_STRING(setting));
	g_assert_cmpstr(value, ==, "new-value");

	g_assert_cmpuint(changed_counter, ==, 1);
	g_assert_cmpuint(property_counter, ==, 1);

	g_assert_finalize_object(setting);
}

/******************************************************************************
 * StringList Tests
 *****************************************************************************/
static void
test_purple_account_setting_string_list_new(void) {
	PurpleAccountSetting *setting = NULL;

	setting = purple_account_setting_string_list_new("list1", "List 1");

	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING_STRING_LIST);
	g_assert_true(G_IS_LIST_MODEL(setting));

	g_assert_finalize_object(setting);
}

static void
test_purple_account_setting_string_list_add(void) {
	PurpleAccountSetting *setting = NULL;
	PurpleAccountSettingStringList *list = NULL;
	BirbLocalizedString *item = NULL;
	GListModel *model = NULL;
	guint counter = 0;
	gboolean result = FALSE;

	setting = purple_account_setting_string_list_new("list1", "List 1");
	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING_STRING_LIST);

	list = PURPLE_ACCOUNT_SETTING_STRING_LIST(setting);
	model = G_LIST_MODEL(setting);

	birb_count_list_model_items_changed(model, &counter);

	birb_assert_list_model_n_items(model, 0);

	/* Add an item and verify it. */
	counter = 0;
	result = purple_account_setting_string_list_add_item(list, "item1",
	                                                     "Item 1");
	g_assert_true(result);
	birb_assert_list_model_n_items(model, 1);
	g_assert_cmpuint(counter, ==, 1);

	item = g_list_model_get_item(model, 0);
	birb_assert_type(item, BIRB_TYPE_LOCALIZED_STRING);
	g_assert_cmpstr(birb_localized_string_get_id(item), ==, "item1");
	g_assert_cmpstr(birb_localized_string_get_label(item), ==, "Item 1");
	g_clear_object(&item);

	/* Add another item and verify it. */
	counter = 0;
	result = purple_account_setting_string_list_add_item(list, "item2",
	                                                     "Item 2");
	g_assert_true(result);
	birb_assert_list_model_n_items(model, 2);
	g_assert_cmpuint(counter, ==, 1);

	item = g_list_model_get_item(model, 1);
	birb_assert_type(item, BIRB_TYPE_LOCALIZED_STRING);
	g_assert_cmpstr(birb_localized_string_get_id(item), ==, "item2");
	g_assert_cmpstr(birb_localized_string_get_label(item), ==, "Item 2");
	g_clear_object(&item);

	/* Add a duplicate and verify it did not get added. */
	counter = 0;
	result = purple_account_setting_string_list_add_item(list, "item1",
	                                                     "Item 3");
	g_assert_false(result);
	birb_assert_list_model_n_items(model, 2);
	g_assert_cmpuint(counter, ==, 0);

	/* Clean up. */
	g_assert_finalize_object(setting);
}

static void
test_purple_account_setting_string_list_set_active(void) {
	PurpleAccountSetting *setting = NULL;
	PurpleAccountSettingStringList *list = NULL;
	const char *active_item = NULL;
	guint counter = 0;

	setting = purple_account_setting_string_list_new("list1", "List 1");
	birb_assert_type(setting, PURPLE_TYPE_ACCOUNT_SETTING_STRING_LIST);

	list = PURPLE_ACCOUNT_SETTING_STRING_LIST(setting);
	birb_count_property_changed(G_OBJECT(setting), "active-item", &counter);

	/* Make sure there is nothing selected by default. */
	active_item = purple_account_setting_string_list_get_active_item(list);
	g_assert_cmpstr(active_item, ==, NULL);

	/* Set active-item to a non existent item and make sure it doesn't get
	 * set.
	 */
	counter = 0;
	purple_account_setting_string_list_set_active_item(list, "missing1");
	active_item = purple_account_setting_string_list_get_active_item(list);
	g_assert_cmpstr(active_item, ==, NULL);
	g_assert_cmpuint(counter, ==, 0);

	/* Add an item. */
	purple_account_setting_string_list_add_item(list, "item1", "Item 1");
	birb_assert_list_model_n_items(G_LIST_MODEL(list), 1);

	/* Make sure the active item hasn't changed. */
	active_item = purple_account_setting_string_list_get_active_item(list);
	g_assert_cmpstr(active_item, ==, NULL);

	/* Try setting the active item to another item that doesn't exist. */
	counter = 0;
	purple_account_setting_string_list_set_active_item(list, "missing2");
	active_item = purple_account_setting_string_list_get_active_item(list);
	g_assert_cmpstr(active_item, ==, NULL);
	g_assert_cmpuint(counter, ==, 0);

	/* Set the active item to the existing item. */
	counter = 0;
	purple_account_setting_string_list_set_active_item(list, "item1");
	active_item = purple_account_setting_string_list_get_active_item(list);
	g_assert_cmpstr(active_item, ==, "item1");
	g_assert_cmpuint(counter, ==, 1);

	/* Set the active item to null. */
	counter = 0;
	purple_account_setting_string_list_set_active_item(list, NULL);
	active_item = purple_account_setting_string_list_get_active_item(list);
	g_assert_cmpstr(active_item, ==, NULL);
	g_assert_cmpuint(counter, ==, 1);

	/* Clean up. */
	g_assert_finalize_object(setting);
}

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

	g_test_add_func("/account-setting/properties",
	                test_purple_account_setting_properties);

	g_test_add_func("/account-setting/changed",
	                test_purple_account_setting_changed);

	g_test_add_func("/account-setting/boolean",
	                test_purple_account_setting_boolean);
	g_test_add_func("/account-setting/int", test_purple_account_setting_int);
	g_test_add_func("/account-setting/string",
	                test_purple_account_setting_string);

	g_test_add_func("/account-setting/string-list/new",
	                test_purple_account_setting_string_list_new);
	g_test_add_func("/account-setting/string-list/add",
	                test_purple_account_setting_string_list_add);
	g_test_add_func("/account-setting/string-list/set-active",
	                test_purple_account_setting_string_list_set_active);

	return g_test_run();
}
