/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * Purple 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 library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU 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 General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <birb.h>

#include <purpleconfig.h>

#include <glib/gi18n-lib.h>
#define G_SETTINGS_ENABLE_BACKEND
#include <gio/gsettingsbackend.h>

#include "purplecore.h"

#include "debug.h"
#include "plugins.h"
#include "purpleaccountmanagerprivate.h"
#include "purplebadgemanagerprivate.h"
#include "purplecommandmanagerprivate.h"
#include "purplecontactmanagerprivate.h"
#include "purpleconversationmanagerprivate.h"
#include "purplecredentialmanagerprivate.h"
#include "purplefiletransfermanagerprivate.h"
#include "purpleidlemanagerprivate.h"
#include "purplenotificationmanagerprivate.h"
#include "purplepath.h"
#include "purplepresencemanagerprivate.h"
#include "purpleprotocolmanagerprivate.h"
#include "purpleschedulerprivate.h"
#include "purplewhiteboardmanagerprivate.h"
#include "util.h"
#ifdef _WIN32
#include "win32/win32dep.h"
#endif

enum {
	PROP_0,
	PROP_ACCOUNT_MANAGER,
	PROP_BADGE_MANAGER,
	PROP_COMMAND_MANAGER,
	PROP_CONTACT_MANAGER,
	PROP_CONVERSATION_MANAGER,
	PROP_CREDENTIAL_MANAGER,
	PROP_FILE_TRANSFER_MANAGER,
	PROP_IDLE_MANAGER,
	PROP_NOTIFICATION_MANAGER,
	PROP_PRESENCE_MANAGER,
	PROP_PROTOCOL_MANAGER,
	PROP_SCHEDULER,
	PROP_SETTINGS_BACKEND,
	PROP_UI,
	PROP_WHITEBOARD_MANAGER,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

struct _PurpleCore {
	GObject parent;

