/* This file is part of GQ
 *
 * AUTHORS
 *     Sven Herzberg  <herzi@gnome-de.org>
 *
 * Copyright (C) 2006  Sven Herzberg
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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
 */

#include "gq-change-bar.h"

#include <glib/gi18n.h>

struct GqChangeBarPrivate {
	GtkWidget   * comparison;
	GtkWidget   * input_form;
	GqDifference* difference;
};
#define P(i) (G_TYPE_INSTANCE_GET_PRIVATE((i), GQ_TYPE_CHANGE_BAR, struct GqChangeBarPrivate))

GtkWidget*
gq_change_bar_new(GqComparison* comparison,
		  GqInputForm * form)
{
	return g_object_new(GQ_TYPE_CHANGE_BAR,
			    "comparison", comparison,
			    "input-form", form,
			    NULL);
}

/* GType */
G_DEFINE_TYPE(GqChangeBar, gq_change_bar, GTK_TYPE_DRAWING_AREA);

enum {
	PROP_0,
	PROP_COMPARISON,
	PROP_INPUT_FORM
};

static void
gq_change_bar_init(GqChangeBar* self G_GNUC_UNUSED)
{}

static void
change_bar_dispose(GObject* object)
{
	if(P(object)->difference) {
		g_signal_handlers_disconnect_by_func(P(object)->difference, gtk_widget_queue_draw, object);
		g_object_unref(P(object)->difference);
		P(object)->difference = NULL;
	}

	if(P(object)->comparison) {
		g_object_unref(P(object)->comparison);
		P(object)->comparison = NULL;
	}

	if(P(object)->input_form) {
		g_object_unref(P(object)->input_form);
		P(object)->input_form = NULL;
	}

	G_OBJECT_CLASS(gq_change_bar_parent_class)->dispose(object);
}

