/**************************************************************

cmpack_graph_data.cpp (C-Munipack project)
Object which holds data for a graph
Copyright (C) 2011 David Motl, dmotl@volny.cz

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.

$Id: cmpack_graph_data.cpp,v 1.3 2016/06/02 11:34:30 dmotl Exp $

**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cmpack_graph_data.h"

enum {
	DATA_UPDATED,
	LAST_SIGNAL
}; 

/*--------------------   GRAPH DATA CLASS   ----------------------------*/

G_DEFINE_TYPE(CmpackGraphData, cmpack_graph_data, G_TYPE_OBJECT)

static guint cmpack_graph_data_signals[LAST_SIGNAL] = { 0 }; 

static void cmpack_graph_data_finalize(GObject *object);

static void cmpack_graph_data_marshal_VOID__INT_INT_INT_INT(GClosure     *closure,
                                  GValue       *return_value G_GNUC_UNUSED,
                                  guint         n_param_values,
                                  const GValue *param_values,
                                  gpointer      invocation_hint G_GNUC_UNUSED,
                                  gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__INT_INT_INT_INT) (gpointer     data1,
                                           gint         arg_1,
										   gint			arg_2,
										   gint			arg_3,
										   gint			arg_4,
                                           gpointer     data2);
  register GMarshalFunc_VOID__INT_INT_INT_INT callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 5);

  if (G_CCLOSURE_SWAP_DATA (closure)) {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
  } else {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
  }
  callback = (GMarshalFunc_VOID__INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
  callback (data1, g_value_get_int (param_values + 1), g_value_get_int (param_values + 2), 
	  g_value_get_int (param_values + 3), g_value_get_int (param_values + 4), data2);
}

/* Class initialization */
static void cmpack_graph_data_class_init(CmpackGraphDataClass *klass)
{
    GObjectClass *object_class = (GObjectClass *)klass;

    /* Override object destroy */
	object_class->finalize = cmpack_graph_data_finalize;

	/* Signals */
	cmpack_graph_data_signals[DATA_UPDATED] = g_signal_new("data-updated",
		  G_TYPE_FROM_CLASS(object_class), 
		  GSignalFlags(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
		  G_STRUCT_OFFSET (CmpackGraphDataClass, data_updated), NULL, NULL, 
		  cmpack_graph_data_marshal_VOID__INT_INT_INT_INT, G_TYPE_NONE, 4, 
		  GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
}

/* Instance initialization */
static void cmpack_graph_data_init(CmpackGraphData *data)
{
}

/* Create graph widget */
CmpackGraphData *cmpack_graph_data_new(gint ncol, gint nrow)
{
	CmpackGraphData *res = (CmpackGraphData*)g_object_new(CMPACK_TYPE_GRAPH_DATA, NULL);
	if (ncol>0 && nrow>0) {
		res->ncol = ncol;
		res->nrow = nrow;
		res->items = (CmpackGraphItem**)g_malloc0(ncol*nrow*sizeof(CmpackGraphItem*));
		res->cols  = (CmpackGraphColumn**)g_malloc0(ncol*sizeof(CmpackGraphColumn*));
	}
	return res;
}

/* Free allocated memory */
static void cmpack_graph_data_finalize(GObject *object)
{
	gint i;
	CmpackGraphData *data = CMPACK_GRAPH_DATA(object);

	/* Free allocated data */
	for (i=0; i<data->nrow*data->ncol; i++) {
		if (data->items[i]) {
			g_free(data->items[i]->tag);
			g_free(data->items[i]);
		}
	}
	g_free(data->items);
	data->items = NULL;
	for (i=0; i<data->ncol; i++) 
		g_free(data->cols[i]);
	data->cols = NULL;
	data->nrow = data->ncol = 0;

    /* Chain up to the parent class */
    G_OBJECT_CLASS(cmpack_graph_data_parent_class)->finalize(object);
}

/* Clear all data */
void cmpack_graph_data_clear(CmpackGraphData *data)
{
	gint i, rmin, rmax, cmin, cmax;

	g_return_if_fail(data != NULL);

	/* Free allocated data */
	rmin = rmax = cmin = cmax = -1;
	for (i=0; i<data->nrow*data->ncol; i++) {
		if (data->items[i]) {
			g_free(data->items[i]->tag);
			g_free(data->items[i]);
			data->items[i] = NULL;
			gint c = (i % data->ncol), r = (i / data->ncol);
			if (rmin >= 0) {
				if (c < cmin)
					cmin = c;
				if (c > cmax)
					cmax = c;
				if (r < rmin)
					rmin = r;
				if (r > rmax)
					rmax = r;
			} else {
				cmin = cmax = c;
				rmin = rmax = r;
			}
		}
	}
	if (rmin>=0) 
		g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, cmin, cmax, rmin, rmax);
}

/* Add new data to the graph */
void cmpack_graph_data_set(CmpackGraphData *data, gint col, gint row, const CmpackGraphItem *d, gsize item_size)
{
	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && row>=0 && col<data->ncol && row<data->nrow);

	gint index = col + row*data->ncol;
	if (data->items[index]) {
		g_free(data->items[index]->tag);
		g_free(data->items[index]);
		data->items[index] = NULL;
	}
	if (d && item_size>0) {
		CmpackGraphItem *item = (CmpackGraphItem*)g_malloc0(sizeof(CmpackGraphItem));
		memcpy(item, d, MIN(sizeof(CmpackGraphItem), item_size));
		item->tag = (d->tag!=NULL ? g_strdup(d->tag) : NULL);
		data->items[index] = item;
	}
	
	g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, row, row);
}

