/*
 * This file is part of gedit
 *
 * Copyright (C) 2001-2005 Paolo Maggi
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

#include "gedit-preferences-dialog.h"
#include <glib/gi18n.h>
#include <libpeas-gtk/peas-gtk.h>
#include "gedit-settings.h"

struct _GeditPreferencesDialogPrivate
{
	/* Text Wrapping */
	GtkToggleButton *wrap_text_checkbutton;
	GtkToggleButton *split_checkbutton;
};

#define PAGE_MARGINS (12)

G_DEFINE_TYPE_WITH_PRIVATE (GeditPreferencesDialog, gedit_preferences_dialog, TEPL_TYPE_PREFS_DIALOG)

/* Prototypes */
static void wrap_mode_setting_changed_cb (GSettings              *settings,
					  const gchar            *key,
					  GeditPreferencesDialog *dialog);

/* This resets *also* the hidden GSettings keys (not exposed in the prefs
 * dialog, but available in dconf-editor for example).
 * Rationale: IF a hidden gsetting was modified, but then much later on the user
 * doesn't understand why gedit behaves "strangely" and forgot about the hidden
 * gsettings, THEN the Reset All button is a good solution for the user. (It's
 * like a "Soft Factory Reset").
 */
static void
reset_all_preferences (void)
{
	GeditSettings *settings;
	GSettings *ui_settings;

	tepl_settings_reset_all ("org.gnome.gedit.preferences.editor");

	settings = _gedit_settings_get_singleton ();

	/* Not all of ui_settings, some of its keys would be better located in
	 * the "state" sub-schema.
	 */
	ui_settings = _gedit_settings_peek_ui_settings (settings);
	g_settings_reset (ui_settings, GEDIT_SETTINGS_SHOW_TABS_MODE);
	g_settings_reset (ui_settings, GEDIT_SETTINGS_STATUSBAR_VISIBLE);
	g_settings_reset (ui_settings, GEDIT_SETTINGS_THEME_VARIANT);
}

static void
gedit_preferences_dialog_class_init (GeditPreferencesDialogClass *klass)
{
}

static GtkGrid *
create_simple_grid_for_component (void)
{
	GtkGrid *component;

	component = GTK_GRID (gtk_grid_new ());
	gtk_orientable_set_orientation (GTK_ORIENTABLE (component), GTK_ORIENTATION_VERTICAL);

	/* To space out individual config widgets */
	gtk_grid_set_row_spacing (component, 6);

	return component;
}

static GtkGrid *
create_simple_grid_for_page (void)
{
	GtkGrid *page_vgrid;

	page_vgrid = GTK_GRID (gtk_grid_new ());
	gtk_orientable_set_orientation (GTK_ORIENTABLE (page_vgrid), GTK_ORIENTATION_VERTICAL);

	/* To space out components */
	gtk_grid_set_row_spacing (page_vgrid, 18);

	g_object_set (page_vgrid,
		      "margin", PAGE_MARGINS,
		      NULL);

	return page_vgrid;
}

static GtkWidget *
create_tab_stops_component (GSettings *editor_settings)
{
	GtkGrid *component;

	component = create_simple_grid_for_component ();

	gtk_container_add (GTK_CONTAINER (component),
			   tepl_prefs_create_tab_width_spinbutton (editor_settings,
								   GEDIT_SETTINGS_TABS_SIZE));
	gtk_container_add (GTK_CONTAINER (component),
			   tepl_prefs_create_checkbutton_simple (editor_settings,
								 GEDIT_SETTINGS_INSERT_SPACES,
								 _("Insert _spaces instead of tabs")));
	gtk_container_add (GTK_CONTAINER (component),
			   tepl_prefs_create_checkbutton_simple (editor_settings,
								 GEDIT_SETTINGS_AUTO_INDENT,
								 _("Automatic _indentation")));

	return tepl_utils_get_titled_component (_("Tab Stops"), GTK_WIDGET (component));
}

static GtkWidget *
create_editor_page (void)
{
	GeditSettings *settings;
	GSettings *editor_settings;
	GtkGrid *editor_page_vgrid;

	settings = _gedit_settings_get_singleton ();
	editor_settings = _gedit_settings_peek_editor_settings (settings);

	editor_page_vgrid = create_simple_grid_for_page ();

	gtk_container_add (GTK_CONTAINER (editor_page_vgrid),
			   create_tab_stops_component (editor_settings));
	gtk_container_add (GTK_CONTAINER (editor_page_vgrid),
			   tepl_prefs_create_files_component (editor_settings,
							      GEDIT_SETTINGS_CREATE_BACKUP_COPY,
							      GEDIT_SETTINGS_AUTO_SAVE,
							      GEDIT_SETTINGS_AUTO_SAVE_INTERVAL));

	return GTK_WIDGET (editor_page_vgrid);
}

