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

#include "gtex-tree-model-node.h"

/**
 * SECTION:tree-model-node
 * @title: GtexTreeModelNode
 * @short_description: #GtkTreeModel based on #GNode
 *
 * #GtexTreeModelNode is a class that implements a maximum of the #GtkTreeModel
 * interface in a generic way, based on a #GNode tree. #GtexTreeModelNode
 * doesn't look at the #GNode's data fields, it is not aware of what the nodes
 * contain.
 *
 * Only the following virtual functions of #GtkTreeModelIface are
 * <emphasis>not</emphasis> implemented by #GtexTreeModelNode:
 * - #GtkTreeModelIface.get_value()
 * - #GtkTreeModelIface.ref_node() (optional)
 * - #GtkTreeModelIface.unref_node() (optional)
 *
 * So a subclass of #GtexTreeModelNode needs to be created, implementing at
 * least #GtkTreeModelIface.get_value().
 *
 * Side note: #GObject permits to implement an interface in this fashion. In
 * other words, class "A" inherits from #GObject and implements some vfuncs of
 * the interface "I". Then class "B" inherits from class "A" and implements the
 * remaining vfuncs from the same interface "I".
 */

struct _GtexTreeModelNodePrivate
{
	gint n_columns;
	GType *column_types;

	gint stamp;

	GNode *tree; /* unowned */
};

static void gtex_tree_model_node_tree_model_init (GtkTreeModelIface *iface);

G_DEFINE_TYPE_WITH_CODE (GtexTreeModelNode, gtex_tree_model_node, G_TYPE_OBJECT,
			 G_ADD_PRIVATE (GtexTreeModelNode)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
						gtex_tree_model_node_tree_model_init))

static void
invalidate_iter (GtkTreeIter *iter)
{
	iter->user_data = NULL;
}

static void
gtex_tree_model_node_finalize (GObject *object)
{
	GtexTreeModelNode *model = GTEX_TREE_MODEL_NODE (object);

	g_free (model->priv->column_types);

	G_OBJECT_CLASS (gtex_tree_model_node_parent_class)->finalize (object);
}

static void
gtex_tree_model_node_class_init (GtexTreeModelNodeClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->finalize = gtex_tree_model_node_finalize;
}

static void
gtex_tree_model_node_init (GtexTreeModelNode *model)
{
	model->priv = gtex_tree_model_node_get_instance_private (model);

	model->priv->stamp = g_random_int_range (1, G_MAXINT);
}

/**
 * gtex_tree_model_node_set_column_types:
 * @gtex_model: a #GtexTreeModelNode.
 * @n_columns: number of columns.
 * @column_types: (array length=n_columns): an array of #GType types for the
 *   columns, from first to last.
 */
void
gtex_tree_model_node_set_column_types (GtexTreeModelNode *gtex_model,
				       gint               n_columns,
				       GType             *column_types)
{
	g_return_if_fail (GTEX_IS_TREE_MODEL_NODE (gtex_model));
	g_return_if_fail (n_columns >= 0);

	gtex_model->priv->n_columns = n_columns;
	gtex_model->priv->column_types = g_memdup2 (column_types, sizeof (GType) * n_columns);
}

void
gtex_tree_model_node_set_tree (GtexTreeModelNode *model,
			       GNode             *tree)
{
	g_return_if_fail (GTEX_IS_TREE_MODEL_NODE (model));
	g_return_if_fail (tree != NULL);
	g_return_if_fail (model->priv->tree == NULL);

	model->priv->tree = tree;
}

gboolean
gtex_tree_model_node_column_index_is_valid (GtexTreeModelNode *model,
					    gint               index)
{
	g_return_val_if_fail (GTEX_IS_TREE_MODEL_NODE (model), FALSE);
	return 0 <= index && index < model->priv->n_columns;
}

/* The stamp needs to change for each time the data in the model changes. */
void
gtex_tree_model_node_change_stamp (GtexTreeModelNode *model)
{
	g_return_if_fail (GTEX_IS_TREE_MODEL_NODE (model));
	model->priv->stamp++;
}

/**
 * gtex_tree_model_node_get_iter_at_node:
 * @model:
 * @iter: (out):
 * @node:
 */
void
gtex_tree_model_node_get_iter_at_node (GtexTreeModelNode *model,
				       GtkTreeIter       *iter,
				       GNode             *node)
{
	g_return_if_fail (GTEX_IS_TREE_MODEL_NODE (model));
	g_return_if_fail (iter != NULL);
	g_return_if_fail (node != NULL);
	g_return_if_fail (node != model->priv->tree);

	iter->stamp = model->priv->stamp;
	iter->user_data = node;
	iter->user_data2 = NULL;
	iter->user_data3 = NULL;
}

