/* SPDX-FileCopyrightText: 2025 - Sébastien Wilmet
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#include "gedit-tab-loader.h"
#include "gedit-document-private.h"
#include "gedit-settings.h"

struct _GeditTabLoaderPrivate
{
	/* Weak ref */
	GeditTab *tab;

	/* Strong ref */
	GtkSourceFileLoader *file_loader;

	/* Where to place the cursor. */
	gint line_position;
	gint column_position;

	guint user_requested_encoding : 1;
};

G_DEFINE_TYPE_WITH_PRIVATE (GeditTabLoader, _gedit_tab_loader, G_TYPE_OBJECT)

static void
_gedit_tab_loader_dispose (GObject *object)
{
	GeditTabLoader *tab_loader = GEDIT_TAB_LOADER (object);

	g_clear_weak_pointer (&tab_loader->priv->tab);
	g_clear_object (&tab_loader->priv->file_loader);

	G_OBJECT_CLASS (_gedit_tab_loader_parent_class)->dispose (object);
}

static void
_gedit_tab_loader_class_init (GeditTabLoaderClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->dispose = _gedit_tab_loader_dispose;
}

static void
_gedit_tab_loader_init (GeditTabLoader *tab_loader)
{
	tab_loader->priv = _gedit_tab_loader_get_instance_private (tab_loader);
}

GeditTabLoader *
_gedit_tab_loader_new (GeditTab *tab)
{
	GeditTabLoader *tab_loader;

	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL);

	tab_loader = g_object_new (GEDIT_TYPE_TAB_LOADER, NULL);

	g_set_weak_pointer (&tab_loader->priv->tab, tab);

	return tab_loader;
}

GtkSourceFileLoader *
_gedit_tab_loader_get_file_loader (GeditTabLoader *tab_loader)
{
	g_return_val_if_fail (GEDIT_IS_TAB_LOADER (tab_loader), NULL);
	return tab_loader->priv->file_loader;
}

void
_gedit_tab_loader_set_file_loader (GeditTabLoader      *tab_loader,
				   GtkSourceFileLoader *file_loader)
{
	GeditSettings *settings;
	GSettings *editor_settings;
	guint64 max_file_size;

	g_return_if_fail (GEDIT_IS_TAB_LOADER (tab_loader));
	g_return_if_fail (GTK_SOURCE_IS_FILE_LOADER (file_loader));
	g_return_if_fail (tab_loader->priv->file_loader == NULL);

	tab_loader->priv->file_loader = g_object_ref (file_loader);

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

	max_file_size = g_settings_get_uint64 (editor_settings, GEDIT_SETTINGS_MAX_FILE_SIZE);
	gtk_source_file_loader_set_max_size (file_loader, max_file_size);
}

void
_gedit_tab_loader_set_line_position (GeditTabLoader *tab_loader,
				     gint            line_position)
{
	g_return_if_fail (GEDIT_IS_TAB_LOADER (tab_loader));
	tab_loader->priv->line_position = line_position;
}

void
_gedit_tab_loader_set_column_position (GeditTabLoader *tab_loader,
				       gint            column_position)
{
	g_return_if_fail (GEDIT_IS_TAB_LOADER (tab_loader));
	tab_loader->priv->column_position = column_position;
}

void
_gedit_tab_loader_place_cursor (GeditTabLoader *tab_loader)
{
	GeditDocument *doc;
	GeditSettings *settings;
	GSettings *editor_settings;
	gboolean check_is_cursor_position = FALSE;
	GtkTextIter iter;

	g_return_if_fail (GEDIT_IS_TAB_LOADER (tab_loader));

	if (tab_loader->priv->tab == NULL)
	{
		return;
	}

	doc = gedit_tab_get_document (tab_loader->priv->tab);

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

	/* To the top by default. */
	gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc), &iter);

	/* At the requested line/column if set. */
	if (tab_loader->priv->line_position > 0)
	{
		gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (doc),
							 &iter,
							 tab_loader->priv->line_position - 1,
							 MAX (0, tab_loader->priv->column_position - 1));
		check_is_cursor_position = TRUE;
	}

	/* From metadata. */
	else if (g_settings_get_boolean (editor_settings, GEDIT_SETTINGS_RESTORE_CURSOR_POSITION))
	{
		gchar *position_str;
		guint64 offset = 0;

		position_str = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_POSITION);

		if (position_str != NULL &&
		    g_ascii_string_to_unsigned (position_str,
						10,
						0,
						G_MAXINT,
						&offset,
						NULL))
		{
			gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc),
							    &iter,
							    (gint) offset);
			check_is_cursor_position = TRUE;
		}

		g_free (position_str);
	}

	/* Make sure it's a valid position, to not end up in the middle of a
	 * utf8 character cluster.
	 */
	if (check_is_cursor_position &&
	    !gtk_text_iter_is_cursor_position (&iter))
	{
		gtk_text_iter_set_line_offset (&iter, 0);
	}

	gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter);
}

