/* 
 * Signal History Widget
 * Copyright (C) 2000 Thomas Sailer <sailer@ife.ee.ethz.ch>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; 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 "signalhist.h"
#include <gtk/gtkgc.h>
#include <gtk/gtkmain.h>
#include <math.h>

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

/* --------------------------------------------------------------------- */

#define PRIO G_PRIORITY_LOW

static void signalhistory_class_init(SignalhistoryClass *klass);
static void signalhistory_init(Signalhistory *signalhistory);
static void signalhistory_finalize(GtkObject *object);
static gint signalhistory_expose(GtkWidget *widget, GdkEventExpose *event);
static void signalhistory_realize(GtkWidget *widget);
static void signalhistory_unrealize(GtkWidget *widget);
static void signalhistory_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
static void signalhistory_send_configure (Signalhistory *signalhistory);
static gint idle_callback(gpointer data);

static GtkWidgetClass *parent_class = NULL;
static SignalhistoryClass *signalhistory_class = NULL;


guint signalhistory_get_type(void)
{
	static guint signalhistory_type = 0;

	if (!signalhistory_type)
	{
		static const GtkTypeInfo signalhistory_info =
		{
			"Signalhistory",
			sizeof(Signalhistory),
			sizeof(SignalhistoryClass),
			(GtkClassInitFunc)signalhistory_class_init,
			(GtkObjectInitFunc)signalhistory_init,
			/* reserved_1 */ NULL,
			/* reserved_2 */ NULL,
			(GtkClassInitFunc)NULL,
		};
		signalhistory_type = gtk_type_unique(gtk_widget_get_type(), &signalhistory_info);
	}
	return signalhistory_type;
}

static void signalhistory_class_init(SignalhistoryClass *klass)
{
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;

	object_class = (GtkObjectClass*)klass;
	widget_class = (GtkWidgetClass*)klass;

	parent_class = gtk_type_class(gtk_widget_get_type());
	signalhistory_class = klass;

	object_class->finalize = signalhistory_finalize;
	widget_class->expose_event = signalhistory_expose;
	widget_class->realize = signalhistory_realize;
	widget_class->unrealize = signalhistory_unrealize;
	widget_class->size_allocate = signalhistory_size_allocate;
}

static void signalhistory_init(Signalhistory *signalhistory)
{
	signalhistory->idlefunc = 0;
	/* initialize the colors */
	signalhistory->col0.red = 39322;
	signalhistory->col0.green = 60948;
	signalhistory->col0.blue = 39322;
	signalhistory->col1.red = 3277;
	signalhistory->col1.green = 60948;
	signalhistory->col1.blue = 0;
	signalhistory->col2.red = 65535;
	signalhistory->col2.green = 15073;
	signalhistory->col2.blue = 0;
	signalhistory->col0_gc = signalhistory->col1_gc = signalhistory->col2_gc = NULL;
	signalhistory->pixmap = NULL;
	/* initialize the data */
	memset(&signalhistory->data, 0, sizeof(signalhistory->data));
	signalhistory->ptr = 0;
}