	PurpleAccountManager *account_manager;
	PurpleBadgeManager *badge_manager;
	PurpleCommandManager *command_manager;
	PurpleContactManager *contact_manager;
	PurpleConversationManager *conversation_manager;
	PurpleCredentialManager *credential_manager;
	PurpleFileTransferManager *file_transfer_manager;
	PurpleIdleManager *idle_manager;
	PurpleNotificationManager *notification_manager;
	PurplePresenceManager *presence_manager;
	PurpleProtocolManager *protocol_manager;
	PurpleScheduler *scheduler;
	GSettingsBackend *settings_backend;
	PurpleUi *ui;
	PurpleWhiteboardManager *whiteboard_manager;
};

G_DEFINE_QUARK(purple-core-error, purple_core_error)

static PurpleCore *default_core = NULL;

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
purple_core_set_ui(PurpleCore *core, PurpleUi *ui) {
	g_return_if_fail(PURPLE_IS_CORE(core));
	g_return_if_fail(PURPLE_IS_UI(ui));

	if(g_set_object(&core->ui, ui)) {
		g_object_notify_by_pspec(G_OBJECT(core), properties[PROP_UI]);
	}
}

/******************************************************************************
 * GInitableIface Implementation
 *****************************************************************************/
static gboolean
purple_core_initable_init(GInitable *initable,
                          G_GNUC_UNUSED GCancellable *cancellable,
                          GError **error)
{
	PurpleCore *core = PURPLE_CORE(initable);

	if(!PURPLE_IS_UI(core->ui)) {
		g_set_error_literal(error,
		                    PURPLE_CORE_ERROR,
		                    PURPLE_CORE_ERROR_REQUIRED_PROPERTY_NOT_SET,
		                    _("the ui property was not set to an instance of "
		                      "Purple.Ui"));

		return FALSE;
	}

	return TRUE;
}

static void
purple_core_initable_iface_init(GInitableIface *iface) {
	iface->init = purple_core_initable_init;
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
G_DEFINE_FINAL_TYPE_WITH_CODE(PurpleCore, purple_core, G_TYPE_OBJECT,
                              G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE,
                                                    purple_core_initable_iface_init))

static void
purple_core_finalize(GObject *obj) {
	PurpleCore *core = PURPLE_CORE(obj);

	g_clear_object(&core->account_manager);
	g_clear_object(&core->badge_manager);
	g_clear_object(&core->command_manager);
	g_clear_object(&core->contact_manager);
	g_clear_object(&core->conversation_manager);
	g_clear_object(&core->credential_manager);
	g_clear_object(&core->file_transfer_manager);
	g_clear_object(&core->idle_manager);
	g_clear_object(&core->notification_manager);
	g_clear_object(&core->presence_manager);
	g_clear_object(&core->protocol_manager);
	g_clear_object(&core->scheduler);
	g_clear_object(&core->settings_backend);
	g_clear_object(&core->ui);
	g_clear_object(&core->whiteboard_manager);

	G_OBJECT_CLASS(purple_core_parent_class)->finalize(obj);
}

static void
purple_core_get_property(GObject *obj, guint param_id, GValue *value,
                         GParamSpec *pspec)
{
	PurpleCore *core = PURPLE_CORE(obj);

	switch(param_id) {
	case PROP_ACCOUNT_MANAGER:
		g_value_set_object(value, purple_core_get_account_manager(core));
		break;
	case PROP_BADGE_MANAGER:
		g_value_set_object(value, purple_core_get_badge_manager(core));
		break;
	case PROP_COMMAND_MANAGER:
		g_value_set_object(value, purple_core_get_command_manager(core));
		break;
	case PROP_CONTACT_MANAGER:
		g_value_set_object(value, purple_core_get_contact_manager(core));
		break;
	case PROP_CONVERSATION_MANAGER:
		g_value_set_object(value, purple_core_get_conversation_manager(core));
		break;
	case PROP_CREDENTIAL_MANAGER:
		g_value_set_object(value, purple_core_get_credential_manager(core));
		break;
	case PROP_FILE_TRANSFER_MANAGER:
		g_value_set_object(value, purple_core_get_file_transfer_manager(core));
		break;
	case PROP_IDLE_MANAGER:
		g_value_set_object(value, purple_core_get_idle_manager(core));
		break;
	case PROP_NOTIFICATION_MANAGER:
		g_value_set_object(value, purple_core_get_notification_manager(core));
		break;
	case PROP_PRESENCE_MANAGER:
		g_value_set_object(value, purple_core_get_presence_manager(core));
		break;
	case PROP_PROTOCOL_MANAGER:
		g_value_set_object(value, purple_core_get_protocol_manager(core));
		break;
	case PROP_SETTINGS_BACKEND:
		g_value_set_object(value, purple_core_get_settings_backend(core));
		break;
	case PROP_UI:
		g_value_set_object(value, purple_core_get_ui(core));
		break;
	case PROP_WHITEBOARD_MANAGER:
		g_value_set_object(value, purple_core_get_whiteboard_manager(core));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
purple_core_set_property(GObject *obj, guint param_id, const GValue *value,
                         GParamSpec *pspec)
{
	PurpleCore *core = PURPLE_CORE(obj);

	switch(param_id) {
	case PROP_UI:
		purple_core_set_ui(core, g_value_get_object(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
purple_core_init(G_GNUC_UNUSED PurpleCore *core) {
}

static void
purple_core_class_init(PurpleCoreClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = purple_core_finalize;
	obj_class->get_property = purple_core_get_property;
	obj_class->set_property = purple_core_set_property;

	/**
	 * PurpleCore:account-manager:
	 *
	 * The account manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_ACCOUNT_MANAGER] = g_param_spec_object(
		"account-manager", NULL, NULL,
		PURPLE_TYPE_ACCOUNT_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:badge-manager:
	 *
	 * The badge manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_BADGE_MANAGER] = g_param_spec_object(
		"badge-manager", NULL, NULL,
		PURPLE_TYPE_BADGE_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:command-manager:
	 *
	 * The command manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_COMMAND_MANAGER] = g_param_spec_object(
		"command-manager", NULL, NULL,
		PURPLE_TYPE_COMMAND_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:contact-manager:
	 *
	 * The contact manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_CONTACT_MANAGER] = g_param_spec_object(
		"contact-manager", NULL, NULL,
		PURPLE_TYPE_CONTACT_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:conversation-manager:
	 *
	 * The conversation manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_CONVERSATION_MANAGER] = g_param_spec_object(
		"conversation-manager", NULL, NULL,
		PURPLE_TYPE_CONVERSATION_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:credential-manager:
	 *
	 * The credential manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_CREDENTIAL_MANAGER] = g_param_spec_object(
		"credential-manager", NULL, NULL,
		PURPLE_TYPE_CREDENTIAL_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:file-transfer-manager:
	 *
	 * The file transfer manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_FILE_TRANSFER_MANAGER] = g_param_spec_object(
		"file-transfer-manager", NULL, NULL,
		PURPLE_TYPE_FILE_TRANSFER_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:idle-manager:
	 *
	 * The idle manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_IDLE_MANAGER] = g_param_spec_object(
		"idle-manager", NULL, NULL,
		PURPLE_TYPE_IDLE_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:notification-manager:
	 *
	 * The notification manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_NOTIFICATION_MANAGER] = g_param_spec_object(
		"notification-manager", NULL, NULL,
		PURPLE_TYPE_NOTIFICATION_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:presence-manager:
	 *
	 * The presence manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_PRESENCE_MANAGER] = g_param_spec_object(
		"presence-manager", NULL, NULL,
		PURPLE_TYPE_PRESENCE_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:protocol-manager:
	 *
	 * The protocol manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_PROTOCOL_MANAGER] = g_param_spec_object(
		"protocol-manager", NULL, NULL,
		PURPLE_TYPE_PROTOCOL_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:scheduler:
	 *
	 * The scheduler the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_SCHEDULER] = g_param_spec_object(
		"scheduler", NULL, NULL,
		PURPLE_TYPE_SCHEDULER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:settings-backend:
	 *
	 * The settings backend that should be used.
	 *
	 * At startup a copy of [vfunc@Ui.get_settings_backend] is stored here.
	 *
	 * Since: 3.0
	 */
	properties[PROP_SETTINGS_BACKEND] = g_param_spec_object(
		"settings-backend", NULL, NULL,
		G_TYPE_SETTINGS_BACKEND,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:ui:
	 *
	 * The user interface being used.
	 *
	 * Since: 3.0
	 */
	properties[PROP_UI] = g_param_spec_object(
		"ui", NULL, NULL,
		PURPLE_TYPE_UI,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	/**
	 * PurpleCore:whiteboard-manager:
	 *
	 * The whiteboard manager the core is using.
	 *
	 * Since: 3.0
	 */
	properties[PROP_WHITEBOARD_MANAGER] = g_param_spec_object(
		"whiteboard-manager", NULL, NULL,
		PURPLE_TYPE_WHITEBOARD_MANAGER,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Callbacks
 *****************************************************************************/
static void
purple_core_presence_manager_loaded_from_backend_cb(G_GNUC_UNUSED PurpleAccountManager *account_manager,
                                                    G_GNUC_UNUSED GError *error,
                                                    gpointer data)
{
	PurpleCore *core = data;

	purple_account_manager_load_from_backend(core->account_manager);
}

static void
purple_core_account_manager_loaded_from_backend_cb(G_GNUC_UNUSED PurpleAccountManager *account_manager,
                                                   G_GNUC_UNUSED GError *error,
                                                   gpointer data)
{
	PurpleCore *core = data;

	purple_contact_manager_load_from_backend(core->contact_manager);
}

static void
purple_core_contact_manager_loaded_from_backend_cb(G_GNUC_UNUSED PurpleAccountManager *account_manager,
                                                   G_GNUC_UNUSED GError *error,
                                                   gpointer data)
{
	PurpleCore *core = data;

	purple_conversation_manager_load_from_backend(core->conversation_manager);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
void
purple_core_set_default(PurpleCore *core) {
	g_return_if_fail(default_core == NULL);

	default_core = core;
	g_object_add_weak_pointer(G_OBJECT(default_core),
                          (gpointer *)&default_core);
}

PurpleAccountManager *
purple_core_get_account_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->account_manager;
}

PurpleBadgeManager *
purple_core_get_badge_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->badge_manager;
}

PurpleCommandManager *
purple_core_get_command_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->command_manager;
}

PurpleContactManager *
purple_core_get_contact_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->contact_manager;
}

PurpleConversationManager *
purple_core_get_conversation_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->conversation_manager;
}

PurpleCredentialManager *
purple_core_get_credential_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->credential_manager;
}

PurpleCore *
purple_core_get_default(void) {
	return default_core;
}

PurpleFileTransferManager *
purple_core_get_file_transfer_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->file_transfer_manager;
}

PurpleIdleManager *
purple_core_get_idle_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->idle_manager;
}

PurpleNotificationManager *
purple_core_get_notification_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->notification_manager;
}

PurplePresenceManager *
purple_core_get_presence_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->presence_manager;
}

PurpleProtocolManager *
purple_core_get_protocol_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->protocol_manager;
}

PurpleScheduler *
purple_core_get_scheduler(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->scheduler;
}

PurpleWhiteboardManager *
purple_core_get_whiteboard_manager(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->whiteboard_manager;
}

PurpleCore *
purple_core_new(PurpleUi *ui, GError **error) {
	g_return_val_if_fail(PURPLE_IS_UI(ui), NULL);

	return g_initable_new(
		PURPLE_TYPE_CORE,
		NULL,
		error,
		"ui", ui,
		NULL);
}

gboolean
purple_core_start(PurpleCore *core, GError **error) {
	GError *local_error = NULL;
	GNetworkMonitor *network_monitor = NULL;
	const char *force_error_message = NULL;

	g_return_val_if_fail(PURPLE_IS_CORE(core), FALSE);

	bindtextdomain(GETTEXT_PACKAGE, purple_get_locale_dir());

	/* Set this environment variable to anything to test the error reporting in
	 * the user interface.
	 */
	force_error_message = g_getenv("PURPLE3_CORE_ERROR_MESSAGE");
	if(force_error_message != NULL) {
		if(birb_str_is_empty(force_error_message)) {
			force_error_message = "This is a forced error for testing user "
			                      "interfaces.";
		}

		g_set_error_literal(error, g_quark_from_static_string("purple"), 0,
		                    force_error_message);

		return FALSE;
	}

	core->settings_backend = purple_ui_get_settings_backend(core->ui);
	if(!G_IS_SETTINGS_BACKEND(core->settings_backend)) {
		g_set_error(error,
		            PURPLE_CORE_ERROR,
		            PURPLE_CORE_ERROR_NO_SETTINGS_BACKEND,
		            _("the user interface did not provide a settings "
		              "backend"));

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_SETTINGS_BACKEND]);

#ifdef _WIN32
	wpurple_init();
#endif

