/*
 * From gedit-utils.c:
 * SPDX-FileCopyrightText: 1998, 1999 - Alex Roberts, Evan Lawrence
 * SPDX-FileCopyrightText: 2000, 2002 - Chema Celorio, Paolo Maggi
 * SPDX-FileCopyrightText: 2003-2005 - Paolo Maggi
 *
 * SPDX-FileCopyrightText: 2014-2022 - Sébastien Wilmet
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

/**
 * SECTION:utils
 * @title: GtexUtils
 * @short_description: Utility functions
 *
 * Various utility functions.
 */

#include "config.h"
#include "gtex-utils.h"
#include <string.h>
#include <tepl/tepl.h>
#include "gtex-synctex.h"

#if HAVE_DCONF_MIGRATION
#include "dh-dconf-migration.h"
#endif

static gboolean
default_document_viewer_is_evince (const gchar *uri)
{
	GFile *file;
	GAppInfo *app_info;
	const gchar *executable;
	gboolean ret;
	GError *error = NULL;

	file = g_file_new_for_uri (uri);
	app_info = g_file_query_default_handler (file, NULL, &error);
	g_object_unref (file);

	if (error != NULL)
	{
		g_warning ("Impossible to know if evince is the default document viewer: %s",
			   error->message);

		g_error_free (error);
		return FALSE;
	}

	executable = g_app_info_get_executable (app_info);
	ret = strstr (executable, "evince") != NULL;

	g_object_unref (app_info);
	return ret;
}

/**
 * gtex_utils_show_uri:
 * @widget: (nullable): the associated #GtkWidget, or %NULL.
 * @uri: the URI to show.
 * @timestamp: a timestamp.
 * @error: (out) (optional): a %NULL #GError, or %NULL.
 *
 * Shows the @uri. If the URI is a PDF file and if Evince is the default
 * document viewer, this function also connects the Evince window so the
 * backward search works (switch from the PDF to the source file).
 */
void
gtex_utils_show_uri (GtkWidget    *widget,
		     const gchar  *uri,
		     guint32       timestamp,
		     GError      **error)
{
	GtkWindow *parent = NULL;

	g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
	g_return_if_fail (uri != NULL);
	g_return_if_fail (error == NULL || *error == NULL);

	if (widget != NULL)
	{
		GtkWidget *toplevel;

		toplevel = gtk_widget_get_toplevel (widget);
		if (gtk_widget_is_toplevel (toplevel) &&
		    GTK_IS_WINDOW (toplevel))
		{
			parent = GTK_WINDOW (toplevel);
		}
	}

	if (gtk_show_uri_on_window (parent, uri, timestamp, error))
	{
		gchar *extension = tepl_utils_get_file_extension (uri);

		if (g_strcmp0 (extension, ".pdf") == 0 &&
		    default_document_viewer_is_evince (uri))
		{
			GtexSynctex *synctex = gtex_synctex_get_instance ();
			gtex_synctex_connect_evince_window (synctex, uri);
		}

		g_free (extension);
	}
}

/**
 * gtex_utils_get_dialog_component:
 * @title: the title of the dialog component.
 * @widget: the widget displayed below the title.
 *
 * Like tepl_utils_get_titled_component() but with an additional overall margin
 * on all sides.
 *
 * TODO: migrate everything to tepl_utils_get_titled_component().
 *
 * Returns: (transfer floating): the dialog component containing the @title and
 * the @widget.
 */
GtkWidget *
gtex_utils_get_dialog_component (const gchar *title,
				 GtkWidget   *widget)
{
	GtkWidget *titled_component;

	g_return_val_if_fail (title != NULL, NULL);
	g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

	titled_component = tepl_utils_get_titled_component (title, widget);

	g_object_set (titled_component,
		      "margin", 6,
		      NULL);

	return titled_component;
}

