/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * gog-control-foocanvas.c :
 *
 * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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
 */

#include <goffice/goffice-config.h>
#include <goffice/graph/gog-control-foocanvas.h>
#include <goffice/graph/gog-graph.h>
#include <goffice/graph/gog-object.h>
#include <libfoocanvas/foo-canvas-util.h>

#include <gsf/gsf-impl-utils.h>
#include <glib/gi18n.h>
#include <math.h>

enum {
	CTRL_FOO_PROP_0,
	CTRL_FOO_PROP_H,
	CTRL_FOO_PROP_W,
	CTRL_FOO_PROP_MODEL,
	CTRL_FOO_PROP_RENDERER,
	CTRL_FOO_PROP_LOGICAL_WIDTH_PTS,
	CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS
};

static GObjectClass *parent_klass;

#define GOG_CONTROL_FOOCANVAS_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvasClass))
#define IS_GOG_CONTROL_FOOCANVAS_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_CONTROL_FOOCANVAS_TYPE))
#define GOG_CONTROL_FOOCANVAS_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((k), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvasClass))

static void
gog_control_foocanvas_set_property (GObject *gobject, guint param_id,
				    GValue const *value, GParamSpec *pspec)
{
	GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (gobject);
	gboolean setup_renderer = (ctrl->renderer == NULL);

	switch (param_id) {
	case CTRL_FOO_PROP_H: ctrl->new_h = g_value_get_double (value); break;
	case CTRL_FOO_PROP_W: ctrl->new_w = g_value_get_double (value); break;

	case CTRL_FOO_PROP_MODEL:
		if (ctrl->renderer != NULL)
			g_object_unref (ctrl->renderer);
		ctrl->renderer = g_object_new (GOG_RENDERER_PIXBUF_TYPE,
					       "model", g_value_get_object (value),
					       NULL);
		break;

	case CTRL_FOO_PROP_RENDERER:
		if (ctrl->renderer != NULL)
			g_object_unref (ctrl->renderer);
		ctrl->renderer = GOG_RENDERER_PIXBUF (g_value_get_object (value));
		if (ctrl->renderer != NULL)
			g_object_ref (ctrl->renderer);
		break;
	case CTRL_FOO_PROP_LOGICAL_WIDTH_PTS :
		g_object_set_property (G_OBJECT (ctrl->renderer),
				       "logical_width_pts", value);
		break;
	case CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS :
		g_object_set_property (G_OBJECT (ctrl->renderer),
				       "logical_height_pts", value);
		break;

	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
		return; /* NOTE : RETURN */
	}

	if (setup_renderer && ctrl->renderer != NULL)
		g_signal_connect_object (G_OBJECT (ctrl->renderer),
			"request_update",
			G_CALLBACK (foo_canvas_item_request_update),
			ctrl, G_CONNECT_SWAPPED);
	foo_canvas_item_request_update (FOO_CANVAS_ITEM (ctrl));
}

static void
gog_control_foocanvas_get_property (GObject *gobject, guint param_id,
				    GValue *value, GParamSpec *pspec)
{
	GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (gobject);

	switch (param_id) {
	case CTRL_FOO_PROP_H: g_value_set_double (value, ctrl->new_h); break;
	case CTRL_FOO_PROP_W: g_value_set_double (value, ctrl->new_w); break;
	case CTRL_FOO_PROP_RENDERER  : g_value_set_object (value, ctrl->renderer); break;
	case CTRL_FOO_PROP_LOGICAL_WIDTH_PTS :
		g_object_get_property (G_OBJECT (ctrl->renderer),
				       "logical_width_pts", value);
		break;
	case CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS :
		g_object_get_property (G_OBJECT (ctrl->renderer),
				       "logical_height_pts", value);
		break;

	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
		break;
	}
}

static void
gog_control_foocanvas_finalize (GObject *obj)
{
	GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (obj);

	if (ctrl->model != NULL) {
		g_object_unref (ctrl->model);
		ctrl->model = NULL;
	}
	if (ctrl->renderer != NULL) {
		g_object_unref (ctrl->renderer);
		ctrl->renderer = NULL;
	}
	(*parent_klass->finalize) (obj);
}

