/* This file is part of GQ
 *
 * AUTHORS
 *     Sven Herzberg  <herzi@gnome-de.org>
 *
 * Copyright (C) 2006  Sven Herzberg <herzi@gnome-de.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include "gq-browser-model.h"

#include "errorchain.h"
#include "gq-browser-node-dummy.h"
#include "gq-browser-node-server.h"
#include "gq-main-loop.h"
#include "gq-server-list.h"

#include <glib/gi18n.h>

#undef DEBUG_MODEL

struct GqBrowserModelPrivate {
	GNode* tree_root;
};
#define P(i) (G_TYPE_INSTANCE_GET_PRIVATE((i), GQ_TYPE_BROWSER_MODEL, struct GqBrowserModelPrivate))

static GType column_types[GQ_BROWSER_MODEL_N_COLUMNS] = {0};

#ifdef DEBUG_MODEL
static void
browser_model_dump_iter(GtkTreeIter* iter)
{
	g_print("  DEBUG ITER: %p\n", iter);
	if(iter) {
		g_print("    stamp:      %d\n"
			"    user_data:  %p\n"
			"    user_data2: %p\n"
			"    user_data3: %p\n",
			iter->stamp,
			iter->user_data,
			iter->user_data2,
			iter->user_data3);
	}
}
#endif
GtkTreeModel*
gq_browser_model_new(void)
{
	return g_object_new(GQ_TYPE_BROWSER_MODEL, NULL);
}

static void
browser_model_get_iter_from_node(GtkTreeModel  * self G_GNUC_UNUSED,
				 GtkTreeIter   * iter,
				 GNode         * node)
{
	if(node) {
#warning "FIXME: set stamp"
		iter->user_data  = node;
		iter->user_data2 = node->data;
	}
	else {
#warning "FIXME: set stamp"
		iter->user_data = NULL;
		iter->user_data2 = NULL;
	}
}

static void
browser_model_notify_connected(GqBrowserModel* self,
			       GParamSpec    * pspec G_GNUC_UNUSED,
			       GqBrowserNode * node)
{
	GtkTreePath* path;
	GtkTreeIter  iter;
	GNode* gnode = g_object_get_data(G_OBJECT(node), "model-node");
	g_return_if_fail(gnode);

	browser_model_get_iter_from_node(GTK_TREE_MODEL(self),
					 &iter,
					 gnode);
	path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), &iter);
	gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
	gtk_tree_path_free(path);
}

void
gq_browser_model_add_node(GqBrowserModel const* self,
			  GtkTreeIter    const* iter,
			  GqBrowserNode       * node)
{
	GNode* gnode;

	g_return_if_fail(GQ_IS_BROWSER_MODEL(self));
	g_return_if_fail(gq_browser_model_validate(self, iter));

	gnode = g_node_append_data(iter->user_data, node);

	g_signal_connect_swapped(node, "notify::connected",
				 G_CALLBACK(browser_model_notify_connected), self);
	g_object_set_data(G_OBJECT(node), "model-node", node);
}

GqBrowserNode*
gq_browser_model_get_node(GqBrowserModel const* self,
			  GtkTreeIter const   * iter)
{
	g_return_val_if_fail(GQ_IS_BROWSER_MODEL(self), NULL);
	g_return_val_if_fail(gq_browser_model_validate(self, iter), NULL);

	return GQ_BROWSER_NODE(iter->user_data2);
}

/* GType */
static void browser_model_implement_tree(GtkTreeModelIface* iface);
G_DEFINE_TYPE_WITH_CODE(GqBrowserModel, gq_browser_model, G_TYPE_OBJECT,
			G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, browser_model_implement_tree));

static void
browser_model_add_server(GqServerList* list G_GNUC_UNUSED,
			 GqServer    * server,
			 gpointer      user_data)
{
	GNode* root = user_data;

	g_node_append_data(root, gq_browser_node_server_new(server));
}

static void
gq_browser_model_init(GqBrowserModel* self)
{
	P(self)->tree_root = g_node_new(NULL);

	gq_server_list_foreach(gq_server_list_get(),
			       browser_model_add_server,
			       P(self)->tree_root);
}

static void
gq_browser_model_class_init(GqBrowserModelClass* self_class)
{
	g_type_class_add_private(self_class, sizeof(struct GqBrowserModelPrivate));
#warning "FIXME: memory management (tree_root)"
	column_types[GQ_BROWSER_MODEL_COL_NAME] = G_TYPE_STRING;
	column_types[GQ_BROWSER_MODEL_COL_TYPE] = G_TYPE_STRING;
}

/* GtkTreeModelIface */
static gint
browser_model_get_n_columns(GtkTreeModel* model)
{
	g_return_val_if_fail(GQ_IS_BROWSER_MODEL(model), 0);

	return GQ_BROWSER_MODEL_N_COLUMNS;
}

