/* dia-shape-x.c
 * Copyright (C) 2001  Arjan Molenaar
 *
 * 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.
 */

#include "dia-shape-art.h"
#include <libgnomecanvas/gnome-canvas-util.h>
#include <gdk/gdk.h>

/* NOTE: this code is a little outdated wrt the dia-shape-art.c code! */

static inline GdkJoinStyle
dia_join_style_to_x (DiaJoinStyle js)
{
	switch (js) {
	case DIA_JOIN_MITER:
		return GDK_JOIN_MITER;
	case DIA_JOIN_ROUND:
		return GDK_JOIN_ROUND;
	case DIA_JOIN_BEVEL:
		return GDK_JOIN_BEVEL;
	default:
		g_warning ("Invalid DiaJoinStyle type: %d.", js);
		return GDK_JOIN_MITER;
	}
}

static inline GdkCapStyle
dia_cap_style_to_x (DiaCapStyle cs)
{
	switch (cs) {
	case DIA_CAP_BUTT:
		return GDK_CAP_BUTT;
	case DIA_CAP_ROUND:
		return GDK_CAP_ROUND;
	case DIA_CAP_SQUARE:
		return GDK_CAP_PROJECTING;
	default:
		g_warning ("Invalid DiaCapStyle type: %d.", cs);
		return GDK_CAP_BUTT;
	}
}

/* Setting GC variables, from gnome-canvas-line.c... */
static void
set_gc_foreground (DiaCanvasViewItem *item, DiaShape *shape)
{
	GdkColor c;

	if (!item->gc)
		return;
	/*dia_canvas_view_color_to_gdk (DIA_CANVAS_VIEW (GNOME_CANVAS_ITEM (item)->canvas),
				      &shape->any.color, &c); */
	c.pixel = gnome_canvas_get_color_pixel (GNOME_CANVAS_ITEM (item)->canvas,
																 shape->color);
	gdk_gc_set_foreground (item->gc, &c);
}

static int
set_gc_width (DiaCanvasViewItem *item, DiaShape *shape)
{
	int width;
	gdouble line_width = 1.0;
	GdkCapStyle cap = GDK_CAP_BUTT;
	GdkJoinStyle join = GDK_JOIN_MITER;

	if (!item->gc)
		return 0.0;

	switch (shape->type) {
	case DIA_SHAPE_PATH:
		line_width = ((DiaShapePath*) shape)->line_width;
		cap = dia_cap_style_to_x (((DiaShapePath*) shape)->cap),
		join = dia_cap_style_to_x (((DiaShapePath*) shape)->join);
		break;
	case DIA_SHAPE_ELLIPSE:
		line_width = ((DiaShapeEllipse*) shape)->line_width;
		break;
	default:
		break;
	}
	width = (int) (line_width * GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit + 0.5);

	gdk_gc_set_line_attributes (item->gc, width, GDK_LINE_SOLID, cap, join);

	return width;
}

/*
 * Paths
 *
 * TODO: Create a nice struct instead of this hackish solution...
 */
static void path_free (DiaShapeViewInfo *view_info);

static inline void
path_update (DiaShapePath *shape, DiaCanvasViewItem *item,
	     double *affine, ArtSVP *clip_path, int flags,
	     DiaShapeViewInfo *view_info)
{
	gint i, len = 0;
	GnomeCanvasItem *gitem = GNOME_CANVAS_ITEM (item);
	GdkPoint *points;
	ArtVpath *vp;
	
	if (!shape || (shape->vpath == NULL))
		return;

	vp = shape->vpath;
	while (vp[len].code != ART_END)
		len++;

	if (len == 0)
		return;

	points = g_new (GdkPoint, len + 1);
	points->x = len;
	points->y = (gint16) set_gc_width (item, (DiaShape*) shape);

	view_info->free_func = path_free;
	view_info->data = points;

	points = &points[1];
	
	if (affine) {
		for (i = 0; i < len; i++) {
			points[i].x = (gint16) (vp[i].x * affine[0] + vp[i].y * affine[2] + affine[4]);
			points[i].y = (gint16) (vp[i].x * affine[1] + vp[i].y * affine[3] + affine[5]);
		}
	} else {
		for (i = 0; i < len; i++) {
			points[i].x = (gint16) vp[i].x;
			points[i].y = (gint16) vp[i].y;
		}
	}

	//gnome_canvas_update_bbox (gitem, gitem->x1, gitem->y1, gitem->x2, gitem->y2);
	gnome_canvas_request_redraw (gitem->canvas, gitem->x1, gitem->y1,
				     gitem->x2, gitem->y2);
}

static inline void
path_render (DiaShape *shape, DiaCanvasViewItem *item,
	     GdkDrawable *drawable, int x, int y, int width, int height,
	     DiaShapeViewInfo *view_info)
{
	GdkPoint *points, *pp, *p;
	gint len, i;

	if (!view_info || !view_info->data)
		return;

	points = view_info->data;
	len = points[0].x;
	points = &points[1];
	
	pp = g_new (GdkPoint, len);
	memcpy (pp, points, len * sizeof(GdkPoint));
	for (i = 0, p = pp; i < len; i++, p++) {
		p->x -= x;
		p->y -= y;
	}
	set_gc_foreground (item, shape);
	set_gc_width (item, shape);
	gdk_draw_lines (drawable, item->gc, pp, len);
	g_free (pp);
}