static void signalhistory_realize(GtkWidget *widget)
{
	Signalhistory *signalhistory;
	GdkWindowAttr attributes;
	gint attributes_mask;
	GdkGCValues gc_values;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(IS_SIGNALHISTORY(widget));

	signalhistory = SIGNALHISTORY(widget);
	GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);

	attributes.window_type = GDK_WINDOW_CHILD;
	attributes.x = widget->allocation.x;
	attributes.y = widget->allocation.y;
	attributes.width = widget->allocation.width;
	attributes.height = widget->allocation.height;
	attributes.wclass = GDK_INPUT_OUTPUT;
	attributes.visual = gtk_widget_get_visual(widget);
	attributes.colormap = gtk_widget_get_colormap(widget);
	attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;

	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

	widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
	gdk_window_set_user_data(widget->window, signalhistory);

	widget->style = gtk_style_attach(widget->style, widget->window);
	gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);

	/* gc's if necessary */
	if (!gdk_color_alloc(widget->style->colormap, &signalhistory->col0))
		g_warning("unable to allocate color: ( %d %d %d )", signalhistory->col0.red, signalhistory->col0.green, signalhistory->col0.blue);
	gc_values.foreground = signalhistory->col0;
	signalhistory->col0_gc = gtk_gc_get(widget->style->depth, widget->style->colormap, &gc_values, GDK_GC_FOREGROUND);
	if (!gdk_color_alloc(widget->style->colormap, &signalhistory->col1))
		g_warning("unable to allocate color: ( %d %d %d )", signalhistory->col1.red, signalhistory->col1.green, signalhistory->col1.blue);
	gc_values.foreground = signalhistory->col1;
	signalhistory->col1_gc = gtk_gc_get(widget->style->depth, widget->style->colormap, &gc_values, GDK_GC_FOREGROUND);
	if (!gdk_color_alloc(widget->style->colormap, &signalhistory->col2))
		g_warning("unable to allocate color: ( %d %d %d )", signalhistory->col2.red, signalhistory->col2.green, signalhistory->col2.blue);
	gc_values.foreground = signalhistory->col2;
	signalhistory->col2_gc = gtk_gc_get(widget->style->depth, widget->style->colormap, &gc_values, GDK_GC_FOREGROUND);
	/* create backing store */
	signalhistory->pixmap = gdk_pixmap_new(widget->window, SIGNALHISTORY_WIDTH, SIGNALHISTORY_HEIGHT, -1);

	signalhistory_send_configure(SIGNALHISTORY(widget));
}

static void signalhistory_unrealize(GtkWidget *widget)
{
	Signalhistory *signalhistory;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(IS_SIGNALHISTORY(widget));

	signalhistory = SIGNALHISTORY(widget);
	if (signalhistory->idlefunc)
		gtk_idle_remove(signalhistory->idlefunc);
	if (signalhistory->col0_gc)
		gtk_gc_release(signalhistory->col0_gc);
	if (signalhistory->col1_gc)
		gtk_gc_release(signalhistory->col1_gc);
	if (signalhistory->col2_gc)
		gtk_gc_release(signalhistory->col2_gc);
	signalhistory->col0_gc = signalhistory->col1_gc = signalhistory->col2_gc = NULL;
	if (signalhistory->pixmap)
			gdk_pixmap_unref(signalhistory->pixmap);
	signalhistory->pixmap = NULL;
	if (GTK_WIDGET_CLASS(parent_class)->unrealize)
		(*GTK_WIDGET_CLASS(parent_class)->unrealize)(widget);
}

static void signalhistory_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
	g_return_if_fail(widget != NULL);
	g_return_if_fail(IS_SIGNALHISTORY(widget));
	g_return_if_fail(allocation != NULL);
	
	widget->allocation = *allocation;
	widget->allocation.width = SIGNALHISTORY_WIDTH;
	widget->allocation.height = SIGNALHISTORY_HEIGHT;

	if (GTK_WIDGET_REALIZED(widget)) {
		gdk_window_move_resize (widget->window,
					allocation->x, allocation->y,
					allocation->width, allocation->height);
		signalhistory_send_configure(SIGNALHISTORY(widget));
	}
}

static void signalhistory_send_configure(Signalhistory *signalhistory)
{
	GtkWidget *widget;
	GdkEventConfigure event;

	widget = GTK_WIDGET(signalhistory);

	event.type = GDK_CONFIGURE;
	event.window = widget->window;
	event.send_event = TRUE;
	event.x = widget->allocation.x;
	event.y = widget->allocation.y;
	event.width = widget->allocation.width;
	event.height = widget->allocation.height;
  
	gtk_widget_event(widget, (GdkEvent*)&event);
}