static void
wrap_mode_checkbutton_toggled_cb (GtkToggleButton        *button,
				  GeditPreferencesDialog *dialog)
{
	GeditSettings *settings;
	GSettings *editor_settings;
	GtkWrapMode mode;

	settings = _gedit_settings_get_singleton ();
	editor_settings = _gedit_settings_peek_editor_settings (settings);

	g_signal_handlers_block_by_func (editor_settings,
					 wrap_mode_setting_changed_cb,
					 dialog);

	if (!gtk_toggle_button_get_active (dialog->priv->wrap_text_checkbutton))
	{
		mode = GTK_WRAP_NONE;
	}
	else
	{
		if (gtk_toggle_button_get_active (dialog->priv->split_checkbutton))
		{
			mode = GTK_WRAP_WORD;
		}
		else
		{
			mode = GTK_WRAP_CHAR;
		}

		g_settings_set_enum (editor_settings,
				     GEDIT_SETTINGS_WRAP_LAST_SPLIT_MODE,
				     mode);
	}

	g_settings_set_enum (editor_settings,
			     GEDIT_SETTINGS_WRAP_MODE,
			     mode);

	g_signal_handlers_unblock_by_func (editor_settings,
					   wrap_mode_setting_changed_cb,
					   dialog);
}

static void
update_text_wrapping_state (GeditPreferencesDialog *dialog)
{
	GeditSettings *settings;
	GSettings *editor_settings;
	GtkWrapMode wrap_mode;
	GtkWrapMode last_split_mode;

	settings = _gedit_settings_get_singleton ();
	editor_settings = _gedit_settings_peek_editor_settings (settings);

	g_signal_handlers_block_by_func (dialog->priv->wrap_text_checkbutton,
					 wrap_mode_checkbutton_toggled_cb,
					 dialog);
	g_signal_handlers_block_by_func (dialog->priv->split_checkbutton,
					 wrap_mode_checkbutton_toggled_cb,
					 dialog);
	g_signal_handlers_block_by_func (editor_settings,
					 wrap_mode_setting_changed_cb,
					 dialog);

	wrap_mode = g_settings_get_enum (editor_settings, GEDIT_SETTINGS_WRAP_MODE);

	switch (wrap_mode)
	{
		case GTK_WRAP_WORD:
			gtk_toggle_button_set_active (dialog->priv->wrap_text_checkbutton, TRUE);
			gtk_toggle_button_set_active (dialog->priv->split_checkbutton, TRUE);

			g_settings_set_enum (editor_settings,
					     GEDIT_SETTINGS_WRAP_LAST_SPLIT_MODE,
					     GTK_WRAP_WORD);
			break;

		case GTK_WRAP_CHAR:
			gtk_toggle_button_set_active (dialog->priv->wrap_text_checkbutton, TRUE);
			gtk_toggle_button_set_active (dialog->priv->split_checkbutton, FALSE);

			g_settings_set_enum (editor_settings,
					     GEDIT_SETTINGS_WRAP_LAST_SPLIT_MODE,
					     GTK_WRAP_CHAR);
			break;

		default:
			last_split_mode = g_settings_get_enum (editor_settings,
							       GEDIT_SETTINGS_WRAP_LAST_SPLIT_MODE);

			gtk_toggle_button_set_active (dialog->priv->wrap_text_checkbutton, FALSE);
			gtk_toggle_button_set_active (dialog->priv->split_checkbutton,
						      last_split_mode == GTK_WRAP_WORD);
			break;
	}

	g_signal_handlers_unblock_by_func (dialog->priv->wrap_text_checkbutton,
					   wrap_mode_checkbutton_toggled_cb,
					   dialog);
	g_signal_handlers_unblock_by_func (dialog->priv->split_checkbutton,
					   wrap_mode_checkbutton_toggled_cb,
					   dialog);
	g_signal_handlers_unblock_by_func (editor_settings,
					   wrap_mode_setting_changed_cb,
					   dialog);
}

static void
wrap_mode_setting_changed_cb (GSettings              *settings,
			      const gchar            *key,
			      GeditPreferencesDialog *dialog)
{
	update_text_wrapping_state (dialog);
}

