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

#include <purple.h>

#include <adwaita.h>

#include "pidginproxyprefs.h"

struct _PidginProxyPrefs {
	AdwPreferencesPage parent;

	/* GNOME version */
	GtkWidget *gnome;
	GtkWidget *gnome_not_found;
	GtkWidget *gnome_program;
	gchar *gnome_program_path;

	/* Non-GNOME version */
	GtkWidget *nongnome;
	GtkWidget *type;
	GtkWidget *options;
	GtkWidget *host;
	GtkWidget *port;
	GtkWidget *username;
	GtkWidget *password;
};

G_DEFINE_FINAL_TYPE(PidginProxyPrefs, pidgin_proxy_prefs,
                    ADW_TYPE_PREFERENCES_PAGE)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static gchar *
proxy_type_expression_cb(GObject *self, G_GNUC_UNUSED gpointer data)
{
	const gchar *text = "";
	const gchar *value = NULL;

	value = gtk_string_object_get_string(GTK_STRING_OBJECT(self));
	if(purple_strequal(value, "none")) {
		text = _("No proxy");
	} else if(purple_strequal(value, "socks4")) {
		text = _("SOCKS 4");
	} else if(purple_strequal(value, "socks5")) {
		text = _("SOCKS 5");
	} else if(purple_strequal(value, "tor")) {
		text = _("Tor/Privacy (SOCKS 5)");
	} else if(purple_strequal(value, "http")) {
		text = _("HTTP");
	} else if(purple_strequal(value, "envvar")) {
		text = _("Use Environmental Settings");
	}

	return g_strdup(text);
}

static void
pidgin_proxy_prefs_type_changed_cb(GSettings *settings, char *key,
                                   gpointer data)
{
	PidginProxyPrefs *prefs = data;
	char *current = g_settings_get_string(settings, key);

	gtk_widget_set_visible(prefs->options,
	                       !purple_strequal(current, "No Proxy") &&
	                       !purple_strequal(current, "Use Environmental Settings"));

	g_free(current);
}

static void
proxy_row_activated_cb(G_GNUC_UNUSED AdwActionRow *row, gpointer data)
{
	PidginProxyPrefs *prefs = data;
	GError *err = NULL;

	if (g_spawn_command_line_async(prefs->gnome_program_path, &err)) {
		return;
	}

	g_warning(_("Cannot start proxy configuration program: %s"), err->message);

	g_error_free(err);
}

static gboolean
pidgin_proxy_prefs_get_type_mapping(GValue *value, GVariant *variant,
                                    G_GNUC_UNUSED gpointer data)
{
	const char *current = g_variant_get_string(variant, NULL);
	guint position = 0;

	/* The values for position are dependent on the order of the GtkStringList
	 * in prefs/proxy.ui.
	 */
	if(purple_strequal(current, "No Proxy")) {
		position = 0;
	} else if(purple_strequal(current, "SOCKS4")) {
		position = 1;
	} else if(purple_strequal(current, "SOCKS5")) {
		position = 2;
	} else if(purple_strequal(current, "TOR")) {
		position = 3;
	} else if(purple_strequal(current, "HTTP")) {
		position = 4;
	} else if(purple_strequal(current, "Use Environmental Settings")) {
		position = 5;
	} else {
		return FALSE;
	}

	g_value_set_uint(value, position);

	return TRUE;
}

static GVariant *
pidgin_proxy_prefs_set_type_mapping(const GValue *gvalue,
                                    G_GNUC_UNUSED const GVariantType *expected_type,
                                    G_GNUC_UNUSED gpointer data)
{
	guint position = g_value_get_uint(gvalue);

	/* The index of these items is dependent on the order of the GtkStringList
	 * in prefs/proxy.ui.
	 */
	const char *map[] = {
		"No Proxy", "SOCKS4", "SOCKS5", "TOR", "HTTP",
		"Use Environmental Settings",
	};

	if(position >= G_N_ELEMENTS(map)) {
		return NULL;
	}

	return g_variant_new_string(map[position]);
}

static gboolean
pidgin_proxy_prefs_get_port_mapping(GValue *value, GVariant *variant,
                                    G_GNUC_UNUSED gpointer data)
{
	g_value_take_string(value,
	                    g_strdup_printf("%d", g_variant_get_uint16(variant)));

	return TRUE;
}