	purple_util_init();

	/* Create the scheduler. */
	core->scheduler = purple_scheduler_startup(core, &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core), properties[PROP_SCHEDULER]);

	/* Create the idle manager. */
	core->idle_manager = purple_idle_manager_startup(core, &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_IDLE_MANAGER]);

	/* Create the file transfer manager. */
	core->file_transfer_manager = purple_file_transfer_manager_startup(core,
	                                                                   &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_FILE_TRANSFER_MANAGER]);

	/* Create the notification manager. */
	core->notification_manager = purple_notification_manager_startup(core,
	                                                                 &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_NOTIFICATION_MANAGER]);

	/* Create the protocol manager. */
	core->protocol_manager = purple_protocol_manager_startup(core,
	                                                         &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_PROTOCOL_MANAGER]);

	/* Start the credential manager before accounts. */
	core->credential_manager = purple_credential_manager_startup(core,
	                                                             &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_CREDENTIAL_MANAGER]);

	/* Create the default badge manager before plugins as they will add badges.
	 */
	core->badge_manager = purple_badge_manager_startup(core, &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_BADGE_MANAGER]);

	/* Create the default command manager before plugins as they will add
	 * commands.
	 */
	core->command_manager = purple_command_manager_startup(core, &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_COMMAND_MANAGER]);

	/* Since plugins get probed so early we should probably initialize their
	 * subsystem right away too.
	 */
	purple_plugins_init();

	/* Create the default presence manager. */
	core->presence_manager = purple_presence_manager_startup(core,
	                                                         &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_PRESENCE_MANAGER]);

	/* Create the default account manager. */
	core->account_manager = purple_account_manager_startup(core, &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_ACCOUNT_MANAGER]);
	g_signal_connect_object(core->presence_manager, "loaded-from-backend",
	                        G_CALLBACK(purple_core_presence_manager_loaded_from_backend_cb),
	                        core, G_CONNECT_DEFAULT);

	/* Connect to the AccountManager::loaded-from-backend signal so we can load
	 * the contact manager which needs to have accounts loaded first.
	 */
	core->contact_manager = purple_contact_manager_startup(core, &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_CONTACT_MANAGER]);
	g_signal_connect_object(core->account_manager, "loaded-from-backend",
	                        G_CALLBACK(purple_core_account_manager_loaded_from_backend_cb),
	                        core, G_CONNECT_DEFAULT);

	/* Connect to the ContactManager::loaded-from-backend signal so we can load
	 * conversations as we need accounts and contacts for that.
	 */
	core->conversation_manager = purple_conversation_manager_startup(core,
	                                                                 &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_CONVERSATION_MANAGER]);
	g_signal_connect_object(core->contact_manager, "loaded-from-backend",
	                        G_CALLBACK(purple_core_contact_manager_loaded_from_backend_cb),
	                        core, G_CONNECT_DEFAULT);

	purple_presence_manager_load_from_backend(core->presence_manager);

	/* Create the default whiteboard manager. */
	core->whiteboard_manager =  purple_whiteboard_manager_startup(core,
	                                                              &local_error);
	if(local_error != NULL) {
		g_propagate_error(error, local_error);

		return FALSE;
	}
	g_object_notify_by_pspec(G_OBJECT(core),
	                         properties[PROP_WHITEBOARD_MANAGER]);

	/* Finally tell the ui to start. */
	if(!purple_ui_start(core->ui, error)) {
		return FALSE;
	}

	network_monitor = g_network_monitor_get_default();
	purple_account_manager_set_online(core->account_manager,
	                                  g_network_monitor_get_network_available(network_monitor));

	return TRUE;
}

