/** \file treeview.c
 * Basic treeview functionality for dropbox and listbox
 */

/* XTrkCad - Model Railroad CAD
 *
 * Copyright 2016 Martin Fischer <m_fischer@users.sf.net>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define GTK_DISABLE_SINGLE_INCLUDES
#define GDK_DISABLE_DEPRECATED
#define GTK_DISABLE_DEPRECATED
#define GSEAL_ENABLE

#include <glib-object.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>

#include "gtkint.h"
//#include "i18n.h"

#define ROW_HEIGHT (15)

/**
 * Get the count of columns in list
 *
 * \param b IN widget
 * \returns count row
 */

int
wTreeViewGetCount(wList_p b)
{
	return b->count;
}

/**
 * Clear the list
 *
 * \param b IN widget
 */

void
wTreeViewClear(wList_p b)
{
	assert(b != NULL);

	wlibListStoreClear(b->listStore);
}

/**
 * Get the user data for a list element
 *
 * \param b IN widget
 * \param inx IN row
 * \returns the user data for the specified row
 */

void *
wTreeViewGetItemContext(wList_p b, int row)
{
	wListItem_p id_p;

	id_p = wlibListItemGet(b->listStore, row, NULL);

	if (id_p) {
		return id_p->itemData;
	} else {
		return NULL;
	}
}

/**
 * Returns the current selected list entry.
 * If <val> if '-1' then no entry is selected.
 *
 * \param b IN widget
 * \returns row of selected entry or -1 if none is selected
 */

wIndex_t wListGetIndex(
        wList_p b)
{
	assert(b!=NULL);

	return b->last;
}

/**
 * Set an entry in the list to selected.
 *
 * \param b IN widget
 * \param index IN entry if -1 the current selection is cleared
 *
 */

void
wlibTreeViewSetSelected(wList_p b, int index)
{
	GtkTreeSelection *sel;
	GtkTreeIter iter;

	wListItem_p id_p;

	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(b->treeView));

	if (gtk_tree_selection_count_selected_rows(sel)) {
		int inx;

		gtk_tree_selection_unselect_all(sel);

		// and synchronize the internal data structures
		wTreeViewGetCount(b);

		for (inx=0; inx<b->count; inx++) {
			id_p = wlibListItemGet(b->listStore, inx, NULL);
			id_p->selected = FALSE;
		}
	}

	if (index != -1) {
		gint childs;

		childs = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(b->listStore),
		                NULL );

		if(index < childs) {
			gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(b->listStore),
			                              &iter,
			                              NULL,
			                              index);
			gtk_tree_selection_select_iter(sel,
			                               &iter);

			id_p = wlibListItemGet(b->listStore, index, NULL);

			if (id_p) {
				id_p->selected = TRUE;
			}
		}
	}
}

/**
 * Create a new tree view for a list store. Titles are enabled optionally.
 *
 * \param ls IN list store
 * \param showTitles IN add column titles
 * \param multiSelection IN enable selecting multiple rows
 * \returns
 */

GtkWidget *
wlibNewTreeView(GtkListStore *ls, int showTitles, int multiSelection)
{
	GtkWidget *treeView;
	GtkTreeSelection *sel;
	assert(ls != NULL);

	/* create and configure the tree view */
	treeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls));
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeView), showTitles);

	/* set up selection handling */
	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
	gtk_tree_selection_set_mode(sel,
	                            (multiSelection)?GTK_SELECTION_MULTIPLE:GTK_SELECTION_BROWSE);

	return (treeView);
}


#if 0
static int changeListColumnWidth(
        GtkTreeViewColumn * column,
        void * width)
{
	//wList_p bl = (wList_p)data;

	//if (bl->recursion)
	//return 0;
	//if ( col >= 0 && col < bl->colCnt )
	//bl->colWidths[col] = width;
	return 0;
}
#endif

/**
 * Create and initialize a column in treeview. Initially all columns are
 * invisible. Visibility is set when values are added to the specific
 * column
 *
 * \param tv IN treeview
 * \param renderer IN renderer to use
 * \param attribute IN attribute for column
 * \param value IN value to set
 */