static void
migrate_gsettings (void)
{
#if HAVE_DCONF_MIGRATION
	DhDconfMigration *migration;
	gint i;

	/* In the same order as in the gschema file. */
	const gchar *keys[] =
	{
		"preferences/editor/use-default-font",
		"preferences/editor/editor-font",
		// Not preferences/editor/style-scheme-for-light-theme-variant, key handled below.
		"preferences/editor/create-backup-copy",
		"preferences/editor/auto-save",
		"preferences/editor/auto-save-interval",
		"preferences/editor/reopen-files",
		"preferences/editor/tabs-size",
		"preferences/editor/insert-spaces",
		"preferences/editor/forget-no-tabs",
		"preferences/editor/display-line-numbers",
		"preferences/editor/highlight-current-line",
		"preferences/editor/bracket-matching",
		"preferences/editor/highlight-misspelled-words",
		"preferences/editor/spell-checking-language",

		"preferences/ui/main-toolbar-visible",
		"preferences/ui/edit-toolbar-visible",
		"preferences/ui/side-panel-visible",
		"preferences/ui/bottom-panel-visible",
		"preferences/ui/side-panel-component",
		"preferences/ui/show-build-warnings",
		"preferences/ui/show-build-badboxes",

		"preferences/latex/interactive-completion",
		"preferences/latex/interactive-completion-num",
		"preferences/latex/no-confirm-clean",
		"preferences/latex/automatic-clean",
		"preferences/latex/clean-extensions",
		"preferences/latex/enabled-default-build-tools",
		"preferences/latex/disabled-default-build-tools",

		"preferences/file-browser/current-directory",
		"preferences/file-browser/show-build-files",
		"preferences/file-browser/show-hidden-files",

		"state/window/state",
		"state/window/size",
		"state/window/documents",
		"state/window/side-panel-size",
		"state/window/vertical-paned-position",
		"state/window/structure-paned-position",

		NULL
	};

	migration = _dh_dconf_migration_new ();

	for (i = 0; keys[i] != NULL; i++)
	{
		const gchar *cur_key = keys[i];
		gchar *cur_enter_tex_key;
		gchar *cur_gnome_latex_key;
		gchar *cur_latexila_key;

		cur_enter_tex_key = g_strconcat ("/org/gnome/enter_tex/", cur_key, NULL);
		cur_gnome_latex_key = g_strconcat ("/org/gnome/gnome-latex/", cur_key, NULL);
		cur_latexila_key = g_strconcat ("/org/gnome/latexila/", cur_key, NULL);

		_dh_dconf_migration_migrate_key (migration,
						 cur_enter_tex_key,
						 cur_gnome_latex_key,
						 cur_latexila_key,
						 NULL);

		g_free (cur_enter_tex_key);
		g_free (cur_gnome_latex_key);
		g_free (cur_latexila_key);
	}

	/* Renamed keys */
	_dh_dconf_migration_migrate_key (migration,
					 "/org/gnome/enter_tex/preferences/editor/style-scheme-for-light-theme-variant",
					 "/org/gnome/gnome-latex/preferences/editor/scheme",
					 "/org/gnome/latexila/preferences/editor/scheme",
					 NULL);

	_dh_dconf_migration_sync_and_free (migration);
#else
	g_warning ("Cannot migrate settings from LaTeXila or GNOME LaTeX to Enter TeX: "
		   "dconf migration not supported.");
#endif
}

typedef enum _CopyFileResult CopyFileResult;
enum _CopyFileResult
{
	COPY_FILE_RESULT_SUCCESS,
	COPY_FILE_RESULT_SOURCE_FILE_NOT_FOUND,
	COPY_FILE_RESULT_DESTINATION_FILE_EXISTS,
	COPY_FILE_RESULT_ERROR,
};