/* Set row tag */
void cmpack_graph_data_set_tag(CmpackGraphData *data, gint col, gint row, const gchar *text)
{
	CmpackGraphItem *item;

	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && row>=0 && col<data->ncol && row<data->nrow);

	gint index = col + row*data->ncol;
	item = data->items[index];
	if (item) {
		if (text) {
			if (!item->tag || strcmp(text, item->tag)!=0) {
				g_free(item->tag); 
				item->tag = g_strdup(text);
				g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, row, row);
			}
		} else {
			if (item->tag) {
				g_free(item->tag); 
				item->tag = NULL;
				g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, row, row);
			}
		}
	}
}

/* Set topmost flag */
void cmpack_graph_data_set_topmost(CmpackGraphData *data, gint col, gint row, gboolean topmost)
{
	CmpackGraphItem *item;

	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && row>=0 && col<data->ncol && row<data->nrow);

	gint index = col + row*data->ncol;
	item = data->items[index];
	if (item && item->topmost != topmost) {
		item->topmost = topmost;
		g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, row, row);
	}
}

/* Set row color */
void cmpack_graph_data_set_color(CmpackGraphData *data, gint col, gint row, CmpackColor color)
{
	CmpackGraphItem *item;

	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && row>=0 && col<data->ncol && row<data->nrow);

	gint index = col + row*data->ncol;
	item = data->items[index];
	if (item && item->color!=color) {
		item->color = color;
		g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, row, row);
	}
}

/* Clear all tags */
void cmpack_graph_data_clear_tags(CmpackGraphData *data)
{
	gint i, rmin, rmax, cmin, cmax;

	g_return_if_fail(data != NULL);

	rmin = rmax = cmin = cmax = -1;
	for (i=0; i<data->nrow*data->ncol; i++) {
		CmpackGraphItem *item = data->items[i];
		if (item && item->tag) {
			g_free(item->tag); 
			item->tag = NULL;
			gint c = (i % data->ncol), r = (i / data->ncol);
			if (rmin >= 0) {
				if (c < cmin)
					cmin = c;
				if (c > cmax)
					cmax = c;
				if (r < rmin)
					rmin = r;
				if (r > rmax)
					rmax = r;
			} else {
				cmin = cmax = c;
				rmin = rmax = r;
			}
		}
	}
	if (rmin>=0) 
		g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, cmin, cmax, rmin, rmax);
}

/* Get row tag */
intptr_t cmpack_graph_data_get_param(CmpackGraphData *data, gint col, gint row)
{
	CmpackGraphItem *item;

	g_return_val_if_fail(data != NULL, 0);
	g_return_val_if_fail(col>=0 && row>=0 && col<data->ncol && row<data->nrow, 0);

	gint index = col + row*data->ncol;
	item = data->items[index];
	if (item)
		return item->param;
	return 0;
}

/* Get number of measurements */
gint cmpack_graph_data_nrow(CmpackGraphData *data)
{
	g_return_val_if_fail(data!=NULL, 0);

	return data->nrow; 
}

/* Get number of measurements */
gint cmpack_graph_data_ncol(CmpackGraphData *data)
{
	g_return_val_if_fail(data!=NULL, 0);

	return data->ncol; 
}