static void
wlibAddColumn(GtkWidget *tv, int visibility, GtkCellRenderer *renderer,
              char *attribute, int value)
{
	GtkTreeViewColumn *column;

	column = gtk_tree_view_column_new();
	gtk_tree_view_column_pack_start(column,
	                                renderer,
	                                TRUE);
	gtk_tree_view_column_add_attribute(column, renderer, attribute, value);
	gtk_tree_view_column_set_visible(column, visibility);
	gtk_tree_view_column_set_resizable(column, TRUE);

	gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);

//	g_signal_connect( column, "notify::width", G_CALLBACK(changeListColumnWidth), tv );
}

/**
 * Add a number of columns to the text view. This includes the bitmap
 * columns and a given number of text columns.
 *
 * \param tv IN tree view
 * \param count IN number of text columns
 * \returns number of columns
 */

int
wlibTreeViewAddColumns(GtkWidget *tv, int count)
{
	GtkCellRenderer *renderer;
	int i;

	assert(tv != NULL);
	renderer = gtk_cell_renderer_pixbuf_new();
	/* first visible column is used for bitmaps */
	wlibAddColumn(tv, FALSE, renderer, "pixbuf", LISTCOL_BITMAP);

	renderer = gtk_cell_renderer_text_new();

	/* add renderers to all columns */
	for (i = 0; i < count; i++) {
		wlibAddColumn(tv, TRUE, renderer, "text", i + LISTCOL_TEXT);
	}

	return i;
}

/**
 * Add the titles to all columns in a tree view.
 *
 * \param tv IN treeview
 * \param titles IN titles
 * \returns number of titles set
 */

int
wlibAddColumnTitles(GtkWidget *tv, const char **titles)
{
	int i = 0;

	assert(tv != NULL);

	if (titles) {
		while (*titles) {
			GtkTreeViewColumn *column;

			column = gtk_tree_view_get_column(GTK_TREE_VIEW(tv), i + 1);

			if (column) {
				gtk_tree_view_column_set_title(column, titles[ i ]);
				i++;
			} else {
				break;
			}
		}
	}

	return i;
}

/**
 * Add text to the text columns of the tree view and update the context
 * information
 *
 * \param tv IN treeview
 * \param cols IN number of cols to change
 * \param label IN tab separated string of values
 * \param userData IN additional context information
 * \returns
 */

int
wlibTreeViewAddData(GtkWidget *tv, int cols, char *label, GdkPixbuf *pixbuf,
                    wListItem_p userData)
{
	GtkListStore *listStore = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(
	                                  tv)));

	wlibListStoreAddData(listStore, pixbuf, cols, userData);

	if (pixbuf) {
		GtkTreeViewColumn *column;

		// first column in list store has pixbuf
		column = gtk_tree_view_get_column(GTK_TREE_VIEW(tv), 0);
		gtk_tree_view_column_set_visible(column,
		                                 TRUE);
	}
	return 0;

}

/**
 * Add a row to the tree view. As necessary the adjustment is update in
 * order to make sure, that the list box is fully visible or has a
 * scrollbar.
 *
 * \param b IN the list box
 * \param label IN the text labels
 * \param bm IN bitmap to show at start
 * \param id_p IN user data
 */

void
wlibTreeViewAddRow(wList_p b, char *label, wIcon_p bm, wListItem_p id_p)
{
	GtkAdjustment *adj;
	GdkPixbuf *pixbuf = NULL;

	if (bm) {
		pixbuf = wlibMakePixbuf(bm);
	}

	wlibTreeViewAddData(b->treeView, b->colCnt, (char *)label, pixbuf, id_p);

	adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(b->widget));

	if (gtk_adjustment_get_upper(adj) < gtk_adjustment_get_step_increment(adj) *
	    (b->count+1)) {
		gtk_adjustment_set_upper(adj,
		                         gtk_adjustment_get_upper(adj) +
		                         gtk_adjustment_get_step_increment(adj));
		gtk_adjustment_changed(adj);
	}

	b->last = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(
	                        b->treeView)),
	                NULL);

}

/**
 * Function for handling a selection change. The internal data structure
 * for the changed row is updated. If a handler function for the list
 * is given, the data for the row are retrieved and passed to that
 * function. This is used to update other fields in a dialog (see Price
 * List for an example).
 *
 * \param selection IN current selection
 * \param model IN
 * \param path IN
 * \param path_currently_selected IN
 * \param data IN the list widget
 */

