/*
 *  Copyright (C) 2004 Mathias Andre <mathias@openbrookes.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU 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.
 *
 */

#ifdef HAVE_CONFIG_H
        #include <config.h>
#endif

#include <glib.h>
#include <gtk/gtk.h>
#include <glade/glade.h>

#include "../nb_note.h"
#include "../nb_note_list.h"
#include "../nb_global.h"
#include "../nb_util.h"
#include "nb_ui_note_view.h"
#include "nb_ui_interface.h"
#include "nb_ui_statusbar.h"
#include "nb_ui_treeview.h"

#define _(text) gettext(text)

/* callbacks */
static void	nb_ui_treeview_cb_update_global_with_selection (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer user_data);
static gboolean	nb_ui_treeview_cb_row_matches_query (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data);
static gboolean	nb_ui_treeview_cb_row_matches_note (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data);

/* column ordering callbacks */
static gint 	nb_ui_treeview_cb_sort_iter_compare_string (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer userdata);
static gint	nb_ui_treeview_cb_sort_iter_compare_date (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer userdata);

/* private functions */
static void	nb_ui_treeview_clear (void);

void
nb_ui_treeview_create (void)
{
	extern GladeXML * xml;
	extern Global * g;
	GtkWidget * tree;
	GtkTreeSelection * selection;
	GtkTreeViewColumn * column;
	GtkCellRenderer * renderer;

	tree = glade_xml_get_widget (xml, "treeview");
	gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (g->store));

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ((gchar *) _("Title"), renderer,
							    "text", TITLE_COLUMN,
							    NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, TITLE_COLUMN);
	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ((gchar *) _("Type"), renderer,
							   "text", TYPE_COLUMN,
							   NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, TYPE_COLUMN);
	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ((gchar *) _("Status"), renderer,
						 	   "text", STATUS_COLUMN,
							   NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, STATUS_COLUMN);
	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ((gchar *) _("Date"), renderer,
							   "text", DATE_COLUMN,
							   NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_column_set_sort_indicator (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, DATE_COLUMN);
	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ((gchar *) _("Last update"), renderer,
							   "text", LAST_UPDATE_COLUMN,
							   NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, LAST_UPDATE_COLUMN);
	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
	g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (nb_ui_treeview_cb_selection_changed), NULL);
}

void
nb_ui_treeview_set_sort_func (GtkListStore * store)
{
	GtkTreeSortable * sortable;

	sortable = GTK_TREE_SORTABLE (store);

	gtk_tree_sortable_set_sort_func (sortable, TITLE_COLUMN, nb_ui_treeview_cb_sort_iter_compare_string, GINT_TO_POINTER (TITLE_COLUMN), NULL);
	gtk_tree_sortable_set_sort_func (sortable, TYPE_COLUMN, nb_ui_treeview_cb_sort_iter_compare_string, GINT_TO_POINTER (TYPE_COLUMN), NULL);
	gtk_tree_sortable_set_sort_func (sortable, STATUS_COLUMN, nb_ui_treeview_cb_sort_iter_compare_string, GINT_TO_POINTER (STATUS_COLUMN), NULL);
	gtk_tree_sortable_set_sort_func (sortable, DATE_COLUMN, nb_ui_treeview_cb_sort_iter_compare_date, GINT_TO_POINTER (DATE_COLUMN), NULL);
	gtk_tree_sortable_set_sort_func (sortable, LAST_UPDATE_COLUMN, nb_ui_treeview_cb_sort_iter_compare_date, GINT_TO_POINTER (LAST_UPDATE_COLUMN), NULL);
}

/* update tree */
GtkTreeIter *
nb_ui_treeview_add_note (gchar * title, gchar * type, gchar * status, gchar * date, gchar * update, Note * n, gboolean scroll)
{
	extern Global * g;
	extern GladeXML * xml;
	GtkTreeIter * iter = g_malloc (sizeof (GtkTreeIter));
	gsize bytes_written;
	GtkWidget * treeview = glade_xml_get_widget (xml, "treeview");

	/* Acquire an iterator */
	gtk_list_store_append ( GTK_LIST_STORE (g->store) , iter);

	/* convert the strings to utf8 and insert them */
	gtk_list_store_set ( GTK_LIST_STORE (g->store) , iter,
			     TITLE_COLUMN, g_locale_to_utf8 (title, -1, NULL, &bytes_written, NULL),
			     TYPE_COLUMN, g_locale_to_utf8 (type, -1, NULL, &bytes_written, NULL),
			     STATUS_COLUMN, g_locale_to_utf8 (status, -1, NULL, &bytes_written, NULL),
			     DATE_COLUMN, g_locale_to_utf8 (date, -1, NULL, &bytes_written, NULL),
			     LAST_UPDATE_COLUMN, g_locale_to_utf8 (update, -1, NULL, &bytes_written, NULL),
			     NOTE_POINTER_COLUMN, n,
			     -1);

	if ( scroll )
	{
		/* scroll the added note */
		gtk_tree_view_scroll_to_cell ( GTK_TREE_VIEW (treeview),
				gtk_tree_model_get_path ( gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)), iter),
				NULL, FALSE, 0.0, 0.0);
	}

	return iter; 
}