static CopyFileResult
copy_file (GFile *source_file,
	   GFile *dest_file)
{
	GError *error = NULL;

	tepl_utils_create_parent_directories (dest_file, NULL, &error);
	if (error != NULL)
	{
		g_message ("Failed to create parent directories: %s", error->message);
		g_error_free (error);
		return COPY_FILE_RESULT_ERROR;
	}

	g_file_copy (source_file,
		     dest_file,
		     G_FILE_COPY_TARGET_DEFAULT_PERMS,
		     NULL, NULL, NULL,
		     &error);

	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
	{
		g_error_free (error);
		return COPY_FILE_RESULT_SOURCE_FILE_NOT_FOUND;
	}
	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
	{
		g_error_free (error);
		return COPY_FILE_RESULT_DESTINATION_FILE_EXISTS;
	}
	if (error != NULL)
	{
		g_message ("Failed to copy file: %s", error->message);
		g_error_free (error);
		return COPY_FILE_RESULT_ERROR;
	}

	return COPY_FILE_RESULT_SUCCESS;
}

static void
migrate_file_by_simple_copy (GFile *latexila_file,
			     GFile *gnome_latex_file,
			     GFile *enter_tex_file)
{
	CopyFileResult result;

	result = copy_file (gnome_latex_file, enter_tex_file);

	if (result == COPY_FILE_RESULT_SOURCE_FILE_NOT_FOUND ||
	    result == COPY_FILE_RESULT_ERROR)
	{
		copy_file (latexila_file, enter_tex_file);
	}
}

static void
migrate_most_used_symbols (void)
{
	GFile *latexila_file;
	GFile *gnome_latex_file;
	GFile *enter_tex_file;

	latexila_file = g_file_new_build_filename (g_get_user_data_dir (),
						   "latexila",
						   "most_used_symbols.xml",
						   NULL);

	gnome_latex_file = g_file_new_build_filename (g_get_user_data_dir (),
						      "gnome-latex",
						      "most_used_symbols.xml",
						      NULL);

	enter_tex_file = g_file_new_build_filename (g_get_user_data_dir (),
						    "enter-tex",
						    "most_used_symbols.xml",
						    NULL);

	migrate_file_by_simple_copy (latexila_file, gnome_latex_file, enter_tex_file);

	g_object_unref (latexila_file);
	g_object_unref (gnome_latex_file);
	g_object_unref (enter_tex_file);
}

static void
migrate_projects (void)
{
	GFile *latexila_file;
	GFile *gnome_latex_file;
	GFile *enter_tex_file;

	latexila_file = g_file_new_build_filename (g_get_user_data_dir (),
						   "latexila",
						   "projects.xml",
						   NULL);

	gnome_latex_file = g_file_new_build_filename (g_get_user_data_dir (),
						      "gnome-latex",
						      "projects.xml",
						      NULL);

	enter_tex_file = g_file_new_build_filename (g_get_user_data_dir (),
						    "enter-tex",
						    "projects.xml",
						    NULL);

	migrate_file_by_simple_copy (latexila_file, gnome_latex_file, enter_tex_file);

	g_object_unref (latexila_file);
	g_object_unref (gnome_latex_file);
	g_object_unref (enter_tex_file);
}

static void
migrate_personal_build_tools (void)
{
	GFile *latexila_file;
	GFile *gnome_latex_file;
	GFile *enter_tex_file;

	latexila_file = g_file_new_build_filename (g_get_user_config_dir (),
						   "latexila",
						   "build_tools.xml",
						   NULL);

	gnome_latex_file = g_file_new_build_filename (g_get_user_config_dir (),
						      "gnome-latex",
						      "build_tools.xml",
						      NULL);

	enter_tex_file = g_file_new_build_filename (g_get_user_config_dir (),
						    "enter-tex",
						    "build_tools.xml",
						    NULL);

	migrate_file_by_simple_copy (latexila_file, gnome_latex_file, enter_tex_file);

	g_object_unref (latexila_file);
	g_object_unref (gnome_latex_file);
	g_object_unref (enter_tex_file);
}