gboolean
changeSelection(GtkTreeSelection *selection,
                GtkTreeModel *model,
                GtkTreePath *path,
                gboolean path_currently_selected,
                gpointer data)
{
	GtkTreeIter iter;
	GValue value = { 0 };
	wListItem_p id_p = NULL;
	wList_p bl = (wList_p)data;
	int row;
	char *text;

	text = gtk_tree_path_to_string(path);
	row = atoi(text);
	g_free(text);

	gtk_tree_model_get_iter(model, &iter, path);
	gtk_tree_model_get_value(model, &iter, LISTCOL_DATA, &value);

	id_p = g_value_get_pointer(&value);
	id_p->selected = !path_currently_selected;

	bl->last = row;

	if (bl->valueP) {
		*bl->valueP = row;
	}

	if (bl->action) {
		bl->action(row, id_p->label, id_p->selected, bl->data, id_p->itemData);
	}

	return TRUE;
}

/**
 * Create a multi column list box.
 *
 * \param parent IN parent window
 * \param x IN X-position
 * \param y IN Y-position
 * \param helpStr IN Help string
 * \param labelStr IN Label
 * \param option IN Options
 * \param number IN Number of displayed entries
 * \param width IN Width of list
 * \param colCnt IN Number of columns
 * \param colWidths IN Width of columns
 * \param colRightJust IN justification of columns
 * \param colTitles IN array of titles for columns
 * \param valueP IN Selected index
 * \param action IN Callback
 * \param data IN Context
 * \returns created list box
 */

wList_p wListCreate(
        wWin_p	parent,
        wWinPix_t	x,
        wWinPix_t	y,
        const char 	* helpStr,
        const char	* labelStr,
        long	option,
        long	number,
        wWinPix_t	width,
        int	colCnt,
        wWinPix_t	* colWidths,
        wBool_t * colRightJust,
        const char 	** colTitles,
        long	*valueP,
        wListCallBack_p action,
        void 	*data)
{
	GtkTreeSelection *sel;
	wList_p bl;
	static wWinPix_t zeroPos = 0;

	assert(width != 0);

	bl = (wList_p)wlibAlloc(parent, B_LIST, x, y, labelStr, sizeof *bl, data);
	bl->option = option;
	bl->number = number;
	bl->count = 0;
	bl->last = -1;
	bl->valueP = valueP;
	bl->action = action;
	bl->listX = bl->realX;

	if (colCnt <= 0) {
		colCnt = 1;
		colWidths = &zeroPos;
	}

	bl->colCnt = colCnt;
	bl->colWidths = (wWinPix_t*)malloc(colCnt * sizeof *(wWinPix_t*)0);
	memcpy(bl->colWidths, colWidths, colCnt * sizeof *(wWinPix_t*)0);

	/* create the data structure for data */
	bl->listStore = wlibNewListStore(colCnt);
	/* create the widget for the list store */
	bl->treeView = wlibNewTreeView(bl->listStore,
	                               colTitles != NULL,
	                               option & BL_MANY);


	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bl->treeView));

	gtk_tree_selection_set_select_function(sel,
	                                       changeSelection,
	                                       bl,
	                                       NULL);

	wlibTreeViewAddColumns(bl->treeView, colCnt);

	wlibAddColumnTitles(bl->treeView, colTitles);

	wlibComputePos((wControl_p)bl);

	bl->widget = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(bl->widget),
	                               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(bl->widget),
	                                      bl->treeView);

	gtk_widget_set_size_request(bl->widget, width, (number+1)*ROW_HEIGHT);

///	g_signal_connect( GTK_OBJECT(bl->list), "resize_column", G_CALLBACK(changeListColumnWidth), bl );

	gtk_widget_show_all(bl->widget);

	gtk_fixed_put(GTK_FIXED(parent->widget), bl->widget, bl->realX, bl->realY);
	wlibControlGetSize((wControl_p)bl);

	if (labelStr) {
		bl->labelW = wlibAddLabel((wControl_p)bl, labelStr);
	}

	wlibAddButton((wControl_p)bl);
	wlibAddHelpString(bl->widget, helpStr);

	return bl;
}