static void
gog_control_foocanvas_draw (FooCanvasItem *item, GdkDrawable *drawable,
			    GdkEventExpose *ev)
{
	GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (item);
	GdkPixbuf *buffer = gog_renderer_pixbuf_get (ctrl->renderer);
	GdkRectangle display_rect, draw_rect;
	GdkRegion *draw_region;

	if (buffer) {
		display_rect.x = item->x1;
		display_rect.y = item->y1;
		display_rect.width  = item->x2 - item->x1;
		display_rect.height = item->y2 - item->y1;

		draw_region = gdk_region_rectangle (&display_rect);
		gdk_region_intersect (draw_region, ev->region);
		if (!gdk_region_empty (draw_region)) {
			gdk_region_get_clipbox (draw_region, &draw_rect);
			gdk_draw_pixbuf (drawable, NULL, buffer,
				/* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
				     draw_rect.x - display_rect.x,
				     draw_rect.y - display_rect.y,
				     draw_rect.x,
				     draw_rect.y,
				     draw_rect.width,
				     draw_rect.height,
				     GDK_RGB_DITHER_NORMAL, 0, 0);
		}
		gdk_region_destroy (draw_region);
	}

	/* we are a canvas group, there could be some children */
	if (FOO_CANVAS_ITEM_CLASS (parent_klass)->draw)
		(FOO_CANVAS_ITEM_CLASS (parent_klass)->draw) (item, drawable, ev);
}

static void
gog_control_foocanvas_update (FooCanvasItem *item,
			      double i2w_dx, double i2w_dy, gint flags)
{
	GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (item);
	gboolean redraw;
	int x1, x2, y1, y2;
	int orig_x1 = item->x1, orig_x2 = item->x2, orig_y1 = item->y1, orig_y2 = item->y2;

	if (FOO_CANVAS_ITEM_CLASS (parent_klass)->update)
		(FOO_CANVAS_ITEM_CLASS (parent_klass)->update) (item, i2w_dx, i2w_dy, flags);
	/* foo_canvas_group_update wipes the bbox */
	item->x1 = orig_x1;	item->x2 = orig_x2;
	item->y1 = orig_y1;	item->y2 = orig_y2;

	foo_canvas_w2c (item->canvas, ctrl->base.xpos, ctrl->base.ypos, &x1, &y1);
	foo_canvas_w2c (item->canvas, ctrl->base.xpos + ctrl->new_w, ctrl->base.ypos + ctrl->new_h, &x2, &y2);

	redraw = gog_renderer_pixbuf_update (ctrl->renderer, x2-x1, y2-y1,
		item->canvas->pixels_per_unit);
	if (item->x1 != x1 || item->y1 != y1 || item->x2 != x2 || item->y2 != y2)
		foo_canvas_update_bbox (FOO_CANVAS_ITEM (ctrl), x1, y1, x2, y2);
	else if (redraw)
		foo_canvas_item_request_redraw (FOO_CANVAS_ITEM (ctrl));
}

static void
gog_control_foocanvas_bounds (FooCanvasItem *item,
			      double *x1, double *y1, double *x2, double *y2)
{
	*x1 = item->x1;
	*x2 = item->x2;
	*y1 = item->y1;
	*y2 = item->y2;
}

static double
gog_control_foocanvas_point (FooCanvasItem *item, double x, double y, int cx, int cy,
			     FooCanvasItem **actual_item)
{
	*actual_item = item;
	return 0.;
}

static void
gog_control_foocanvas_class_init (GogControlFooCanvasClass *klass)
{
	GObjectClass *gobject_klass = (GObjectClass *) klass;
	FooCanvasItemClass *item_klass = (FooCanvasItemClass *) klass;

	parent_klass = g_type_class_peek_parent (klass);

	gobject_klass->set_property = gog_control_foocanvas_set_property;
	gobject_klass->get_property = gog_control_foocanvas_get_property;
	gobject_klass->finalize	    = gog_control_foocanvas_finalize;
	item_klass->draw   = gog_control_foocanvas_draw;
	item_klass->update = gog_control_foocanvas_update;
	item_klass->bounds = gog_control_foocanvas_bounds;
	item_klass->point  = gog_control_foocanvas_point;

	g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_H,
		 g_param_spec_double ("h", _("H"), _("Height"),
			0, G_MAXDOUBLE, 100., G_PARAM_READWRITE));
	g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_W,
		 g_param_spec_double ("w", _("W"), _("Width"),
			0, G_MAXDOUBLE, 100., G_PARAM_READWRITE));
	g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_MODEL,
		g_param_spec_object ("model", "model",
			"the GogObject this object displays",
			GOG_OBJECT_TYPE, G_PARAM_WRITABLE));
	g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_RENDERER,
		g_param_spec_object ("renderer", "renderer",
			"the GogRendererPixbuf being displayed",
			GOG_RENDERER_PIXBUF_TYPE, G_PARAM_READWRITE));
	g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_LOGICAL_WIDTH_PTS,
		g_param_spec_double ("logical_width_pts", "Logical Width Pts",
			"Logical width of the drawing area in pts",
			0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
	g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS,
		g_param_spec_double ("logical_height_pts", "Logical Height Pts",
			"Logical height of the drawing area in pts",
			0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
}

static void
gog_control_foocanvas_init (GogControlFooCanvas *ctrl)
{
	ctrl->new_h = ctrl->new_w = 0.;
}

GSF_CLASS (GogControlFooCanvas, gog_control_foocanvas,
	   gog_control_foocanvas_class_init, gog_control_foocanvas_init,
	   FOO_TYPE_CANVAS_GROUP)