void
purple_core_quit(PurpleCore *core) {
	GError *error = NULL;

	g_return_if_fail(PURPLE_IS_CORE(core));

	/* Transmission ends */
	purple_account_manager_set_online(core->account_manager, FALSE);

	/* Remove the active provider in the credential manager. */
	purple_credential_manager_set_active(core->credential_manager, NULL, NULL);

	/* Save .xml files, remove signals, etc. */
	g_clear_object(&core->idle_manager);
	g_clear_object(&core->whiteboard_manager);
	g_clear_object(&core->conversation_manager);

	purple_ui_stop(core->ui);

	/* Everything after this must not try to use any settings */
	purple_plugins_uninit();

	/* after plugins */
	g_clear_object(&core->contact_manager);
	g_clear_object(&core->account_manager);
	if(error != NULL) {
		g_warning("error while shutting down the account manager: %s",
		          error->message);

		g_clear_error(&error);
	}

	g_clear_object(&core->presence_manager);

	g_clear_object(&core->credential_manager);
	g_clear_object(&core->protocol_manager);
	g_clear_object(&core->command_manager);
	g_clear_object(&core->badge_manager);

	g_clear_object(&core->notification_manager);
	g_clear_object(&core->file_transfer_manager);
	g_clear_object(&core->scheduler);

	/* Everything after util_uninit cannot try to write things to the
	 * confdir.
	 */
	purple_util_uninit();

	g_clear_object(&core->ui);
	g_clear_object(&core);

#ifdef _WIN32
	wpurple_cleanup();
#endif
}