static GType
browser_model_get_column_type(GtkTreeModel* model G_GNUC_UNUSED,
			      gint          column)
{
	return column_types[column];
}

gboolean
gq_browser_model_validate(gconstpointer     * model,
			  GtkTreeIter  const* iter)
{
#ifdef DEBUG_MODEL
	g_print("browser_model_validate_iter(%p, %p): ", model, iter);
#endif
#warning "FIXME: check stamp"
	if(!iter->user_data) {
#ifdef DEBUG_MODEL
		g_print("false (iter->user_data == NULL)\n");
#endif
		return FALSE;
	}

	if(!GQ_IS_BROWSER_NODE(iter->user_data2)) {
#ifdef DEBUG_MODEL
		g_print("false (iter->user_data2 is no GqBrowserNode)\n");
#endif
		return FALSE;
	}

#ifdef DEBUG_MODEL
	g_print("true\n");
#endif
	return TRUE;
}

#define browser_model_validate_iter gq_browser_model_validate
static gboolean
browser_model_get_iter(GtkTreeModel* model,
		       GtkTreeIter * iter,
		       GtkTreePath * path)
{
	GNode* node = P(model)->tree_root;
	gint  depth,
	    * indices,
	      max_depth;
#ifdef DEBUG_MODEL
	gchar* path_s = gtk_tree_path_to_string(path);
	g_print("%s(%p, %p, %s): ", __PRETTY_FUNCTION__, model, iter, path_s);
	g_free(path_s);
	path_s = NULL;
#endif

	max_depth = gtk_tree_path_get_depth(path);
	indices   = gtk_tree_path_get_indices(path);
	for(depth = 0; node && depth < max_depth; depth++) {
		node = g_node_nth_child(node, indices[depth]);
	}

	if(node) {
		browser_model_get_iter_from_node(model, iter, node);
#ifdef DEBUG_MODEL
		g_print("%s\n", browser_model_validate_iter(model, iter) ? "true" : "false");
#endif
		return browser_model_validate_iter(model, iter);
	}
	else {
#ifdef DEBUG_MODEL
		g_print("false\n");
#endif
		return FALSE;
	}
}

static GtkTreePath*
browser_model_get_path(GtkTreeModel* model,
		       GtkTreeIter * iter)
{
	GtkTreePath* path;
	GNode      * parent_node;
#warning "FIXME: check stamp"
	parent_node = ((GNode*)iter->user_data)->parent;
	if(((GNode*)iter->user_data)->parent == P(model)->tree_root) {
		path = gtk_tree_path_new();
	}
	else {
		GtkTreeIter parent;
		gtk_tree_model_iter_parent(model, &parent, iter);
		path = browser_model_get_path(model, &parent);
	}

	gtk_tree_path_append_index(path, g_node_child_position(parent_node, iter->user_data));

	return path;
}

static void
browser_model_get_value(GtkTreeModel* model,
			GtkTreeIter * iter,
			gint          column,
			GValue      * value)
{
	g_value_init(value, gtk_tree_model_get_column_type(model, column));

#warning "FIXME: check stamp"

	switch(column) {
		gchar const* status;
	case GQ_BROWSER_MODEL_COL_NAME:
#warning "FIXME: display the new part of DN's only"
		g_value_take_string(value, gq_browser_node_get_name(iter->user_data2, TRUE));
		break;
	case GQ_BROWSER_MODEL_COL_TYPE:
		status = gq_browser_node_get_status(iter->user_data2);
		g_value_set_string(value, status ? status : "folder");
		break;
	default:
		// not dangerous as g_value_init() already set an empty default
		g_warning("Invalid column type");
		break;
	}
}

static gboolean
browser_model_iter_has_child(GtkTreeModel* model,
			     GtkTreeIter * iter)
{
	g_return_val_if_fail(gq_browser_model_validate(model, iter), FALSE);
#ifdef DEBUG_MODEL
	g_print("browser_model_iter_has_child(%p)\n", iter->user_data);
#endif

	return (iter->user_data && ((GNode*)iter->user_data)->children) ||
	       !gq_browser_node_get_seen(iter->user_data2);
}

static gint
browser_model_iter_n_children(GtkTreeModel* model,
			      GtkTreeIter * iter)
{
	g_return_val_if_fail(gq_browser_model_validate(model, iter), 0);

	return g_node_n_children(iter->user_data);
}

