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

#include "gedit-code-comment-window-activatable.h"
#include <gedit/gedit-window.h>
#include <gedit/gedit-window-activatable.h>
#include <tepl/tepl.h>

struct _GeditCodeCommentWindowActivatablePrivate
{
	GeditWindow *window;
	TeplSignalGroup *window_signal_group;

	/* The TeplCodeCommentView for the active view. */
	TeplCodeCommentView *code_comment_view;
	TeplSignalGroup *code_comment_view_signal_group;
};

enum
{
	PROP_0,
	PROP_WINDOW
};

static void gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface);

G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditCodeCommentWindowActivatable,
				gedit_code_comment_window_activatable,
				G_TYPE_OBJECT,
				0,
				G_ADD_PRIVATE_DYNAMIC (GeditCodeCommentWindowActivatable)
				G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_WINDOW_ACTIVATABLE,
							       gedit_window_activatable_iface_init))

static void
gedit_code_comment_window_activatable_get_property (GObject    *object,
						    guint       prop_id,
						    GValue     *value,
						    GParamSpec *pspec)
{
	GeditCodeCommentWindowActivatable *activatable = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (object);

	switch (prop_id)
	{
		case PROP_WINDOW:
			g_value_set_object (value, activatable->priv->window);
			break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}

static void
gedit_code_comment_window_activatable_set_property (GObject      *object,
						    guint         prop_id,
						    const GValue *value,
						    GParamSpec   *pspec)
{
	GeditCodeCommentWindowActivatable *activatable = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (object);

	switch (prop_id)
	{
		case PROP_WINDOW:
			g_assert (activatable->priv->window == NULL);
			activatable->priv->window = GEDIT_WINDOW (g_value_dup_object (value));
			break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}

static void
gedit_code_comment_window_activatable_dispose (GObject *object)
{
	GeditCodeCommentWindowActivatable *activatable = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (object);

	g_clear_object (&activatable->priv->window);
	tepl_signal_group_clear (&activatable->priv->window_signal_group);

	g_clear_object (&activatable->priv->code_comment_view);
	tepl_signal_group_clear (&activatable->priv->code_comment_view_signal_group);

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

static void
gedit_code_comment_window_activatable_class_init (GeditCodeCommentWindowActivatableClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->get_property = gedit_code_comment_window_activatable_get_property;
	object_class->set_property = gedit_code_comment_window_activatable_set_property;
	object_class->dispose = gedit_code_comment_window_activatable_dispose;

	g_object_class_override_property (object_class, PROP_WINDOW, "window");
}

static void
gedit_code_comment_window_activatable_class_finalize (GeditCodeCommentWindowActivatableClass *klass)
{
}

static void
gedit_code_comment_window_activatable_init (GeditCodeCommentWindowActivatable *activatable)
{
	activatable->priv = gedit_code_comment_window_activatable_get_instance_private (activatable);
}

static void
comment_activate_cb (GSimpleAction *action,
		     GVariant      *parameter,
		     gpointer       user_data)
{
	GeditCodeCommentWindowActivatable *self = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (user_data);

	g_return_if_fail (self->priv->code_comment_view != NULL);
	g_return_if_fail (tepl_code_comment_view_code_comment_is_supported (self->priv->code_comment_view));

	tepl_code_comment_view_comment_selected_lines (self->priv->code_comment_view);
}

static void
uncomment_activate_cb (GSimpleAction *action,
		       GVariant      *parameter,
		       gpointer       user_data)
{
	GeditCodeCommentWindowActivatable *self = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (user_data);

	g_return_if_fail (self->priv->code_comment_view != NULL);
	g_return_if_fail (tepl_code_comment_view_code_comment_is_supported (self->priv->code_comment_view));

	tepl_code_comment_view_uncomment_selection (self->priv->code_comment_view);
}

static const GActionEntry action_entries[] = {
	{ "code-comment-plugin-comment", comment_activate_cb },
	{ "code-comment-plugin-uncomment", uncomment_activate_cb },
};

static void
add_actions (GeditCodeCommentWindowActivatable *self)
{
	g_action_map_add_action_entries (G_ACTION_MAP (self->priv->window),
					 action_entries,
					 G_N_ELEMENTS (action_entries),
					 self);
}

static void
remove_actions (GeditCodeCommentWindowActivatable *self)
{
	g_action_map_remove_action_entries (G_ACTION_MAP (self->priv->window),
					    action_entries,
					    G_N_ELEMENTS (action_entries));
}

static void
update_actions_sensitivity (GeditCodeCommentWindowActivatable *self)
{
	gboolean sensitive = FALSE;
	gint action_entry_num;

	if (self->priv->code_comment_view != NULL)
	{
		sensitive = tepl_code_comment_view_code_comment_is_supported (self->priv->code_comment_view);
	}

	for (action_entry_num = 0; action_entry_num < G_N_ELEMENTS (action_entries); action_entry_num++)
	{
		const gchar *cur_action_name = action_entries[action_entry_num].name;
		GAction *cur_action;

		cur_action = g_action_map_lookup_action (G_ACTION_MAP (self->priv->window),
							 cur_action_name);
		if (cur_action != NULL)
		{
			g_simple_action_set_enabled (G_SIMPLE_ACTION (cur_action), sensitive);
		}
	}
}

static void
code_comment_is_supported_notify_cb (TeplCodeCommentView               *code_comment_view,
				     GParamSpec                        *pspec,
				     GeditCodeCommentWindowActivatable *self)
{
	update_actions_sensitivity (self);
}

static void
view_changed (GeditCodeCommentWindowActivatable *self)
{
	GeditView *active_view;

	g_clear_object (&self->priv->code_comment_view);
	tepl_signal_group_clear (&self->priv->code_comment_view_signal_group);

	active_view = gedit_window_get_active_view (self->priv->window);
	if (active_view != NULL)
	{
		self->priv->code_comment_view = tepl_code_comment_view_new (GTK_SOURCE_VIEW (active_view));
		self->priv->code_comment_view_signal_group = tepl_signal_group_new (G_OBJECT (self->priv->code_comment_view));
		tepl_signal_group_add (self->priv->code_comment_view_signal_group,
				       g_signal_connect (self->priv->code_comment_view,
							 "notify::code-comment-is-supported",
							 G_CALLBACK (code_comment_is_supported_notify_cb),
							 self));
	}

	update_actions_sensitivity (self);
}

static void
active_tab_changed_cb (GeditWindow                       *window,
		       GeditCodeCommentWindowActivatable *self)
{
	view_changed (self);
}

static void
gedit_code_comment_window_activatable_activate (GeditWindowActivatable *activatable)
{
	GeditCodeCommentWindowActivatable *self = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (activatable);

	add_actions (self);

	tepl_signal_group_clear (&self->priv->window_signal_group);
	self->priv->window_signal_group = tepl_signal_group_new (G_OBJECT (self->priv->window));
	tepl_signal_group_add (self->priv->window_signal_group,
			       g_signal_connect (self->priv->window,
						 "active-tab-changed",
						 G_CALLBACK (active_tab_changed_cb),
						 self));

	view_changed (self);
}

static void
gedit_code_comment_window_activatable_deactivate (GeditWindowActivatable *activatable)
{
	GeditCodeCommentWindowActivatable *self = GEDIT_CODE_COMMENT_WINDOW_ACTIVATABLE (activatable);

	remove_actions (self);

	tepl_signal_group_clear (&self->priv->window_signal_group);

	g_clear_object (&self->priv->code_comment_view);
	tepl_signal_group_clear (&self->priv->code_comment_view_signal_group);
}

static void
gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface)
{
	iface->activate = gedit_code_comment_window_activatable_activate;
	iface->deactivate = gedit_code_comment_window_activatable_deactivate;

	/* To update the GActions sensitivity, the
	 * GeditWindowActivatableInterface::update_state() method is _not_ used,
	 * because:
	 * 1. It doesn't use the right APIs. The right API is to reach the
	 *    TeplCodeCommentView:code-comment-is-supported property.
	 * 2. update_state() is called much more often, and we don't want to
	 *    instantiate a new TeplCodeCommentView GObject each time (for some
	 *    performance reasons, although it probably doesn't matter much).
	 */
}

void
gedit_code_comment_window_activatable_register (PeasObjectModule *module)
{
	gedit_code_comment_window_activatable_register_type (G_TYPE_MODULE (module));

	peas_object_module_register_extension_type (module,
						    GEDIT_TYPE_WINDOW_ACTIVATABLE,
						    GEDIT_TYPE_CODE_COMMENT_WINDOW_ACTIVATABLE);
}