/**
 * gtex_tree_model_node_get_node_from_iter:
 * @iter: must be a valid #GtkTreeIter.
 *
 * Returns: (transfer none):
 */
GNode *
gtex_tree_model_node_get_node_from_iter (const GtkTreeIter *iter)
{
	g_return_val_if_fail (iter != NULL, NULL);
	return iter->user_data;
}

gboolean
gtex_tree_model_node_iter_is_valid (GtexTreeModelNode *model,
				    const GtkTreeIter *iter)
{
	g_return_val_if_fail (GTEX_IS_TREE_MODEL_NODE (model), FALSE);
	g_return_val_if_fail (iter != NULL, FALSE);

	return (iter->stamp == model->priv->stamp &&
		iter->user_data != NULL);
}

static GtkTreeModelFlags
gtex_tree_model_node_get_flags (GtkTreeModel *gtk_model)
{
	return 0;
}

static gint
gtex_tree_model_node_get_n_columns (GtkTreeModel *gtk_model)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	return gtex_model->priv->n_columns;
}

static GType
gtex_tree_model_node_get_column_type (GtkTreeModel *gtk_model,
				      gint          index)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);

	g_return_val_if_fail (gtex_tree_model_node_column_index_is_valid (gtex_model, index),
			      G_TYPE_INVALID);

	return gtex_model->priv->column_types[index];
}

static gboolean
gtex_tree_model_node_get_iter (GtkTreeModel *gtk_model,
			       GtkTreeIter  *iter,
			       GtkTreePath  *path)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	gint depth;
	gint *indices;
	GNode *node;
	gint cur_depth;

	g_return_val_if_fail (iter != NULL, FALSE);
	g_return_val_if_fail (path != NULL, FALSE);
	g_return_val_if_fail (gtex_model->priv->tree != NULL, FALSE);

	depth = gtk_tree_path_get_depth (path);
	g_return_val_if_fail (depth >= 1, FALSE);

	indices = gtk_tree_path_get_indices (path);
	node = gtex_model->priv->tree;

	for (cur_depth = 0; cur_depth < depth; cur_depth++)
	{
		gint index = indices[cur_depth];
		guint n_children = g_node_n_children (node);

		if (index < 0 || (gint)n_children <= index)
		{
			return FALSE;
		}

		node = g_node_nth_child (node, index);
	}

	gtex_tree_model_node_get_iter_at_node (gtex_model, iter, node);
	return TRUE;
}

static GtkTreePath *
gtex_tree_model_node_get_path (GtkTreeModel *gtk_model,
			       GtkTreeIter  *iter)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GtkTreePath *path;
	GNode *node;

	g_return_val_if_fail (iter != NULL, NULL);
	g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, iter), NULL);

	path = gtk_tree_path_new ();
	node = gtex_tree_model_node_get_node_from_iter (iter);

	while (!G_NODE_IS_ROOT (node))
	{
		gint position = g_node_child_position (node->parent, node);
		gtk_tree_path_prepend_index (path, position);
		node = node->parent;
	}

	return path;
}

static gboolean
gtex_tree_model_node_iter_next (GtkTreeModel *gtk_model,
				GtkTreeIter  *iter)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;
	GNode *next_node;

	g_return_val_if_fail (iter != NULL, FALSE);
	g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, iter), FALSE);

	node = gtex_tree_model_node_get_node_from_iter (iter);
	next_node = node->next;

	if (next_node == NULL)
	{
		invalidate_iter (iter);
		return FALSE;
	}

	gtex_tree_model_node_get_iter_at_node (gtex_model, iter, next_node);
	return TRUE;
}

static gboolean
gtex_tree_model_node_iter_previous (GtkTreeModel *gtk_model,
				    GtkTreeIter  *iter)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;
	GNode *prev_node;

	g_return_val_if_fail (iter != NULL, FALSE);
	g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, iter), FALSE);

	node = gtex_tree_model_node_get_node_from_iter (iter);
	prev_node = node->prev;

	if (prev_node == NULL)
	{
		invalidate_iter (iter);
		return FALSE;
	}

	gtex_tree_model_node_get_iter_at_node (gtex_model, iter, prev_node);
	return TRUE;
}