/* Returns true if the migration is successful. */
static gboolean
migrate_personal_templates_tex_files_from_dir (GFile *source_dir)
{
	GFile *enter_tex_dir;
	GFileEnumerator *enumerator;
	GError *error = NULL;
	gboolean success = FALSE;

	enter_tex_dir = g_file_new_build_filename (g_get_user_data_dir (),
						   "enter-tex",
						   NULL);

	enumerator = g_file_enumerate_children (source_dir,
						G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
						G_FILE_QUERY_INFO_NONE,
						NULL,
						&error);

	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
	{
		g_clear_error (&error);
		goto out;
	}

	if (enumerator == NULL || error != NULL)
	{
		goto out;
	}

	while (TRUE)
	{
		GFileInfo *child_file_info;
		GFile *child_file;
		const gchar *child_name;
		GFile *enter_tex_child_file;
		CopyFileResult copy_result;

		g_file_enumerator_iterate (enumerator, &child_file_info, &child_file, NULL, &error);
		if (error != NULL)
		{
			break;
		}
		if (child_file == NULL)
		{
			/* End of iteration */
			success = TRUE;
			break;
		}

		if (!g_file_info_has_attribute (child_file_info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
		{
			continue;
		}

		child_name = g_file_info_get_display_name (child_file_info);
		if (child_name == NULL || !g_str_has_suffix (child_name, ".tex"))
		{
			continue;
		}

		enter_tex_child_file = g_file_get_child (enter_tex_dir, child_name);
		copy_result = copy_file (child_file, enter_tex_child_file);
		g_object_unref (enter_tex_child_file);

		if (copy_result != COPY_FILE_RESULT_SUCCESS)
		{
			break;
		}
	}

out:
	if (error != NULL)
	{
		g_warning ("Error when migrating personal templates: %s",
			   error->message);
		g_clear_error (&error);
	}

	g_object_unref (enter_tex_dir);
	g_clear_object (&enumerator);

	return success;
}

typedef enum
{
	MIGRATE_PERSONAL_TEMPLATES_RESULT_FROM_GNOME_LATEX,
	MIGRATE_PERSONAL_TEMPLATES_RESULT_FROM_LATEXILA,
	MIGRATE_PERSONAL_TEMPLATES_RESULT_NONE,
} MigratePersonalTemplatesResult;

static MigratePersonalTemplatesResult
migrate_personal_templates_tex_files (void)
{
	GFile *gnome_latex_dir;
	GFile *latexila_dir;
	MigratePersonalTemplatesResult result = MIGRATE_PERSONAL_TEMPLATES_RESULT_NONE;

	gnome_latex_dir = g_file_new_build_filename (g_get_user_data_dir (),
						     "gnome-latex",
						     NULL);
	latexila_dir = g_file_new_build_filename (g_get_user_data_dir (),
						  "latexila",
						  NULL);

	if (migrate_personal_templates_tex_files_from_dir (gnome_latex_dir))
	{
		result = MIGRATE_PERSONAL_TEMPLATES_RESULT_FROM_GNOME_LATEX;
	}
	else if (migrate_personal_templates_tex_files_from_dir (latexila_dir))
	{
		result = MIGRATE_PERSONAL_TEMPLATES_RESULT_FROM_LATEXILA;
	}

	g_object_unref (gnome_latex_dir);
	g_object_unref (latexila_dir);

	return result;
}

#define TEMPLATES_RC_FILE_OLD_GROUP_NAME "[LaTeXila]\n"
#define TEMPLATES_RC_FILE_NEW_GROUP_NAME "[Personal templates]\n"

static void
migrate_personal_templates_rc_file_from_latexila (void)
{
	GFile *latexila_file;
	gchar *content = NULL;
	GFile *enter_tex_file = NULL;
	GFileOutputStream *output_stream = NULL;
	GError *error = NULL;

	/* Load old RC file. */
	latexila_file = g_file_new_build_filename (g_get_user_data_dir (),
						   "latexila",
						   "templatesrc",
						   NULL);

	g_file_load_contents (latexila_file, NULL, &content, NULL, NULL, &error);

	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
	{
		g_clear_error (&error);
		goto out;
	}

	if (error != NULL || content == NULL)
	{
		goto out;
	}

	/* Modify group name. */
	if (g_str_has_prefix (content, TEMPLATES_RC_FILE_OLD_GROUP_NAME))
	{
		gchar *modified_content;

		modified_content = g_strconcat (TEMPLATES_RC_FILE_NEW_GROUP_NAME,
						content + strlen (TEMPLATES_RC_FILE_OLD_GROUP_NAME),
						NULL);
		g_free (content);
		content = modified_content;
	}

	/* Save to new location. */
	enter_tex_file = g_file_new_build_filename (g_get_user_data_dir (),
						    "enter-tex",
						    "templatesrc",
						    NULL);

	output_stream = g_file_create (enter_tex_file, G_FILE_CREATE_NONE, NULL, &error);

	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
	{
		g_clear_error (&error);
		goto out;
	}

	if (error != NULL || output_stream == NULL)
	{
		goto out;
	}

	g_output_stream_write_all (G_OUTPUT_STREAM (output_stream),
				   content,
				   strlen (content),
				   NULL,
				   NULL,
				   &error);

out:
	if (error != NULL)
	{
		g_warning ("Error when migrating LaTeXila to Enter TeX personal templates: %s",
			   error->message);
		g_clear_error (&error);
	}

	g_object_unref (latexila_file);
	g_free (content);
	g_clear_object (&enter_tex_file);
	g_clear_object (&output_stream);
}

static void
migrate_personal_templates_rc_file_from_gnome_latex (void)
{
	GFile *gnome_latex_file;
	GFile *enter_tex_file;

	gnome_latex_file = g_file_new_build_filename (g_get_user_data_dir (),
						      "gnome-latex",
						      "templatesrc",
						      NULL);

	enter_tex_file = g_file_new_build_filename (g_get_user_data_dir (),
						    "enter-tex",
						    "templatesrc",
						    NULL);

	copy_file (gnome_latex_file, enter_tex_file);

	g_object_unref (gnome_latex_file);
	g_object_unref (enter_tex_file);
}

static void
migrate_personal_templates (void)
{
	MigratePersonalTemplatesResult result;

	result = migrate_personal_templates_tex_files ();

	/* Do not mix the TeX templates from LaTeXila with the rc file from
	 * GNOME LaTeX, or vice-versa! Be consistent.
	 */
	if (result == MIGRATE_PERSONAL_TEMPLATES_RESULT_FROM_GNOME_LATEX)
	{
		migrate_personal_templates_rc_file_from_gnome_latex ();
	}
	else if (result == MIGRATE_PERSONAL_TEMPLATES_RESULT_FROM_LATEXILA)
	{
		migrate_personal_templates_rc_file_from_latexila ();
	}
}

/**
 * gtex_utils_migrate_settings:
 *
 * Migrates the #GSettings values and user data/config files from LaTeXila and
 * GNOME LaTeX to Enter TeX, so that users don't lose all their settings.
 *
 * TODO in >= 2032: delete this code.
 */
void
gtex_utils_migrate_settings (void)
{
	GSettings *settings;

	settings = g_settings_new ("org.gnome.enter_tex");

	if (!g_settings_get_boolean (settings, "settings-migration-done"))
	{
		/* Set the gsetting to true directly. If the application crashes
		 * because of one of the migrate_*() function, then it won't
		 * crash repeatedly.
		 */
		g_settings_set_boolean (settings, "settings-migration-done", TRUE);

		migrate_gsettings ();
		migrate_most_used_symbols ();
		migrate_projects ();
		migrate_personal_build_tools ();
		migrate_personal_templates ();
	}

	g_object_unref (settings);
}
