// 26 june 2016
#include "uipriv_unix.h"
#include "table.h"

static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface);

G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT,
	G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init))

static void uiTableModel_init(uiTableModel *m)
{
	// nothing to do
}

static void uiTableModel_dispose(GObject *obj)
{
	G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj);
}

static void uiTableModel_finalize(GObject *obj)
{
	G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj);
}

static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm)
{
	return GTK_TREE_MODEL_LIST_ONLY;
}

static gint uiTableModel_get_n_columns(GtkTreeModel *mm)
{
	uiTableModel *m = uiTableModel(mm);

	return uiprivTableModelNumColumns(m);
}

static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index)
{
	uiTableModel *m = uiTableModel(mm);

	switch (uiprivTableModelColumnType(m, index)) {
	case uiTableValueTypeString:
		return G_TYPE_STRING;
	case uiTableValueTypeImage:
		return G_TYPE_POINTER;
	case uiTableValueTypeInt:
		return G_TYPE_INT;
	case uiTableValueTypeColor:
		return GDK_TYPE_RGBA;
	}
	// TODO
	return G_TYPE_INVALID;
}

#define STAMP_GOOD 0x1234
#define STAMP_BAD 0x5678

static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path)
{
	uiTableModel *m = uiTableModel(mm);
	gint row;

	if (gtk_tree_path_get_depth(path) != 1)
		goto bad;
	row = gtk_tree_path_get_indices(path)[0];
	if (row < 0)
		goto bad;
	if (row >= uiprivTableModelNumRows(m))
		goto bad;
	iter->stamp = STAMP_GOOD;
	iter->user_data = GINT_TO_POINTER(row);
	return TRUE;
bad:
	iter->stamp = STAMP_BAD;
	return FALSE;
}

// GtkListStore returns NULL on error; let's do that too
static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter  *iter)
{
	gint row;

	if (iter->stamp != STAMP_GOOD)
		return NULL;
	row = GPOINTER_TO_INT(iter->user_data);
	return gtk_tree_path_new_from_indices(row, -1);
}

// GtkListStore leaves value empty on failure; let's do the same
static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value)
{
	uiTableModel *m = uiTableModel(mm);
	gint row;
	uiTableValue *tvalue;
	double r, g, b, a;
	GdkRGBA rgba;

	if (iter->stamp != STAMP_GOOD)
		return;
	row = GPOINTER_TO_INT(iter->user_data);
	tvalue = uiprivTableModelCellValue(m, row, column);
	switch (uiprivTableModelColumnType(m, column)) {
	case uiTableValueTypeString:
		g_value_init(value, G_TYPE_STRING);
		g_value_set_string(value, uiTableValueString(tvalue));
		uiFreeTableValue(tvalue);
		return;
	case uiTableValueTypeImage:
		g_value_init(value, G_TYPE_POINTER);
		g_value_set_pointer(value, uiTableValueImage(tvalue));
		uiFreeTableValue(tvalue);
		return;
	case uiTableValueTypeInt:
		g_value_init(value, G_TYPE_INT);
		g_value_set_int(value, uiTableValueInt(tvalue));
		uiFreeTableValue(tvalue);
		return;
	case uiTableValueTypeColor:
		g_value_init(value, GDK_TYPE_RGBA);
		if (tvalue == NULL) {
			g_value_set_boxed(value, NULL);
			return;
		}
		uiTableValueColor(tvalue, &r, &g, &b, &a);
		uiFreeTableValue(tvalue);
		rgba.red = r;
		rgba.green = g;
		rgba.blue = b;
		rgba.alpha = a;
		g_value_set_boxed(value, &rgba);
		return;
	}
	// TODO
}

static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter)
{
	uiTableModel *m = uiTableModel(mm);
	gint row;

	if (iter->stamp != STAMP_GOOD)
		return FALSE;
	row = GPOINTER_TO_INT(iter->user_data);
	row++;
	if (row >= uiprivTableModelNumRows(m)) {
		iter->stamp = STAMP_BAD;
		return FALSE;
	}
	iter->user_data = GINT_TO_POINTER(row);
	return TRUE;
}

static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter)
{
	gint row;

	if (iter->stamp != STAMP_GOOD)
		return FALSE;
	row = GPOINTER_TO_INT(iter->user_data);
	row--;
	if (row < 0) {
		iter->stamp = STAMP_BAD;
		return FALSE;
	}
	iter->user_data = GINT_TO_POINTER(row);
	return TRUE;
}

static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent)
{
	return gtk_tree_model_iter_nth_child(mm, iter, parent, 0);
}

static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter)
{
	return FALSE;
}

static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter)
{
	uiTableModel *m = uiTableModel(mm);

	if (iter != NULL)
		return 0;
	return uiprivTableModelNumRows(m);
}

static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
{
	uiTableModel *m = uiTableModel(mm);

	if (iter->stamp != STAMP_GOOD)
		return FALSE;
	if (parent != NULL)
		goto bad;
	if (n < 0)
		goto bad;
	if (n >= uiprivTableModelNumRows(m))
		goto bad;
	iter->stamp = STAMP_GOOD;
	iter->user_data = GINT_TO_POINTER(n);
	return TRUE;
bad:
	iter->stamp = STAMP_BAD;
	return FALSE;
}

gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child)
{
	iter->stamp = STAMP_BAD;
	return FALSE;
}

static void uiTableModel_class_init(uiTableModelClass *class)
{
	G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose;
	G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize;
}

static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface)
{
	iface->get_flags = uiTableModel_get_flags;
	iface->get_n_columns = uiTableModel_get_n_columns;
	iface->get_column_type = uiTableModel_get_column_type;
	iface->get_iter = uiTableModel_get_iter;
	iface->get_path = uiTableModel_get_path;
	iface->get_value = uiTableModel_get_value;
	iface->iter_next = uiTableModel_iter_next;
	iface->iter_previous = uiTableModel_iter_previous;
	iface->iter_children = uiTableModel_iter_children;
	iface->iter_has_child = uiTableModel_iter_has_child;
	iface->iter_n_children = uiTableModel_iter_n_children;
	iface->iter_nth_child = uiTableModel_iter_nth_child;
	iface->iter_parent = uiTableModel_iter_parent;
	// don't specify ref_node() or unref_node()
}

uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
{
	uiTableModel *m;

	m = uiTableModel(g_object_new(uiTableModelType, NULL));
	m->mh = mh;
	return m;
}

void uiFreeTableModel(uiTableModel *m)
{
	g_object_unref(m);
}

void uiTableModelRowInserted(uiTableModel *m, int newIndex)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	path = gtk_tree_path_new_from_indices(newIndex, -1);
	iter.stamp = STAMP_GOOD;
	iter.user_data = GINT_TO_POINTER(newIndex);
	gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter);
	gtk_tree_path_free(path);
}

void uiTableModelRowChanged(uiTableModel *m, int index)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	path = gtk_tree_path_new_from_indices(index, -1);
	iter.stamp = STAMP_GOOD;
	iter.user_data = GINT_TO_POINTER(index);
	gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter);
	gtk_tree_path_free(path);
}

void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
	GtkTreePath *path;

	path = gtk_tree_path_new_from_indices(oldIndex, -1);
	gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path);
	gtk_tree_path_free(path);
}

uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m)
{
	return m->mh;
}