static void
change_bar_get_property(GObject   * object,
			guint       prop_id,
			GValue    * value,
			GParamSpec* pspec)
{
	switch(prop_id) {
	case PROP_COMPARISON:
		g_value_set_object(value, P(object)->comparison);
		break;
	case PROP_INPUT_FORM:
		g_value_set_object(value, P(object)->input_form);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}

static void
change_bar_set_property(GObject     * object,
			guint         prop_id,
			GValue const* value,
			GParamSpec  * pspec)
{
	switch(prop_id) {
	case PROP_COMPARISON:
		if(P(object)->comparison) {
			g_signal_handlers_disconnect_by_func(P(object)->difference, gtk_widget_queue_draw, object);
			g_object_unref(P(object)->difference);
			P(object)->difference = NULL;
			g_object_unref(P(object)->comparison);
		}
		P(object)->comparison = (GtkWidget*)g_value_dup_object(value);
		if(P(object)->comparison) {
			P(object)->difference = g_object_ref(gq_comparison_get_difference(GQ_COMPARISON(P(object)->comparison)));
			g_signal_connect_swapped(P(object)->difference, "updated",
						 G_CALLBACK(gtk_widget_queue_draw), object);
		}
		g_object_notify(object, "comparison");
		break;
	case PROP_INPUT_FORM:
		if(P(object)->input_form) {
			g_object_unref(P(object)->input_form);
		}
		P(object)->input_form = (GtkWidget*)g_value_dup_object(value);
		g_object_notify(object, "input-form");
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}

static void
change_bar_display_change(GqFormfill* form1,
			  GqFormfill* form2,
			  gboolean     equal,
			  gpointer     user_data)
{
	gpointer * self_and_cr = user_data;
	GtkWidget* form = P(self_and_cr[0])->input_form;
	GtkWidget* comp1 = !form1 ? NULL : gtk_widget_get_parent(
					gtk_widget_get_parent(
						gtk_widget_get_parent(
							gtk_widget_get_parent(
							gtk_widget_get_parent(
								gq_formfill_get_event_box(form1) // GtkEventBox
								) // should return GtkTable
							) // GtkVBox
						) // should return GtkViewport
					) // should return GtkScrolledWindow
				); // should return GqInputForm
	GtkWidget* comp2 = !form2 ? NULL : gtk_widget_get_parent(
					gtk_widget_get_parent(
						gtk_widget_get_parent(
							gtk_widget_get_parent(
							gtk_widget_get_parent(
								gq_formfill_get_event_box(form2) // GtkEventBox
								) // should return GtkTable
							) // should return GtkVBox
						) // should return GtkViewport
					) // should return GtkScrolledWindow
				); // should return GqInputForm

	if(equal) {
		return;
	}

	if(comp1 == form || comp2 == form) {
		GtkWidget* reference = self_and_cr[1];
		GtkWidget* table;
		GtkWidget* ebox;
		cairo_t* cr = self_and_cr[2];
		cairo_set_line_width(cr, 1.0);
		if(comp1 == form) {
			ebox = gq_formfill_get_event_box(form1);
		}
		else {
			ebox = gq_formfill_get_event_box(form2);
		}
		table = gtk_widget_get_parent(ebox);
		cairo_rectangle(cr, 0.5, ((int)1.0*reference->allocation.height*(ebox->allocation.y+table->allocation.x)/(table->allocation.height+table->allocation.x))+0.5, 11.0, 3.0);
		if(form1 && form2) {
			cairo_set_source_rgb(cr, 0.45, 0.62, 0.81);
		}
		else {
			cairo_set_source_rgb(cr, 0.54, 0.88, 0.2);
		}
		cairo_fill_preserve(cr);
		if(form1 && form2) {
			cairo_set_source_rgb(cr, 0.13, 0.29, 0.53);
		}
		else
		{
			cairo_set_source_rgb(cr, 0.3, 0.6, 0.02);
		}
		cairo_stroke(cr);
	}
}

static gboolean
change_bar_expose_event(GtkWidget     * widget,
			GdkEventExpose* event  G_GNUC_UNUSED)
{
	GtkWidget* reference = gtk_bin_get_child(GTK_BIN(gq_input_form_get_scrolled_window(GQ_INPUT_FORM(P(widget)->input_form))));
	cairo_t* cr = gdk_cairo_create(widget->window);
	gpointer self_ref_and_cr[3] = {widget, reference, cr};
	cairo_translate(cr, 0.0, reference->allocation.y - widget->allocation.y);
	gq_difference_foreach(P(widget)->difference, change_bar_display_change, self_ref_and_cr);
	cairo_destroy(cr);
	return FALSE;
}

static void
change_bar_size_request(GtkWidget     * widget G_GNUC_UNUSED,
			GtkRequisition* requisition)
{
	requisition->width = 12;
}

static void
gq_change_bar_class_init(GqChangeBarClass* self_class)
{
	GObjectClass* object_class = G_OBJECT_CLASS(self_class);
	GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(self_class);

	/* GObjectClass */
	object_class->dispose      = change_bar_dispose;
	object_class->get_property = change_bar_get_property;
	object_class->set_property = change_bar_set_property;

	g_object_class_install_property(object_class, PROP_COMPARISON,
					g_param_spec_object("comparison",
							    _("Comparison"),
							    _("The compare widget"),
							    GQ_TYPE_COMPARISON,
							    G_PARAM_READWRITE));
	g_object_class_install_property(object_class, PROP_INPUT_FORM,
					g_param_spec_object("input-form",
							    _("Input Form"),
							    _("The input form of this change bar"),
							    GQ_TYPE_INPUT_FORM,
							    G_PARAM_READWRITE));

	/* GtkWidgetClass */
	widget_class->expose_event = change_bar_expose_event;
	widget_class->size_request = change_bar_size_request;

	/* GqChangeBarClass */
	g_type_class_add_private(self_class, sizeof(struct GqChangeBarPrivate));
}