static GtkWidget *
create_text_wrapping_component (GeditPreferencesDialog *dialog,
				GSettings              *editor_settings)
{
	GtkGrid *component;

	g_assert (dialog->priv->wrap_text_checkbutton == NULL);
	g_assert (dialog->priv->split_checkbutton == NULL);

	dialog->priv->wrap_text_checkbutton =
		GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Enable text _wrapping")));
	dialog->priv->split_checkbutton =
		GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Do not _split words over two lines")));

	component = create_simple_grid_for_component ();
	gtk_container_add (GTK_CONTAINER (component), GTK_WIDGET (dialog->priv->wrap_text_checkbutton));
	gtk_container_add (GTK_CONTAINER (component), GTK_WIDGET (dialog->priv->split_checkbutton));

	/* FIXME: This is a bit convoluted code. Perhaps rework it by changing
	 * the gschema, to have keys that can be directly bound with
	 * g_settings_bind().
	 */

	g_signal_connect_object (dialog->priv->wrap_text_checkbutton,
				 "toggled",
				 G_CALLBACK (wrap_mode_checkbutton_toggled_cb),
				 dialog,
				 G_CONNECT_DEFAULT);

	g_signal_connect_object (dialog->priv->split_checkbutton,
				 "toggled",
				 G_CALLBACK (wrap_mode_checkbutton_toggled_cb),
				 dialog,
				 G_CONNECT_DEFAULT);

	g_signal_connect_object (editor_settings,
				 "changed::" GEDIT_SETTINGS_WRAP_MODE,
				 G_CALLBACK (wrap_mode_setting_changed_cb),
				 dialog,
				 G_CONNECT_DEFAULT);

	g_signal_connect_object (editor_settings,
				 "changed::" GEDIT_SETTINGS_WRAP_LAST_SPLIT_MODE,
				 G_CALLBACK (wrap_mode_setting_changed_cb),
				 dialog,
				 G_CONNECT_DEFAULT);

	g_object_bind_property (dialog->priv->wrap_text_checkbutton, "active",
				dialog->priv->split_checkbutton, "sensitive",
				G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);

	g_object_bind_property (dialog->priv->wrap_text_checkbutton, "active",
				dialog->priv->split_checkbutton, "inconsistent",
				G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);

	update_text_wrapping_state (dialog);

	return tepl_utils_get_titled_component (_("Text Wrapping"), GTK_WIDGET (component));
}

static GtkWidget *
create_view_page (GeditPreferencesDialog *dialog)
{
	GeditSettings *settings;
	GSettings *ui_settings;
	GSettings *editor_settings;
	GtkGrid *view_page_vgrid;
	GtkGrid *misc_items_vgrid;

	settings = _gedit_settings_get_singleton ();
	ui_settings = _gedit_settings_peek_ui_settings (settings);
	editor_settings = _gedit_settings_peek_editor_settings (settings);

	view_page_vgrid = create_simple_grid_for_page ();

	/* Misc items */
	misc_items_vgrid = create_simple_grid_for_component ();
	gtk_container_add (GTK_CONTAINER (misc_items_vgrid),
			   tepl_prefs_create_display_line_numbers_checkbutton (editor_settings,
									       GEDIT_SETTINGS_DISPLAY_LINE_NUMBERS));
	gtk_container_add (GTK_CONTAINER (misc_items_vgrid),
			   tepl_prefs_create_right_margin_component (editor_settings,
								     GEDIT_SETTINGS_DISPLAY_RIGHT_MARGIN,
								     GEDIT_SETTINGS_RIGHT_MARGIN_POSITION));
	gtk_container_add (GTK_CONTAINER (misc_items_vgrid),
			   tepl_prefs_create_display_statusbar_checkbutton (ui_settings,
									    GEDIT_SETTINGS_STATUSBAR_VISIBLE));

	/* Pack the components */
	gtk_container_add (GTK_CONTAINER (view_page_vgrid),
			   GTK_WIDGET (misc_items_vgrid));
	gtk_container_add (GTK_CONTAINER (view_page_vgrid),
			   create_text_wrapping_component (dialog, editor_settings));
	gtk_container_add (GTK_CONTAINER (view_page_vgrid),
			   tepl_prefs_create_highlighting_component (editor_settings,
								     GEDIT_SETTINGS_HIGHLIGHT_CURRENT_LINE,
								     GEDIT_SETTINGS_BRACKET_MATCHING));

	return GTK_WIDGET (view_page_vgrid);
}

static GtkWidget *
create_font_and_colors_page (void)
{
	GeditSettings *settings;
	GSettings *editor_settings;
	GSettings *ui_settings;
	GtkGrid *page_vgrid;

	settings = _gedit_settings_get_singleton ();
	editor_settings = _gedit_settings_peek_editor_settings (settings);
	ui_settings = _gedit_settings_peek_ui_settings (settings);

	page_vgrid = create_simple_grid_for_page ();

	gtk_container_add (GTK_CONTAINER (page_vgrid),
			   tepl_prefs_create_font_component (editor_settings,
							     GEDIT_SETTINGS_USE_DEFAULT_FONT,
							     GEDIT_SETTINGS_EDITOR_FONT));
	gtk_container_add (GTK_CONTAINER (page_vgrid),
			   tepl_prefs_create_theme_variant_combo_box (ui_settings,
								      GEDIT_SETTINGS_THEME_VARIANT));
	gtk_container_add (GTK_CONTAINER (page_vgrid),
			   GTK_WIDGET (tepl_style_scheme_chooser_full_new ()));

	return GTK_WIDGET (page_vgrid);
}