/* The returned list may contain duplicated encodings. Only the first occurrence
 * of a duplicated encoding should be kept, like it is done by
 * gtk_source_file_loader_set_candidate_encodings().
 * Returns: (transfer full) (element-type GtkSourceEncoding)
 */
static GSList *
get_candidate_encodings (GeditTabLoader *tab_loader)
{
	GeditDocument *doc;
	GtkSourceFile *file;
	GSList *candidates = NULL;
	gchar *metadata_charset;
	const GtkSourceEncoding *file_encoding;

	g_return_val_if_fail (GEDIT_IS_TAB_LOADER (tab_loader), NULL);

	if (tab_loader->priv->tab == NULL)
	{
		return NULL;
	}

	doc = gedit_tab_get_document (tab_loader->priv->tab);
	file = gedit_document_get_file (doc);

	candidates = gedit_settings_get_candidate_encodings (NULL);

	/* Prepend the encoding stored in the metadata. */
	metadata_charset = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_ENCODING);
	if (metadata_charset != NULL)
	{
		GtkSourceEncoding *metadata_encoding;

		metadata_encoding = gtk_source_encoding_new (metadata_charset);

		if (gtk_source_encoding_has_known_charset (metadata_encoding))
		{
			candidates = g_slist_prepend (candidates, metadata_encoding);
		}
		else
		{
			gtk_source_encoding_free (metadata_encoding);
		}
	}

	/* Finally prepend the GtkSourceFile's encoding, if previously set by a
	 * file loader or file saver.
	 */
	file_encoding = gtk_source_file_get_encoding (file);
	if (file_encoding != NULL)
	{
		candidates = g_slist_prepend (candidates, gtk_source_encoding_copy (file_encoding));
	}

	g_free (metadata_charset);
	return candidates;
}

void
_gedit_tab_loader_set_encoding (GeditTabLoader          *tab_loader,
				const GtkSourceEncoding *encoding)
{
	GSList *candidate_encodings = NULL;

	g_return_if_fail (GEDIT_IS_TAB_LOADER (tab_loader));
	g_return_if_fail (tab_loader->priv->file_loader != NULL);

	if (encoding != NULL)
	{
		tab_loader->priv->user_requested_encoding = TRUE;
		candidate_encodings = g_slist_append (NULL, gtk_source_encoding_copy (encoding));
	}
	else
	{
		tab_loader->priv->user_requested_encoding = FALSE;
		candidate_encodings = get_candidate_encodings (tab_loader);
	}

	gtk_source_file_loader_set_candidate_encodings (tab_loader->priv->file_loader, candidate_encodings);
	g_slist_free_full (candidate_encodings, (GDestroyNotify) gtk_source_encoding_free);
}

void
_gedit_tab_loader_save_encoding_metadata (GeditTabLoader *tab_loader)
{
	GeditDocument *doc;
	const GtkSourceEncoding *encoding;
	const gchar *charset;

	g_return_if_fail (GEDIT_IS_TAB_LOADER (tab_loader));
	g_return_if_fail (tab_loader->priv->file_loader != NULL);

	if (tab_loader->priv->tab == NULL ||
	    !tab_loader->priv->user_requested_encoding)
	{
		return;
	}

	doc = gedit_tab_get_document (tab_loader->priv->tab);

	encoding = gtk_source_file_loader_get_encoding (tab_loader->priv->file_loader);
	charset = gtk_source_encoding_get_charset (encoding);

	gedit_document_set_metadata (doc,
				     GEDIT_METADATA_ATTRIBUTE_ENCODING, charset,
				     NULL);
}