void
nb_ui_treeview_update_note (gchar * title, gchar * type, gchar * status, gchar * last_update, Note * n)
{
	extern Global * g;
	extern GladeXML * xml;
	GtkTreeIter iter;
	GtkWidget * treeview = glade_xml_get_widget (xml, "treeview");
	GtkTreeSelection * selection;
	gsize bytes_written;
	GList  * rows;
	NoteMatch parm;

	parm.n = n;
	parm.iter = NULL;

	gtk_tree_model_foreach ( GTK_TREE_MODEL (g->store), nb_ui_treeview_cb_row_matches_note, &parm);

	gtk_list_store_set (GTK_LIST_STORE (g->store), &iter,
			    TITLE_COLUMN, g_locale_to_utf8 (title, -1, NULL, &bytes_written, NULL),
			    TYPE_COLUMN, g_locale_to_utf8 (type, -1, NULL, &bytes_written, NULL),
			    STATUS_COLUMN, g_locale_to_utf8 (status, -1, NULL, &bytes_written, NULL),
			    LAST_UPDATE_COLUMN, g_locale_to_utf8 (last_update, -1, NULL, &bytes_written, NULL),
			    -1);
}

void
nb_ui_treeview_update (gchar * query)
{
	static gchar * old_query = NULL;
	GtkTreeIter * last_added_iter;
	extern Global * g;
	Note * n;
	gint i;

	if ( query != NULL )
		query = g_utf8_casefold (query, -1);

	if ( (query != NULL) && (old_query != NULL) )
	{
		/* if the new old query is part of the new one, then we only need to check the currently displayed notes */
		if ( g_strrstr (query, old_query) != NULL )
		{
			GList * node;
			RemoveCbArgs parm;
			parm.rr_list = NULL;
			parm.query = query;

			gtk_tree_model_foreach ( GTK_TREE_MODEL (g->store), nb_ui_treeview_cb_row_matches_query, &parm);

			for ( node = parm.rr_list;  node != NULL;  node = node->next )
			{
				GtkTreePath * path;
				path = gtk_tree_row_reference_get_path ( (GtkTreeRowReference *) node->data);

				if ( path )
				{
					GtkTreeIter iter;

					if ( gtk_tree_model_get_iter ( GTK_TREE_MODEL (g->store), &iter, path))
					{
						gtk_list_store_remove (g->store, &iter);
					}
				}
			}

			old_query = query;

			return ;
		}
	}

	nb_ui_treeview_clear ();

	for ( i = 0 ; i < nb_note_list_get_nb (g->nl) ; i++ )
	{
		n = g_ptr_array_index (g->nl->notes, i);

		if ( query != NULL )
			nb_note_set_text (n, nb_note_list_get_note_text_from_index (g->nl, i));

		if ( (query == NULL) || (nb_note_matches_query (n, query)) )
		{
			gchar * type = (gchar *) _(note_type_string [n->type]);
			gchar * status = (gchar *) _(note_status_string [n->status]);
			gchar * date = (gchar *) nb_util_get_date_string (n->date);
			gchar * update = (gchar *) nb_util_get_date_string (n->update);

			last_added_iter = nb_ui_treeview_add_note (n->title,
						 		   type, status,
								   date, update,
								   n,
								   TRUE);

			g_free (date);
			g_free (update);
		}

		nb_note_set_text (n, NULL);
	}

	old_query = query;

		nb_ui_treeview_select_iter (last_added_iter);
}

GtkTreeSelection * 
nb_ui_treeview_get_selection (void)
{
	extern GladeXML * xml;
	GtkWidget * treeview = glade_xml_get_widget (xml, "treeview");

	return gtk_tree_view_get_selection ( GTK_TREE_VIEW (treeview));
}

void
nb_ui_treeview_select_iter (GtkTreeIter * iter)
{
	extern GladeXML * xml;
	GtkTreeModel * model;
	GtkTreeSelection * selection;
	GtkWidget * treeview = glade_xml_get_widget (xml, "treeview");

	if ( iter == NULL )
		return ;

	model = gtk_tree_view_get_model ( GTK_TREE_VIEW (treeview));
	selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (treeview));

	gtk_tree_selection_unselect_all (selection);
	gtk_tree_selection_select_iter (selection, iter);
}