static GVariant *
pidgin_proxy_prefs_set_port_mapping(const GValue *value,
                                    G_GNUC_UNUSED const GVariantType *expected_type,
                                    G_GNUC_UNUSED gpointer data)
{
	const char *current = g_value_get_string(value);

	if(current != NULL) {
		return g_variant_new_uint16(atoi(current) & 0xFFFF);
	}

	return NULL;
}

static void
pidgin_proxy_prefs_init_gnome(PidginProxyPrefs *prefs) {
	gchar *path = NULL;

	gtk_widget_set_visible(prefs->gnome, TRUE);
	gtk_widget_set_visible(prefs->nongnome, FALSE);

	path = g_find_program_in_path("gnome-network-properties");
	if (path == NULL) {
		path = g_find_program_in_path("gnome-network-preferences");
	}
	if (path == NULL) {
		path = g_find_program_in_path("gnome-control-center");
		if (path != NULL) {
			char *tmp = g_strdup_printf("%s network", path);
			g_free(path);
			path = tmp;
		}
	}

	prefs->gnome_program_path = path;
	gtk_widget_set_visible(prefs->gnome_not_found, path == NULL);
	gtk_widget_set_visible(prefs->gnome_program, path != NULL);
}

static void
pidgin_proxy_prefs_init_non_gnome(PidginProxyPrefs *prefs) {
	GSettings *settings = NULL;
	gpointer settings_backend = NULL;

	settings_backend = purple_core_get_settings_backend();
	settings = g_settings_new_with_backend("im.pidgin.Purple.Proxy",
	                                       settings_backend);

	gtk_widget_set_visible(prefs->gnome, FALSE);
	gtk_widget_set_visible(prefs->nongnome, TRUE);

	g_settings_bind_with_mapping(settings, "type",
	                             prefs->type, "selected",
	                             G_SETTINGS_BIND_DEFAULT,
	                             pidgin_proxy_prefs_get_type_mapping,
	                             pidgin_proxy_prefs_set_type_mapping,
	                             NULL,
	                             NULL);

	g_settings_bind(settings, "host", prefs->host, "text",
	                G_SETTINGS_BIND_DEFAULT);
	g_settings_bind_with_mapping(settings, "port",
	                             prefs->port, "text",
	                             G_SETTINGS_BIND_DEFAULT,
	                             pidgin_proxy_prefs_get_port_mapping,
	                             pidgin_proxy_prefs_set_port_mapping,
	                             NULL,
	                             NULL);
	g_settings_bind(settings, "username", prefs->username, "text",
	                G_SETTINGS_BIND_DEFAULT);
	g_settings_bind(settings, "password", prefs->password, "text",
	                G_SETTINGS_BIND_DEFAULT);

	g_signal_connect_object(settings, "changed::type",
	                        G_CALLBACK(pidgin_proxy_prefs_type_changed_cb),
	                        prefs, 0);

	/* Manually call the callback to set the initial visibility. */
	pidgin_proxy_prefs_type_changed_cb(settings, "type", prefs);
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
pidgin_proxy_prefs_finalize(GObject *obj)
{
	PidginProxyPrefs *prefs = PIDGIN_PROXY_PREFS(obj);

	purple_prefs_disconnect_by_handle(obj);

	g_free(prefs->gnome_program_path);

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

static void
pidgin_proxy_prefs_class_init(PidginProxyPrefsClass *klass)
{
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);

	obj_class->finalize = pidgin_proxy_prefs_finalize;

	gtk_widget_class_set_template_from_resource(
		widget_class,
		"/im/pidgin/Pidgin3/Prefs/proxy.ui"
	);

	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, gnome);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, gnome_not_found);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, gnome_program);

	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, nongnome);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, type);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, options);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, host);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, port);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, username);
	gtk_widget_class_bind_template_child(
			widget_class, PidginProxyPrefs, password);
	gtk_widget_class_bind_template_callback(widget_class,
	                                        proxy_type_expression_cb);
	gtk_widget_class_bind_template_callback(widget_class,
	                                        proxy_row_activated_cb);
}

static void
pidgin_proxy_prefs_init(PidginProxyPrefs *prefs)
{

	gtk_widget_init_template(GTK_WIDGET(prefs));

	if(purple_running_gnome()) {
		pidgin_proxy_prefs_init_gnome(prefs);
	} else {
		pidgin_proxy_prefs_init_non_gnome(prefs);
	}
}

/******************************************************************************
 * API
 *****************************************************************************/
GtkWidget *
pidgin_proxy_prefs_new(void) {
	return g_object_new(PIDGIN_TYPE_PROXY_PREFS, NULL);
}