gboolean
purple_core_get_developer_mode(PurpleCore *core) {
	GSettings *settings = NULL;
	gboolean developer_mode = FALSE;

	g_return_val_if_fail(PURPLE_IS_CORE(core), FALSE);

	settings = purple_core_new_settings(core, "im.pidgin.Purple.Core");
	developer_mode = g_settings_get_boolean(settings, "developer-mode");
	g_clear_object(&settings);

	return developer_mode;
}

gpointer
purple_core_get_settings_backend(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->settings_backend;
}

GSettings *
purple_core_new_settings(PurpleCore *core, const char *schema_id) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);
	g_return_val_if_fail(!birb_str_is_empty(schema_id), NULL);

	return g_settings_new_with_backend(schema_id, core->settings_backend);
}

PurpleUi *
purple_core_get_ui(PurpleCore *core) {
	g_return_val_if_fail(PURPLE_IS_CORE(core), NULL);

	return core->ui;
}

const char *
purple_get_locale_dir(void) {
	const char *locale_dir = NULL;

	locale_dir = g_getenv("PURPLE_LOCALE_DIR");
	if(!birb_str_is_empty(locale_dir)) {
		return locale_dir;
	}

#ifdef _WIN32
	return wpurple_locale_dir();
#else
	return PURPLE_LOCALEDIR;
#endif
}