/* public callbacks */
void
nb_ui_treeview_cb_selection_changed (GtkTreeSelection * selection, gpointer user_data)
{
	extern Global * g;
	gint count;
	gboolean same_note = FALSE;

	count = gtk_tree_selection_count_selected_rows (selection);

	if ( count > 1 )
		return;

	if ( g->modified_note == EDITED )
	{
		if ( (g->inserted_note == INSERTED) || !nb_ui_note_view_is_empty () )
		{
			if ( nb_ui_dialog_question ((gchar *) _("Do you want to save note?"), (gchar *) _("Save note?")) )
			{
				if ( nb_global_save_note (g) )
					nb_ui_statusbar_push ((gchar *) _("Successfully saved note to file."));
				else
					return ;
			}
		}
	}

	gtk_tree_selection_selected_foreach (selection, (GtkTreeSelectionForeachFunc) nb_ui_treeview_cb_update_global_with_selection, (void *) same_note);

	if ( same_note )
		return ;

	if ( nb_note_get_nb_attachments (g->current_note) > 0 )
		nb_ui_attachment_view_show ();
	else
		nb_ui_attachment_view_hide();

	nb_global_check_attachments (g);

	nb_ui_note_view_set_fields (g->current_note->title,
				    g->current_note->text,
				    g->current_note->type,
				    g->current_note->status,
				    nb_util_get_date_string (g->current_note->date),
				    g->current_note->attachments);

	g->inserted_note = INSERTED;
	g->modified_note = CLEAN;
}

/* callbacks */
static void
nb_ui_treeview_cb_update_global_with_selection (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer user_data)
{
	extern Global * g;
	Note * n;
	gint index;
	gboolean same_note = (gboolean) user_data;

	gtk_tree_model_get (model, iter, NOTE_POINTER_COLUMN, &n, -1);

	if ( n == g->current_note )
	{
		same_note = TRUE;
		return ;
	}
	else
		nb_note_clear_text (g->current_note);

	g->current_note = n;

	index = nb_note_list_get_note_index (g->nl, g->current_note);

	if ( index == -1 )
	{
		nb_note_set_text (g->current_note, NULL);
		fprintf (stderr, "Invalid note! Note is not present in the array!\n");
	}
	else
		nb_note_set_text (g->current_note, nb_note_list_get_note_text_from_index (g->nl, index));
}

static gboolean	
nb_ui_treeview_cb_row_matches_query (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data)
{
	Note * n;
	RemoveCbArgs * parm = (RemoveCbArgs *) data;

	gtk_tree_model_get ( GTK_TREE_MODEL (model), iter, NOTE_POINTER_COLUMN, &n, -1);

	if ( !nb_note_matches_query (n, (gchar *) parm->query) )
	{
		GtkTreeRowReference  * rowref;

		rowref = gtk_tree_row_reference_new (model, path);
		parm->rr_list = g_list_append (parm->rr_list, rowref);
	}

	return FALSE;
}

static gboolean	
nb_ui_treeview_cb_row_matches_note (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data)
{
	Note * n;
	NoteMatch * parm = (NoteMatch *) data;

	gtk_tree_model_get ( GTK_TREE_MODEL (model), iter, NOTE_POINTER_COLUMN, &n, -1);

	if ( n == parm->n )
	{
		parm->iter = iter;
		return TRUE;
	}
	
	return FALSE;
}

/* column ordering callbacks */
static gint
nb_ui_treeview_cb_sort_iter_compare_string (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer userdata)
{
	gint column = GPOINTER_TO_INT (userdata);
	gchar * val1, * val2;
	gint ret;

	switch (column)
	{
		case TITLE_COLUMN:
			{
				gtk_tree_model_get (model, a, TITLE_COLUMN, &val1, -1);
				gtk_tree_model_get (model, b, TITLE_COLUMN, &val2, -1);
				break;
			}

		case TYPE_COLUMN:
			{
				gtk_tree_model_get (model, a, TYPE_COLUMN, &val1, -1);
				gtk_tree_model_get (model, b, TYPE_COLUMN, &val2, -1);
				break;
			}

		case STATUS_COLUMN:
			{
				gtk_tree_model_get (model, a, STATUS_COLUMN, &val1, -1);
				gtk_tree_model_get (model, b, STATUS_COLUMN, &val2, -1);
				break;
			}
	}

	if ( (val1 == NULL) || (val2 == NULL) )
	{
		if ( val1 == NULL )
		{
			if ( val2 == NULL )
				ret = 0;
			else
				ret = -1;
		}
		else
			ret = 1;
	}
	else
	{
		ret = g_utf8_collate (val1,val2);
	}

	g_free (val1);
	g_free (val2);

	return ret;
}

static gint
nb_ui_treeview_cb_sort_iter_compare_date (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer userdata)
{
	gint column = GPOINTER_TO_INT (userdata);
	Note * n1, * n2;
	gint ret;

	gtk_tree_model_get (model, a, NOTE_POINTER_COLUMN, &n1, -1);
	gtk_tree_model_get (model, b, NOTE_POINTER_COLUMN, &n2, -1);

	if ( column == DATE_COLUMN )
		ret = g_date_compare (n1->date, n2->date);
	else
		ret = g_date_compare (n1->update, n2->update);

	return ret;
}

/* private functions */
static void
nb_ui_treeview_clear (void)
{
	extern Global * g;

	gtk_list_store_clear (g->store);
}