static GtkWidget *
create_plugins_page (void)
{
	GtkWidget *plugin_manager;

	plugin_manager = peas_gtk_plugin_manager_new (NULL);
	gtk_widget_set_hexpand (plugin_manager, TRUE);
	gtk_widget_set_vexpand (plugin_manager, TRUE);
	g_object_set (plugin_manager,
		      "margin", PAGE_MARGINS,
		      NULL);

	return plugin_manager;
}

static GtkNotebook *
create_notebook (GeditPreferencesDialog *dialog)
{
	GtkNotebook *notebook;

	notebook = GTK_NOTEBOOK (gtk_notebook_new ());
	gtk_notebook_set_scrollable (notebook, TRUE);
	gtk_notebook_set_show_border (notebook, FALSE);

	gtk_notebook_append_page (notebook,
				  create_view_page (dialog),
				  gtk_label_new (_("View")));

	gtk_notebook_append_page (notebook,
				  create_editor_page (),
				  gtk_label_new (_("Editor")));

	gtk_notebook_append_page (notebook,
				  create_font_and_colors_page (),
				  gtk_label_new (_("Font & Colors")));

	gtk_notebook_append_page (notebook,
				  create_plugins_page (),
				  gtk_label_new (_("Plugins")));

	gtk_widget_show_all (GTK_WIDGET (notebook));
	gtk_notebook_set_current_page (notebook, 0);
	return notebook;
}

static void
reset_confirm_dialog_response_cb (GtkDialog              *confirm_dialog,
				  gint                    response_id,
				  GeditPreferencesDialog *dialog)
{
	if (response_id == GTK_RESPONSE_YES)
	{
		reset_all_preferences ();
	}

	gtk_widget_destroy (GTK_WIDGET (confirm_dialog));
}

static void
reset_button_clicked_cb (GtkButton              *reset_button,
			 GeditPreferencesDialog *dialog)
{
	GtkWidget *confirm_dialog;

	confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
						 GTK_DIALOG_MODAL |
						 GTK_DIALOG_DESTROY_WITH_PARENT,
						 GTK_MESSAGE_QUESTION,
						 GTK_BUTTONS_NONE,
						 _("Do you really want to reset all preferences?"));

	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (confirm_dialog),
						  _("Note that plugins will not be affected."));

	gtk_dialog_add_button (GTK_DIALOG (confirm_dialog),
			       _("_Cancel"),
			       GTK_RESPONSE_CANCEL);

	gtk_dialog_add_button (GTK_DIALOG (confirm_dialog),
			       _("_Reset All"),
			       GTK_RESPONSE_YES);

	g_signal_connect_object (confirm_dialog,
				 "response",
				 G_CALLBACK (reset_confirm_dialog_response_cb),
				 dialog,
				 G_CONNECT_DEFAULT);

	gtk_widget_show_all (confirm_dialog);
}

static void
add_reset_all_button (GeditPreferencesDialog *dialog)
{
	GtkWidget *header_bar;
	GtkWidget *reset_button;

	header_bar = gtk_dialog_get_header_bar (GTK_DIALOG (dialog));
	g_return_if_fail (header_bar != NULL);

	reset_button = gtk_button_new_with_mnemonic (_("_Reset All…"));
	gtk_widget_set_tooltip_text (reset_button, _("Reset all preferences"));
	gtk_widget_show (reset_button);

	gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), reset_button);

	g_signal_connect_object (reset_button,
				 "clicked",
				 G_CALLBACK (reset_button_clicked_cb),
				 dialog,
				 G_CONNECT_DEFAULT);
}

static void
gedit_preferences_dialog_init (GeditPreferencesDialog *dialog)
{
	GtkBox *content_area;

	dialog->priv = gedit_preferences_dialog_get_instance_private (dialog);

	content_area = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
	gtk_box_pack_start (content_area,
			    GTK_WIDGET (create_notebook (dialog)),
			    TRUE, TRUE, 0);

	add_reset_all_button (dialog);
}

/* Returns: (transfer floating) */
TeplPrefsDialog *
gedit_preferences_dialog_new (void)
{
	return g_object_new (GEDIT_TYPE_PREFERENCES_DIALOG,
			     "use-header-bar", TRUE,
			     NULL);
}