static void
path_free (DiaShapeViewInfo *view_info)
{
	GdkPoint *points = view_info->data;
	int len, i;
	DiaRectangle bb;
	GnomeCanvasItem *gitem = view_info->key;
	gint lw;
	
	len = points[0].x;
	lw = points[0].y;
	points = &points[1];
	bb.left = bb.right = points[0].x;
	bb.top = bb.bottom = points[0].y;
	for (i = 1; i < len; i++) {
		DiaPoint p;
		p.x = points[i].x;
		p.y = points[i].y;
		dia_rectangle_add_point (&bb, &p);
	}

	gnome_canvas_request_redraw (gitem->canvas, bb.left -lw, bb.top - lw,
				     bb.right + lw, bb.bottom + lw);

	g_free (view_info->data);
}

/*
 * Ellipse
 */

typedef struct {
	gint x, y, width, height, line_width;
} EllipseData;

static void ellipse_free (DiaShapeViewInfo *view_info);

static inline void
ellipse_update (DiaShape *shape, DiaCanvasViewItem *item,
		double *affine, ArtSVP *clip_path, int flags,
		DiaShapeViewInfo *view_info)
{
	GnomeCanvasItem *gitem = (GnomeCanvasItem*) item;
	EllipseData *data;
	DiaShapeEllipse *ell = (DiaShapeEllipse*) shape;

	data = g_new (EllipseData,1);

	data->x = ell->center.x * affine[0] + ell->center.y * affine[2] + affine[4];
	data->y = ell->center.x * affine[1] + ell->center.y * affine[3] + affine[5];
	data->width = ell->width * affine[0] + ell->height * affine[2];
	data->height = ell->width * affine[1] + ell->height * affine[3];

	data->x -= data->width / 2;
	data->y -= data->height / 2;
	data->line_width = set_gc_width (item, shape);

	view_info->data = data;
	view_info->free_func = ellipse_free;
	gnome_canvas_request_redraw (gitem->canvas, gitem->x1, gitem->y1,
				     gitem->x2, gitem->y2);
}

static inline void
ellipse_render (DiaShape *shape, DiaCanvasViewItem *item,
		GdkDrawable *drawable, int x, int y, int width, int height,
		DiaShapeViewInfo *view_info)
{
	EllipseData *data = view_info->data;

	set_gc_foreground (item, shape);
	set_gc_width (item, shape);
	gdk_draw_arc (drawable, item->gc, FALSE /* filled */,
		      data->x - x, data->y - y,
		      data->width, data->height, 0, 360 * 64);
}

static void
ellipse_free (DiaShapeViewInfo *view_info)
{
	GnomeCanvasItem *item = view_info->key;
	EllipseData *data = view_info->data;

	gnome_canvas_request_redraw (item->canvas, data->x - data->line_width / 2,
				     data->y - data->line_width / 2,
				     data->x + data->width + data->line_width +1,
				     data->y + data->height + data->line_width +1);
	g_free (view_info->data);
	view_info->data = NULL;
}

ArtSVP*
dia_shape_x_update (DiaShape *shape, DiaCanvasViewItem *item,
		    double *affine, ArtSVP *clip_path, int flags)
{
	DiaShapeViewInfo *view_info = NULL;
	GnomeCanvas *view;
	gboolean is_new = FALSE;

	g_assert (DIA_IS_CANVAS_VIEW_ITEM (item));
	
	view = GNOME_CANVAS_ITEM (item)->canvas;
	
	/* First, find the old shape-item-info (if none, create a new entry)
	 */
	view_info = dia_shape_get_view_info (shape, item);

	if (!view_info) {
		view_info = g_new0 (DiaShapeViewInfo, 1);
		shape->view_info = g_list_append (shape->view_info, view_info);
		view_info->key = (gpointer) item;
		is_new = TRUE;
	} else {
		if (dia_shape_need_update (shape))
			dia_shape_view_info_clear (view_info);
		else
			return NULL;
	}

	g_assert (DIA_IS_CANVAS_VIEW_ITEM (view_info->key));
			
	switch (shape->type) {
	case DIA_SHAPE_PATH:
		path_update ((DiaShapePath*) shape, item,
			     affine, clip_path, flags, view_info);
		break;
	case DIA_SHAPE_ELLIPSE:
		ellipse_update (shape, item, affine, clip_path, flags,
				view_info);
		break;
	case DIA_SHAPE_TEXT:
		break;
	case DIA_SHAPE_IMAGE:
		break;
	case DIA_SHAPE_WIDGET:
		break;
	default:
		break;
	}

	if (!is_new)
		dia_shape_is_updated (shape);

	return NULL;
}

void
dia_shape_x_render (DiaShape *shape, DiaCanvasViewItem *item,
		    GdkDrawable *drawable, int x, int y, int width, int height)
{
	DiaShapeViewInfo *view_info;

	g_assert (DIA_IS_CANVAS_VIEW_ITEM (item));

	view_info = dia_shape_get_view_info  (shape, item);
	if (!view_info)
		return;

	g_assert (DIA_IS_CANVAS_VIEW_ITEM (view_info->key));

	switch (shape->type) {
	case DIA_SHAPE_PATH:
		path_render (shape, item, drawable, x, y, width, height,
			     view_info);
		break;
	case DIA_SHAPE_ELLIPSE:
		ellipse_render (shape, item, drawable, x, y, width, height,
				view_info);
		break;
	case DIA_SHAPE_TEXT:
		break;
	case DIA_SHAPE_IMAGE:
		break;
	case DIA_SHAPE_WIDGET:
		break;
	default:
		break;
	}
}

PangoLayout*
dia_shape_x_get_pango_layout (void)
{
	g_warning (G_STRLOC": No default font layout has been set...");
	return NULL;
}

