/*
 * Pidgin - Internet Messenger
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * Pidgin 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 General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib/gi18n.h>

#include <purple.h>

#include "pidginaccountmanager.h"

#include "pidgincore.h"
#include "pidginaccounteditor.h"
#include "pidginaccountmanagerrow.h"

struct _PidginAccountManager {
	AdwWindow parent;

	AdwNavigationView *view;

	struct {
		GtkListBox *list_box;
		GtkActionBar *action_bar;
	} overview;

	struct {
		PidginAccountEditor *editor;
		GtkButton *add;
		GtkWidget *remove;
	} editor;

	/* This is used to not go back to the manager when an account was edited
	 * directly, via the `accounts->(account)->edit` menu or the
	 * `connection error` notification.
	 */
	gboolean edit_only;
};

G_DEFINE_FINAL_TYPE(PidginAccountManager, pidgin_account_manager,
                    ADW_TYPE_WINDOW)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static GtkWidget *
pidgin_account_manager_create_widget(gpointer item,
                                     G_GNUC_UNUSED gpointer data)
{
	if(!PURPLE_IS_ACCOUNT(item)) {
		return NULL;
	}

	return pidgin_account_manager_row_new(PURPLE_ACCOUNT(item));
}

static void
pidgin_account_manager_real_edit_account(PidginAccountManager *manager,
                                         PurpleAccount *account,
                                         gboolean edit_only)
{
	g_return_if_fail(PIDGIN_IS_ACCOUNT_MANAGER(manager));

	manager->edit_only = edit_only;

	pidgin_account_editor_set_account(manager->editor.editor, account);

	/* Scroll the editor back to the top of the scrolled window. */
	adw_preferences_page_scroll_to_top(ADW_PREFERENCES_PAGE(manager->editor.editor));

	if(PURPLE_IS_ACCOUNT(account)) {
		gtk_button_set_label(manager->editor.add, _("_Save"));
		gtk_widget_set_visible(manager->editor.remove, TRUE);
	} else {
		gtk_button_set_label(manager->editor.add, _("_Add"));
		gtk_widget_set_visible(manager->editor.remove, FALSE);
	}

	adw_navigation_view_push_by_tag(manager->view, "editor");
}

/******************************************************************************
 * Callbacks
 *****************************************************************************/
/* This is used by the add button on the placeholder page. */
static void
pidgin_account_manager_create_account(G_GNUC_UNUSED GtkButton *self,
                                      gpointer data)
{
	PidginAccountManager *manager = data;

	pidgin_account_manager_real_edit_account(manager, NULL, FALSE);
}

static void
pidgin_account_manager_refresh_add_cb(GListModel *list,
                                      G_GNUC_UNUSED guint position,
                                      G_GNUC_UNUSED guint removed,
                                      G_GNUC_UNUSED guint added,
                                      gpointer data)
{
	PidginAccountManager *manager = data;

	/* If there are no accounts, the placeholder is shown, which includes an
	 * Add button. So hide the button box if that's the case. */
	gtk_action_bar_set_revealed(manager->overview.action_bar,
	                            g_list_model_get_n_items(list) != 0);
}

static void
pidgin_account_manager_row_activated_cb(G_GNUC_UNUSED GtkListBox *box,
                                        GtkListBoxRow *row,
                                        gpointer data)
{
	PurpleAccount *account = NULL;
	PidginAccountManager *manager = data;
	PidginAccountManagerRow *manager_row = NULL;

	manager_row = PIDGIN_ACCOUNT_MANAGER_ROW(row);
	account = pidgin_account_manager_row_get_account(manager_row);

	pidgin_account_manager_real_edit_account(manager, account, FALSE);
}

static void
pidgin_account_manager_remove_account_cb(G_GNUC_UNUSED AdwMessageDialog *self,
                                         char *response, gpointer data)
{
	PidginAccountManager *account_manager = data;
	PurpleAccount *account = NULL;
	PurpleNotificationManager *notification_manager = NULL;

	if(!purple_strequal(response, "remove")) {
		return;
	}

	account = pidgin_account_editor_get_account(account_manager->editor.editor);

	/* Remove all notifications including connection errors for the account. */
	notification_manager = purple_notification_manager_get_default();
	purple_notification_manager_remove_with_account(notification_manager,
	                                                account, TRUE);

	/* Delete the account. */
	purple_accounts_delete(account);

	pidgin_account_manager_show_overview(account_manager);
}