static gboolean
browser_model_remove_dummy(gpointer user_data)
{
	gpointer* self_and_node = user_data;
	GtkTreeIter  iter;
	GtkTreePath* path;

	// prepare the tree data
	browser_model_get_iter_from_node(self_and_node[0], &iter, self_and_node[1]);
	path = gtk_tree_model_get_path(self_and_node[0], &iter);

	// remove the node
	g_object_unref(((GNode*)self_and_node[1])->data);
	((GNode*)self_and_node[1])->data = NULL;
	g_node_destroy(self_and_node[1]);

	// emit the signals
	gtk_tree_model_row_deleted(self_and_node[0], path);
	gtk_tree_path_up(path);
	g_return_val_if_fail(gtk_tree_model_get_iter(self_and_node[0], &iter, path), FALSE);
	gtk_tree_model_row_has_child_toggled(self_and_node[0], path, &iter);
	gtk_tree_path_free(path);

	return FALSE;
}

static gboolean
browser_model_iter_nth_child(GtkTreeModel* model,
			     GtkTreeIter * iter,
			     GtkTreeIter * parent,
			     gint          child)
{
	g_return_val_if_fail(!parent || browser_model_validate_iter(model, parent), FALSE);

	gq_main_loop_block();

#ifdef DEBUG_MODEL
	g_print("browser_model_iter_nth_child(%p, %p, %p, %d): ", model, iter, parent, child);
#endif

	if(parent && !gq_browser_node_get_seen(parent->user_data2)) {
		GqBrowserNode* node = gq_browser_model_get_node(model, parent);
		int ctxt = error_new_context(_("Expanding Subtree"), NULL);
		gq_main_loop_block();
#warning "FIXME: we don't have a tab here"
		gq_browser_node_expand(node, ctxt, model, parent, NULL/*tab*/);
		gq_main_loop_release();
#warning "FIXME: we don't have a tab here"
		// record_path(tab, node, model, parent);
		error_flush(ctxt);

		if(G_UNLIKELY(!gtk_tree_model_iter_n_children(model, parent))) {
			// add a dummy and delete it ASAP
			gpointer* self_and_node = g_new0(gpointer, 2);
			self_and_node[0] = model;
			self_and_node[1] = g_node_append_data(parent->user_data, gq_browser_node_dummy_new());
			g_idle_add_full(G_PRIORITY_HIGH, browser_model_remove_dummy, self_and_node, g_free);
			gq_browser_node_set_seen(parent->user_data2, TRUE);
		}
	}

	browser_model_get_iter_from_node(model, iter,
					 g_node_nth_child(parent ? parent->user_data : P(model)->tree_root, child));

#ifdef DEBUG_MODEL
	g_print("%s\n", browser_model_validate_iter(model, iter) ? "true" : "false");
#endif
	gq_main_loop_release();
	return browser_model_validate_iter(model, iter);
}

static gboolean
browser_model_iter_children(GtkTreeModel* model,
			    GtkTreeIter * iter,
			    GtkTreeIter * parent)
{
	return browser_model_iter_nth_child(model, iter, parent, 0);
}

static gboolean
browser_model_iter_next(GtkTreeModel* model,
			GtkTreeIter * iter)
{
	g_return_val_if_fail(browser_model_validate_iter(model, iter), FALSE);

#ifdef DEBUG_MODEL
	g_print("browser_model_iter_next(%p, %p): ", model, iter);
#endif

	browser_model_get_iter_from_node(model, iter, g_node_next_sibling(iter->user_data));

#ifdef DEBUG_MODEL
	g_print("%s\n", browser_model_validate_iter(model, iter) ? "true" : "false");
#endif
	return browser_model_validate_iter(model, iter);
}

static gboolean
browser_model_iter_parent(GtkTreeModel* model,
			  GtkTreeIter * iter,
			  GtkTreeIter * child)
{
	g_return_val_if_fail(browser_model_validate_iter(model, child), FALSE);

#ifdef DEBUG_MODEL
	g_print("browser_model_iter_parent(%p, %p, %p): ", model, iter, child);
#endif

	browser_model_get_iter_from_node(model, iter, ((GNode*)child->user_data)->parent);

#ifdef DEBUG_MODEL
	g_print("%s\n", browser_model_validate_iter(model, iter) ? "TRUE" : "FALSE");
#endif
	return browser_model_validate_iter(model, iter);
}

static void
browser_model_implement_tree(GtkTreeModelIface* iface)
{
	iface->get_n_columns   = browser_model_get_n_columns;
	iface->get_column_type = browser_model_get_column_type;
	iface->get_iter        = browser_model_get_iter;
	iface->get_path        = browser_model_get_path;
	iface->get_value       = browser_model_get_value;
	iface->iter_has_child  = browser_model_iter_has_child;
	iface->iter_n_children = browser_model_iter_n_children;
	iface->iter_nth_child  = browser_model_iter_nth_child;
	iface->iter_children   = browser_model_iter_children;
	iface->iter_next       = browser_model_iter_next;
	iface->iter_parent     = browser_model_iter_parent;
}

