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

cmpack_timescale_data.cpp (C-Munipack project)
Object which holds data for a time scale
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_time_scale_data.cpp,v 1.1 2016/02/21 10:32:08 dmotl Exp $

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

#include "cmpack_time_scale_data.h"

enum {
	ROW_INSERTED, 
	ROW_UPDATED,
	ROW_DELETED,
	DATA_CLEARED,
	LAST_SIGNAL
}; 

/*--------------------   TIME SCALE DATA CLASS   ----------------------------*/

G_DEFINE_TYPE(CmpackTimeScaleData, cmpack_time_scale_data, G_TYPE_OBJECT)

static guint cmpack_time_scale_data_signals[LAST_SIGNAL] = { 0 }; 

static void cmpack_time_scale_data_finalize(GObject *object);

/* Class initialization */
static void cmpack_time_scale_data_class_init(CmpackTimeScaleDataClass *klass)
{
    GObjectClass *object_class = (GObjectClass *)klass;

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

	/* Signals */
	cmpack_time_scale_data_signals[ROW_INSERTED] = g_signal_new("row-inserted",
		  G_TYPE_FROM_CLASS(object_class), 
		  GSignalFlags(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
		  G_STRUCT_OFFSET (CmpackTimeScaleDataClass, row_inserted), NULL, NULL, 
		  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); 

	cmpack_time_scale_data_signals[ROW_UPDATED] = g_signal_new("row-updated",
		  G_TYPE_FROM_CLASS(object_class), 
		  GSignalFlags(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
		  G_STRUCT_OFFSET (CmpackTimeScaleDataClass, row_updated), NULL, NULL, 
		  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); 

	cmpack_time_scale_data_signals[ROW_DELETED] = g_signal_new("row-deleted",
		  G_TYPE_FROM_CLASS(object_class), 
		  GSignalFlags(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
		  G_STRUCT_OFFSET (CmpackTimeScaleDataClass, row_deleted), NULL, NULL, 
		  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); 
	
	cmpack_time_scale_data_signals[DATA_CLEARED] = g_signal_new("data-cleared",
		  G_TYPE_FROM_CLASS(object_class), 
		  GSignalFlags(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
		  G_STRUCT_OFFSET (CmpackTimeScaleDataClass, data_cleared), NULL, NULL, 
		  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
}

/* Instance initialization */
static void cmpack_time_scale_data_init(CmpackTimeScaleData *data)
{
}

/* Create time scale widget */
CmpackTimeScaleData *cmpack_time_scale_data_new()
{
	return (CmpackTimeScaleData*)g_object_new(CMPACK_TYPE_TIME_SCALE_DATA, NULL);
}

/* Create time scale widget */
CmpackTimeScaleData *cmpack_time_scale_data_new_with_alloc(gint capacity)
{
	CmpackTimeScaleData *res = cmpack_time_scale_data_new();
	cmpack_time_scale_data_alloc(res, capacity);
	return res;
}

/* Free allocated memory */
static void cmpack_time_scale_data_finalize(GObject *object)
{
	gint i;
	CmpackTimeScaleData *data = CMPACK_TIME_SCALE_DATA(object);

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

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

/* Clear data and set number of channels and initial capacity */
void cmpack_time_scale_data_alloc(CmpackTimeScaleData *data, gint capacity)
{
	g_return_if_fail(data != NULL);

	capacity = MAX(capacity, data->count);
	if (capacity != data->capacity) {
		if (capacity>0) {
			data->items = (CmpackTimeScaleItem**)g_realloc(data->items, capacity*sizeof(CmpackTimeScaleItem*));
		} else {
			g_free(data->items);
			data->items = NULL;
		}
		data->capacity = capacity;
	}
}

/* Clear all data */
void cmpack_time_scale_data_clear(CmpackTimeScaleData *data)
{
	gboolean changed;
	gint i;
	g_return_if_fail(data != NULL);

	/* Free allocated data */
	changed = data->count>0;
	for (i=0; i<data->count; i++) {
		g_free(data->items[i]->tag);
		g_free(data->items[i]);
	}
	g_free(data->items);
	data->items = NULL;
	data->count = data->capacity = 0;

	if (changed)
		g_signal_emit(data, cmpack_time_scale_data_signals[DATA_CLEARED], 0);
}

/* Add new data to the time scale */
gint cmpack_time_scale_data_add(CmpackTimeScaleData *data, const CmpackTimeScaleItem *d, gsize item_size)
{
	gint index;
	CmpackTimeScaleItem *item;

	g_return_val_if_fail(data != NULL, -1);

	if (data->count>=data->capacity) {
		data->capacity += 64;
		data->items = (CmpackTimeScaleItem**)g_realloc(data->items, data->capacity*sizeof(CmpackTimeScaleItem*));
	}
	index = data->count++;
	
	item = (CmpackTimeScaleItem*)g_malloc0(sizeof(CmpackTimeScaleItem));
	memcpy(item, d, MIN(sizeof(CmpackTimeScaleItem), item_size));
	item->tag = (d->tag!=NULL ? g_strdup(d->tag) : NULL);
	data->items[index] = item;
	
	g_signal_emit(data, cmpack_time_scale_data_signals[ROW_INSERTED], 0, index);
	return index;
}

/* Set row tag */
void cmpack_time_scale_data_set_tag(CmpackTimeScaleData *data, gint row, const gchar *text)
{
	CmpackTimeScaleItem *item;

	g_return_if_fail(data != NULL);
	g_return_if_fail(row>=0 && row<data->count);

	item = data->items[row];
	if (text) {
		if (!item->tag || strcmp(text, item->tag)!=0) {
			g_free(item->tag); 
			item->tag = g_strdup(text);
			g_signal_emit(data, cmpack_time_scale_data_signals[ROW_UPDATED], 0, row);
		}
	} else {
		if (item->tag) {
			g_free(item->tag); 
			item->tag = NULL;
			g_signal_emit(data, cmpack_time_scale_data_signals[ROW_UPDATED], 0, row);
		}
	}
}

/* Set topmost flag */
void cmpack_time_scale_data_set_topmost(CmpackTimeScaleData *data, gint row, gboolean topmost)
{
	CmpackTimeScaleItem *item;

	g_return_if_fail(data != NULL);
	g_return_if_fail(row>=0 && row<data->count);

	item = data->items[row];
	if (item->topmost != topmost) {
		data->items[row]->topmost = topmost;
		g_signal_emit(data, cmpack_time_scale_data_signals[ROW_UPDATED], 0, row);
	}
}

/* Set row color */
void cmpack_time_scale_data_set_color(CmpackTimeScaleData *data, gint row, CmpackColor color)
{
	CmpackTimeScaleItem *item;

	g_return_if_fail(data != NULL);
	g_return_if_fail(row>=0 && row<data->count);

	item = data->items[row];
	if (item->color!=color) {
		item->color = color;
		g_signal_emit(data, cmpack_time_scale_data_signals[ROW_UPDATED], 0, row);
	}
}

/* Clear all tags */
void cmpack_time_scale_data_clear_tags(CmpackTimeScaleData *data)
{
	gint i;

	g_return_if_fail(data != NULL);

	for (i=0; i<data->count; i++) {
		CmpackTimeScaleItem *item = data->items[i];
		if (item->tag) {
			g_free(item->tag); 
			item->tag = NULL;
			g_signal_emit(data, cmpack_time_scale_data_signals[ROW_UPDATED], 0, i);
		}
	}
}

/* Get row tag */
const gchar *cmpack_time_scale_data_get_tag(CmpackTimeScaleData *data, gint row)
{
	g_return_val_if_fail(data != NULL, NULL);
	g_return_val_if_fail(row>=0 && row<data->count, NULL);

	return data->items[row]->tag;
}

/* Get row tag */
CmpackColor cmpack_time_scale_data_get_color(CmpackTimeScaleData *data, gint row)
{
	g_return_val_if_fail(data != NULL, CMPACK_COLOR_DEFAULT);
	g_return_val_if_fail(row>=0 && row<data->count, CMPACK_COLOR_DEFAULT);

	return data->items[row]->color;
}

/* Get row tag */
intptr_t cmpack_time_scale_data_get_param(CmpackTimeScaleData *data, gint row)
{
	g_return_val_if_fail(data != NULL, 0);
	g_return_val_if_fail(row>=0 && row<data->count, 0);

	return data->items[row]->param;
}

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

	return data->count; 
}

/* Get single value */
const CmpackTimeScaleItem *cmpack_time_scale_data_get_item(CmpackTimeScaleData *data, gint row)
{
	g_return_val_if_fail(data != NULL, NULL);
	g_return_val_if_fail(row>=0 && row<data->count, NULL);

	return data->items[row];
}

/* Find object by its identifier */
gint cmpack_time_scale_data_find_item(CmpackTimeScaleData *data, intptr_t param)
{
	gint i;

	g_return_val_if_fail(data != NULL, -1);

	for (i=0; i<data->count; i++) {
		if (data->items[i]->param == param)
			return i;
	}
	return -1;
}