static gboolean
gtex_tree_model_node_iter_children (GtkTreeModel *gtk_model,
				    GtkTreeIter  *iter,
				    GtkTreeIter  *parent)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;

	g_return_val_if_fail (iter != NULL, FALSE);

	if (parent == NULL)
	{
		node = gtex_model->priv->tree;
	}
	else
	{
		g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, parent), FALSE);
		node = gtex_tree_model_node_get_node_from_iter (parent);
	}

	if (G_NODE_IS_LEAF (node))
	{
		invalidate_iter (iter);
		return FALSE;
	}

	gtex_tree_model_node_get_iter_at_node (gtex_model, iter, node->children);
	return TRUE;
}

static gboolean
gtex_tree_model_node_iter_has_child (GtkTreeModel *gtk_model,
				     GtkTreeIter  *iter)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;

	g_return_val_if_fail (iter != NULL, FALSE);
	g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, iter), FALSE);

	node = gtex_tree_model_node_get_node_from_iter (iter);
	return !G_NODE_IS_LEAF (node);
}

static gint
gtex_tree_model_node_iter_n_children (GtkTreeModel *gtk_model,
				      GtkTreeIter  *iter)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;

	if (iter == NULL)
	{
		node = gtex_model->priv->tree;
	}
	else
	{
		g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, iter), 0);
		node = gtex_tree_model_node_get_node_from_iter (iter);
	}

	return g_node_n_children (node);
}

static gboolean
gtex_tree_model_node_iter_nth_child (GtkTreeModel *gtk_model,
				     GtkTreeIter  *iter,
				     GtkTreeIter  *parent,
				     gint          n)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;
	GNode *child_node;

	g_return_val_if_fail (iter != NULL, FALSE);

	invalidate_iter (iter);

	if (parent == NULL)
	{
		node = gtex_model->priv->tree;
	}
	else
	{
		g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, parent), FALSE);
		node = gtex_tree_model_node_get_node_from_iter (parent);
	}

	if (G_NODE_IS_LEAF (node) || n < 0)
	{
		return FALSE;
	}

	child_node = g_node_nth_child (node, n);
	if (child_node == NULL)
	{
		return FALSE;
	}

	gtex_tree_model_node_get_iter_at_node (gtex_model, iter, child_node);
	return TRUE;
}

static gboolean
gtex_tree_model_node_iter_parent (GtkTreeModel *gtk_model,
				  GtkTreeIter  *iter,
				  GtkTreeIter  *child)
{
	GtexTreeModelNode *gtex_model = GTEX_TREE_MODEL_NODE (gtk_model);
	GNode *node;
	GNode *parent_node;

	g_return_val_if_fail (iter != NULL, FALSE);
	g_return_val_if_fail (child != NULL, FALSE);
	g_return_val_if_fail (gtex_tree_model_node_iter_is_valid (gtex_model, child), FALSE);
	g_return_val_if_fail (iter != child, FALSE);

	node = gtex_tree_model_node_get_node_from_iter (child);
	parent_node = node->parent;

	/* Normally, there is always a parent GNode. */
	g_return_val_if_fail (parent_node != NULL, FALSE);

	/* But the root GNode is not a good parent. I.e., @child is already at
	 * the toplevel.
	 */
	if (parent_node == gtex_model->priv->tree)
	{
		invalidate_iter (iter);
		return FALSE;
	}

	gtex_tree_model_node_get_iter_at_node (gtex_model, iter, parent_node);
	return TRUE;
}

static void
gtex_tree_model_node_tree_model_init (GtkTreeModelIface *iface)
{
	/* get_value() cannot be implemented in a generic way. It must be
	 * implemented by a subclass of GtexTreeModelNode.
	 */

	iface->get_flags = gtex_tree_model_node_get_flags;
	iface->get_n_columns = gtex_tree_model_node_get_n_columns;
	iface->get_column_type = gtex_tree_model_node_get_column_type;
	iface->get_iter = gtex_tree_model_node_get_iter;
	iface->get_path = gtex_tree_model_node_get_path;
	iface->iter_next = gtex_tree_model_node_iter_next;
	iface->iter_previous = gtex_tree_model_node_iter_previous;
	iface->iter_children = gtex_tree_model_node_iter_children;
	iface->iter_has_child = gtex_tree_model_node_iter_has_child;
	iface->iter_n_children = gtex_tree_model_node_iter_n_children;
	iface->iter_nth_child = gtex_tree_model_node_iter_nth_child;
	iface->iter_parent = gtex_tree_model_node_iter_parent;
}