GtkWidget* signalhistory_new(gchar *name, gchar *string1, gchar *string2, gint int1, gint int2)
{
	Signalhistory *signalhistory;
	
	signalhistory = gtk_type_new(signalhistory_get_type());
	memset(&signalhistory->data, 0, sizeof(signalhistory->data));
	signalhistory->ptr = 0;
	return GTK_WIDGET(signalhistory);
}

static void signalhistory_finalize(GtkObject *object)
{
	g_return_if_fail(object != NULL);
	g_return_if_fail(IS_SIGNALHISTORY(object));
	(*GTK_OBJECT_CLASS(parent_class)->finalize)(object);
}

static gint signalhistory_expose(GtkWidget *widget, GdkEventExpose *event)
{
	Signalhistory *signalhistory;

	g_return_val_if_fail(widget != NULL, FALSE);
	g_return_val_if_fail(IS_SIGNALHISTORY(widget), FALSE);
	g_return_val_if_fail (event != NULL, FALSE);
	if (GTK_WIDGET_DRAWABLE(widget)) {
		signalhistory = SIGNALHISTORY(widget);
		if (!signalhistory->idlefunc)
			signalhistory->idlefunc = gtk_idle_add_priority(PRIO, idle_callback, signalhistory);
	}
	return FALSE;
}

static void draw(Signalhistory *signalhistory)
{
	guint i, j, ptr;
	GdkPoint pt[SIGNALHISTORY_WIDTH+1];
	GtkWidget *widget;
	unsigned char col;

	widget = GTK_WIDGET(signalhistory);
	g_return_if_fail(GTK_WIDGET_DRAWABLE(widget));
	g_return_if_fail(signalhistory->pixmap);
	/* clear window */
	gdk_draw_rectangle(signalhistory->pixmap, widget->style->base_gc[widget->state],
			   TRUE, 0, 0, widget->allocation.width, widget->allocation.height);
	/* draw data points */
	ptr = signalhistory->ptr;
	for (i = 0; i < SIGNALHISTORY_WIDTH; ) {
		col = signalhistory->data[ptr].col;
		for (j = 0; i < SIGNALHISTORY_WIDTH;) {
			pt[j].x = i;
			pt[j].y = ((255-signalhistory->data[ptr].sample)*SIGNALHISTORY_HEIGHT) >> 8;
			j++;
			if (col != signalhistory->data[ptr].col)
				break;
			i++;
			ptr = (ptr + 1) % SIGNALHISTORY_WIDTH;
		}
		gdk_draw_lines(signalhistory->pixmap, (col == 0) ? signalhistory->col0_gc : ((col == 1) ? signalhistory->col1_gc : signalhistory->col2_gc), pt, j);
	}
	/* draw to screen */
	gdk_draw_pixmap(widget->window, widget->style->base_gc[widget->state], signalhistory->pixmap, 
			0, 0, 0, 0, widget->allocation.width, widget->allocation.height);
}

static gint idle_callback(gpointer data)
{
	g_return_val_if_fail(data != NULL, FALSE);
	g_return_val_if_fail(IS_SIGNALHISTORY(data), FALSE);
	SIGNALHISTORY(data)->idlefunc = 0;
	if (!GTK_WIDGET_DRAWABLE(GTK_WIDGET(data)))
		return FALSE;
	draw(SIGNALHISTORY(data));
	return FALSE;  /* don't call this callback again */
}

void signalhistory_newdata(Signalhistory *signalhistory, unsigned char sample, unsigned char col)
{
	g_return_if_fail(signalhistory != NULL);
	g_return_if_fail(IS_SIGNALHISTORY(signalhistory));
	signalhistory->data[signalhistory->ptr].sample = sample;
	signalhistory->data[signalhistory->ptr].col = col;
	signalhistory->ptr = (signalhistory->ptr + 1) % SIGNALHISTORY_WIDTH;
	if (GTK_WIDGET_DRAWABLE(GTK_WIDGET(signalhistory))) {
		if (!signalhistory->idlefunc)
			signalhistory->idlefunc = gtk_idle_add_priority(PRIO, idle_callback, signalhistory);
	}
}