/* Get single value */
gboolean cmpack_graph_data_get_item(CmpackGraphData *data, gint col, gint row, CmpackGraphItem *pItem, gsize item_size)
{
	g_return_val_if_fail(data != NULL, FALSE);
	g_return_val_if_fail(col>=0 && row>=0 && col<data->ncol && row<data->nrow, FALSE);

	CmpackGraphItem *item = data->items[col + row*data->ncol];
	if (item) {
		CmpackGraphItem tmp;
		memcpy(&tmp, data->items[col + row*data->ncol], sizeof(CmpackGraphItem));
		if (data->cols[col]) {
			if (tmp.color == CMPACK_COLOR_DEFAULT)
				tmp.color = data->cols[col]->color;
			tmp.hidden = tmp.hidden || data->cols[col]->hidden;
			tmp.outline = tmp.outline || data->cols[col]->outline;
			tmp.topmost = tmp.topmost || data->cols[col]->topmost;
			tmp.disabled = tmp.disabled || data->cols[col]->disabled;
		}
		memcpy(pItem, &tmp, item_size);
	} else {
		memset(pItem, 0, item_size);
	}
	return TRUE;
}

/* Find object by its identifier */
gboolean cmpack_graph_data_find_item(CmpackGraphData *data, intptr_t param, gint *col, gint *row)
{
	gint i;

	g_return_val_if_fail(data != NULL, FALSE);

	for (i=0; i<data->nrow*data->ncol; i++) {
		CmpackGraphItem *item = data->items[i];
		if (item && item->param == param) {
			if (col)
				*col = (i % data->ncol);
			if (row)
				*row = (i / data->ncol);
			return TRUE;
		}
	}
	return FALSE;
}

/* Set default topmost flag for entire dataset */
void cmpack_graph_data_set_dataset_topmost(CmpackGraphData *data, gint col, gboolean topmost)
{
	int rmin, rmax;

	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && col<data->ncol);

	rmin = rmax = -1;
	if (topmost) {
		if (data->cols[col]) {
			if (!data->cols[col]->topmost) {
				data->cols[col]->topmost = TRUE;
				rmin = 0;
				rmax = data->nrow-1;
			}
		} else {
			CmpackGraphColumn *c = (CmpackGraphColumn*)g_malloc0(sizeof(CmpackGraphColumn));
			c->topmost = topmost;
			rmin = 0;
			rmax = data->nrow-1;
			data->cols[col] = c;
		}
	} else {
		if (data->cols[col]) {
			if (data->cols[col]->topmost) {
				data->cols[col]->topmost = FALSE;
				rmin = 0;
				rmax = data->nrow-1;
			}
		}
	}
	if (rmin>=0) 
		g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, rmin, rmax);
}

/* Set default color for entire dataset */
void cmpack_graph_data_set_dataset_color(CmpackGraphData *data, gint col, CmpackColor color)
{
	int rmin, rmax;

	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && col<data->ncol);

	rmin = rmax = -1;
	if (color != CMPACK_COLOR_DEFAULT) {
		if (data->cols[col]) {
			if (data->cols[col]->color == CMPACK_COLOR_DEFAULT) {
				data->cols[col]->color = color;
				rmin = 0;
				rmax = data->nrow-1;
			}
		} else {
			CmpackGraphColumn *c = (CmpackGraphColumn*)g_malloc0(sizeof(CmpackGraphColumn));
			c->color = color;
			rmin = 0;
			rmax = data->nrow-1;
			data->cols[col] = c;
		}
	} else {
		if (data->cols[col]) {
			if (data->cols[col]->color != CMPACK_COLOR_DEFAULT) {
				data->cols[col]->color = CMPACK_COLOR_DEFAULT;
				rmin = 0;
				rmax = data->nrow-1;
			}
		}
	}
	if (rmin>=0) 
		g_signal_emit(data, cmpack_graph_data_signals[DATA_UPDATED], 0, col, col, rmin, rmax);
}

/* Set dataset user data */
void cmpack_graph_data_set_dataset_param(CmpackGraphData *data, gint col, intptr_t param)
{
	g_return_if_fail(data != NULL);
	g_return_if_fail(col>=0 && col<data->ncol);

	if (!data->cols[col]) 
		data->cols[col] = (CmpackGraphColumn*)g_malloc0(sizeof(CmpackGraphColumn));
	data->cols[col]->param = param;
}