static void
pidgin_account_manager_remove_clicked_cb(G_GNUC_UNUSED GtkButton *self,
                                         gpointer data)
{
	PidginAccountManager *manager = data;
	PurpleAccount *account = NULL;
	PurpleProtocol *protocol = NULL;
	AdwAlertDialog *dialog = NULL;
	const char *name = NULL;
	const char *protocol_name = _("Unknown");

	account = pidgin_account_editor_get_account(manager->editor.editor);
	name = purple_account_get_username(account);
	protocol = purple_account_get_protocol(account);
	if(PURPLE_IS_PROTOCOL(protocol)) {
		protocol_name = purple_protocol_get_name(protocol);
	}

	dialog = ADW_ALERT_DIALOG(adw_alert_dialog_new(_("Remove account?"), NULL));
	adw_alert_dialog_format_body(
		dialog,
		_("Do you want to remove the %s (%s) account from Pidgin?"),
		name, protocol_name);
	adw_alert_dialog_add_responses(dialog, "cancel", _("Cancel"),
	                               "remove", _("Remove"), NULL);
	adw_alert_dialog_set_response_appearance(dialog, "remove",
	                                         ADW_RESPONSE_DESTRUCTIVE);
	adw_alert_dialog_set_default_response(dialog, "cancel");
	adw_alert_dialog_set_close_response(dialog, "cancel");

	g_signal_connect(dialog, "response",
	                 G_CALLBACK(pidgin_account_manager_remove_account_cb),
	                 manager);

	adw_dialog_present(ADW_DIALOG(dialog), GTK_WIDGET(manager));
}

static void
pidgin_account_manager_save_clicked_cb(G_GNUC_UNUSED GtkButton *self,
                                       gpointer data)
{
	PidginAccountManager *manager = data;

	pidgin_account_editor_save(manager->editor.editor);

	if(manager->edit_only) {
		gtk_window_destroy(GTK_WINDOW(manager));
	} else {
		pidgin_account_manager_show_overview(manager);
	}
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
pidgin_account_manager_init(PidginAccountManager *manager) {
	GListModel *purple_manager = NULL;

	gtk_widget_init_template(GTK_WIDGET(manager));

	purple_manager = purple_account_manager_get_default_as_model();
	gtk_list_box_bind_model(manager->overview.list_box, purple_manager,
	                        pidgin_account_manager_create_widget, NULL, NULL);
	g_signal_connect_object(purple_manager, "items-changed",
	                        G_CALLBACK(pidgin_account_manager_refresh_add_cb),
	                        manager, 0);
	pidgin_account_manager_refresh_add_cb(purple_manager, 0, 0, 0, manager);
}

static void
pidgin_account_manager_class_init(PidginAccountManagerClass *klass) {
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);

	gtk_widget_class_set_template_from_resource(
		widget_class,
		"/im/pidgin/Pidgin3/Accounts/manager.ui"
	);

	gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
	                                     view);

	gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
	                                     overview.list_box);
	gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
	                                     overview.action_bar);

	gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
	                                     editor.editor);
	gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
	                                     editor.add);
	gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
	                                     editor.remove);

	gtk_widget_class_bind_template_callback(widget_class,
	                                        pidgin_account_manager_row_activated_cb);
	gtk_widget_class_bind_template_callback(widget_class,
	                                        pidgin_account_manager_create_account);
	gtk_widget_class_bind_template_callback(widget_class,
	                                        pidgin_account_manager_remove_clicked_cb);
	gtk_widget_class_bind_template_callback(widget_class,
	                                        pidgin_account_manager_save_clicked_cb);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
GtkWidget *
pidgin_account_manager_new(GtkApplication *application) {
	return g_object_new(
		PIDGIN_TYPE_ACCOUNT_MANAGER,
		"application", application,
		NULL);
}

void
pidgin_account_manager_show_overview(PidginAccountManager *manager) {
	g_return_if_fail(PIDGIN_IS_ACCOUNT_MANAGER(manager));

	adw_navigation_view_pop_to_tag(manager->view, "overview");

	pidgin_account_editor_set_account(manager->editor.editor, NULL);
}

void
pidgin_account_manager_edit_account(PidginAccountManager *manager,
                                    PurpleAccount *account)
{
	g_return_if_fail(PIDGIN_IS_ACCOUNT_MANAGER(manager));
	g_return_if_fail(PURPLE_IS_ACCOUNT(account));

	pidgin_account_manager_real_edit_account(manager, account, TRUE);
}
