/* dia-shape.h
 * Copyright (C) 2000  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.h"
#include "dia-canvas.h"
#include <libart_lgpl/art_affine.h>
#include <libart_lgpl/art_misc.h>
#include <math.h>

static void dia_shape_path_free		(DiaShapePath *path);
static void dia_shape_bezier_free	(DiaShapeBezier *bezier);
static void dia_shape_ellipse_free	(DiaShapeEllipse *ellipse);
static void dia_shape_text_free		(DiaShapeText *text);
static void dia_shape_image_free	(DiaShapeImage *image);

/**
 * dia_shape_clean_view_info:
 * @shape: 
 * @key: 
 *
 * Find the DiaShapeViewInfo that belongs to @key. In most cases @key
 * is the #DiaCanvasViewItem object that draws the shape.
 *
 * Return value: 
 **/
DiaShapeViewInfo*
dia_shape_get_view_info (DiaShape *shape, gpointer key)
{
	GList *l;
	for (l = shape->view_info; l != NULL; l = l->next) {
		if (((DiaShapeViewInfo*) l->data)->key == key) {
			//g_message ("Have view info");
			return (DiaShapeViewInfo*) l->data;
		}
	}
	return NULL;
}

/**
 * dia_shape_view_info_clear:
 * @view_info: 
 *
 * Empty the @view_info structure.
 **/
void
dia_shape_view_info_clear (DiaShapeViewInfo *view_info)
{
	if (view_info->free_func)
		view_info->free_func (view_info);
	view_info->free_func = NULL;
	view_info->data = NULL;
}

/**
 * dia_shape_view_info_remove:
 * @shape: 
 * @view_info: 
 *
 * Remove @view_info from the shape. @view_info is also freed.
 **/
void
dia_shape_view_info_remove (DiaShape *shape, DiaShapeViewInfo *view_info)
{
	dia_shape_view_info_clear (view_info);
	shape->view_info = g_list_remove (shape->view_info, view_info);
	g_free (view_info);
}

/*
 * DiaShape -- updates
 */
static inline void
request_update (DiaShape *shape)
{
	if (!shape->update_cnt)
		shape->update_cnt = g_list_length (shape->view_info);
}

void
dia_shape_request_update (DiaShape *shape)
{
	request_update (shape);
}

gboolean
dia_shape_need_update (DiaShape *shape)
{
	g_return_val_if_fail (shape != NULL, FALSE);

	return (shape->update_cnt > 0 ? TRUE : FALSE);
}

void
dia_shape_is_updated (DiaShape *shape)
{
	g_return_if_fail (shape != NULL);

	if (shape->update_cnt > 0)
		shape->update_cnt--;
}

/*
 * DiaShape -- creation and destruction
 */
GType
dia_shape_get_type (DiaShape *shape)
{
	g_return_val_if_fail (shape != NULL, 0);

	switch (shape->type) {
	case DIA_SHAPE_PATH:
		return DIA_TYPE_SHAPE_PATH;
	case DIA_SHAPE_BEZIER:
		return DIA_TYPE_SHAPE_BEZIER;
	case DIA_SHAPE_ELLIPSE:
		return DIA_TYPE_SHAPE_ELLIPSE;
	case DIA_SHAPE_TEXT:
		return DIA_TYPE_SHAPE_TEXT;
	case DIA_SHAPE_IMAGE:
		return DIA_TYPE_SHAPE_IMAGE;
	default:
		g_warning("No GType for shape type %d", shape->type);
	}
	return 0;
}

DiaShape*
dia_shape_new (DiaShapeType type)
{
	DiaShape *shape = NULL;
	DiaShapePath *path;
	DiaShapeBezier *bezier;
	DiaShapeEllipse *ellipse;
	DiaShapeText *text;
	DiaShapeImage *image;
	DiaShapeClip *clip;

	switch (type) {
	case DIA_SHAPE_PATH:
		path = g_new0 (DiaShapePath, 1);
  		shape = (DiaShape*) path;
		path->fill_color = DIA_COLOR_A(0,0,0,0);
		path->fill = DIA_FILL_NONE;
		path->join = DIA_JOIN_ROUND;
		path->cap = DIA_CAP_ROUND;
		path->cyclic = FALSE;
		path->clipping = FALSE;
		path->line_width = 0.1;
		path->dash.offset = 0.0;
		path->dash.n_dash = 0;
		path->dash.dash = NULL;
		break;
	case DIA_SHAPE_BEZIER:
		bezier = g_new0 (DiaShapeBezier, 1);
		shape = (DiaShape*) bezier;
		bezier->join = DIA_JOIN_ROUND;
		bezier->cap = DIA_CAP_ROUND;
		bezier->line_width = 0.1;
		bezier->fill = DIA_FILL_NONE;
		bezier->fill_color = DIA_COLOR_A(0,0,0,0);
		bezier->cyclic = FALSE;
		bezier->clipping = FALSE;
		bezier->dash.offset = 0.0;
		bezier->dash.n_dash = 0;
		bezier->dash.dash = NULL;
		break;
	case DIA_SHAPE_ELLIPSE:
		ellipse = g_new0 (DiaShapeEllipse, 1);
		shape = (DiaShape*) ellipse;
		ellipse->center.x = 0.0;
		ellipse->center.y = 0.0;
		ellipse->width = 1.0;
		ellipse->height = 1.0;
		ellipse->line_width = 0.1;
		ellipse->fill = DIA_FILL_NONE;
		ellipse->fill_color = DIA_COLOR_A(0, 0, 0, 0);
		ellipse->clipping = FALSE;
		ellipse->dash.offset = 0.0;
		ellipse->dash.n_dash = 0;
		ellipse->dash.dash = NULL;
		break;
	case DIA_SHAPE_TEXT:
		text = g_new0 (DiaShapeText, 1);
		shape = (DiaShape*) text;
		text->font_desc = NULL;
		text->text = NULL;
		text->need_free = TRUE;
		text->justify = FALSE;
		text->markup = TRUE;
		text->wrap_mode = DIA_WRAP_WORD;
		text->line_spacing = 0;
		text->alignment = PANGO_ALIGN_LEFT;
		text->max_width = G_MAXINT;
		text->max_height = G_MAXINT;
		text->text_width = 0.0;
		art_affine_identity (text->affine);
		text->cursor = -1;
		break;
	case DIA_SHAPE_IMAGE:
		image = g_new0 (DiaShapeImage, 1);
		shape = (DiaShape*) image;
		image->pixbuf = NULL;
		art_affine_identity (image->affine);
		break;
	case DIA_SHAPE_WIDGET:
		//shape = g_new0 (DiaShapeWidget, 1);
		break;
	case DIA_SHAPE_CLIP:
		clip = g_new0 (DiaShapeClip, 1);
		shape = (DiaShape*) clip;
		clip->clip.x0 = clip->clip.x1 = 0.0;
		clip->clip.y0 = clip->clip.y1 = 0.0;
		break;
	default:
		g_assert_not_reached ();
	}

	g_assert (shape != NULL);
  
	/* Those initializations don't need to take place: */
	shape->type = type;
	shape->color = DIA_COLOR (0, 0, 0);
	shape->update_cnt = 0;
	shape->visibility = DIA_SHAPE_VISIBLE;

	return shape;
}

void
dia_shape_free (DiaShape *shape)
{
	GList *l;
	DiaShapeViewInfo *view_info;

	g_return_if_fail (shape != NULL);

	switch (shape->type) {
	case DIA_SHAPE_PATH:
		dia_shape_path_free ((DiaShapePath*) shape);
		break;
	case DIA_SHAPE_BEZIER:
		dia_shape_bezier_free ((DiaShapeBezier*) shape);
		break;
	case DIA_SHAPE_ELLIPSE:
		dia_shape_ellipse_free ((DiaShapeEllipse*) shape);
		break;
	case DIA_SHAPE_TEXT:
		dia_shape_text_free ((DiaShapeText*) shape);
		break;
	case DIA_SHAPE_IMAGE:
		dia_shape_image_free ((DiaShapeImage*) shape);
		break;
	default:
		break;
	}

	/* Free the list of data added by DiaCanvasViewItem's */
	for (l = shape->view_info; l != NULL; l = l->next) {
		view_info = (DiaShapeViewInfo*) l->data;
		if (view_info && view_info->free_func)
			view_info->free_func (view_info);
		g_free (view_info);
	}
	g_list_free (shape->view_info);
	g_free (shape);
}

/*
 * Generic attributes
 */
void
dia_shape_set_visibility (DiaShape *shape, DiaShapeVisibility vis)
{
	g_return_if_fail (shape != NULL);

	shape->visibility = vis;

	request_update (shape);
}

void
dia_shape_set_color (DiaShape *shape, DiaColor color)
{
	g_return_if_fail (shape != NULL);
	
	shape->color = color;

	request_update (shape);
}


gboolean
dia_shape_get_bounds (DiaShape *shape, DiaRectangle *bb)
{
	if (!shape) return FALSE;

	return FALSE;
}


/*
 * Path like shapes
 */
static void
dia_shape_path_free (DiaShapePath *path)
{
	if (path->vpath)
		art_free (path->vpath);
	path->vpath = NULL;
}


void
dia_shape_line (DiaShape *shape, DiaPoint *start, DiaPoint *end)
{
	DiaShapePath *path = (DiaShapePath*) shape;
	ArtVpath *vpath;
 
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);
	g_return_if_fail (start != NULL);
	g_return_if_fail (end != NULL);
  
	if (path->vpath)
		vpath = path->vpath = art_renew (path->vpath, ArtVpath, 3);
	else
		vpath = path->vpath = art_new (ArtVpath, 3);
  
	vpath[0].code = ART_MOVETO;
	vpath[0].x = start->x;
	vpath[0].y = start->y;
	vpath[1].code = ART_LINETO;
	vpath[1].x = end->x;
	vpath[1].y = end->y;
	vpath[2].code = ART_END;
	vpath[2].x = 0;
	vpath[2].y = 0;

	dia_shape_path_set_cyclic (shape, FALSE);

	request_update (shape);
}

void
dia_shape_rectangle (DiaShape *shape,
		     DiaPoint *upper_left, DiaPoint *lower_right)
{
	DiaShapePath *path = (DiaShapePath*) shape;
	ArtVpath *vpath;

	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);
	g_return_if_fail (upper_left != NULL);
	g_return_if_fail (lower_right != NULL);
  
	if (path->vpath)
		vpath = path->vpath = art_renew (path->vpath, ArtVpath, 5);
	else
		vpath = path->vpath = art_new (ArtVpath, 5);
  
	vpath[0].code = ART_MOVETO;
	vpath[0].x = upper_left->x;
	vpath[0].y = upper_left->y;
	vpath[1].code = ART_LINETO;
	vpath[1].x = lower_right->x;
	vpath[1].y = upper_left->y;
	vpath[2].code = ART_LINETO;
	vpath[2].x = lower_right->x;
	vpath[2].y = lower_right->y;
	vpath[3].code = ART_LINETO;
	vpath[3].x = upper_left->x;
	vpath[3].y = lower_right->y;
	vpath[4].code = ART_END;
	vpath[4].x = 0;
	vpath[4].y = 0;

	dia_shape_path_set_cyclic (shape, TRUE);

	request_update (shape);
}

/**
 * dia_shape_polyline:
 * @shape: 
 * @n_points: 
 * @points: 
 *
 * Create a line with @n_points elements. @points is an array of @n_points
 * #DiaPoint elements.
 **/
void
dia_shape_polyline (DiaShape *shape, guint n_points, DiaPoint *points)
{
	DiaShapePath *path = (DiaShapePath*) shape;
	ArtVpath *vpath;
	guint i;
  
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);
	g_return_if_fail (n_points > 0);
	g_return_if_fail (points != NULL);
  
	if (path->vpath)
		vpath = path->vpath = art_renew (path->vpath, ArtVpath, n_points + 1);
	else
		vpath = path->vpath = art_new (ArtVpath, n_points + 1);

	vpath[0].code = ART_MOVETO;
	vpath[0].x = points[0].x;
	vpath[0].y = points[0].y;
	for (i = 1; i < n_points; i++) {
		vpath[i].code = ART_LINETO;
		vpath[i].x = points[i].x;
		vpath[i].y = points[i].y;
	}
	vpath[i].code = ART_END;
	vpath[i].x = 0.0;
	vpath[i].y = 0.0;

	dia_shape_path_set_cyclic (shape, FALSE);

	request_update (shape);
}

/**
 * dia_shape_polygon:
 * @shape: 
 * @n_points: 
 * @points: 
 *
 * Like dia_shape_polyline(), but in this case the line is closed.
 **/
void
dia_shape_polygon (DiaShape *shape, guint n_points, DiaPoint *points)
{
	DiaShapePath* path = (DiaShapePath*) shape;
	ArtVpath *vpath;
	guint i;
  
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);
	g_return_if_fail (n_points > 0);
	g_return_if_fail (points != NULL);

	if (path->vpath)
		vpath = path->vpath = art_renew (path->vpath, ArtVpath, n_points + 1);
	else
		vpath = path->vpath = art_new (ArtVpath, n_points + 1);

	vpath[0].code = ART_MOVETO;
	vpath[0].x = points[0].x;
	vpath[0].y = points[0].y;
	for (i = 1; i < n_points; i++) {
		vpath[i].code = ART_LINETO;
		vpath[i].x = points[i].x;
		vpath[i].y = points[i].y;
	}
	vpath[i].code = ART_END;
	vpath[i].x = 0.0;
	vpath[i].y = 0.0;
	dia_shape_path_set_cyclic (shape, TRUE);

	request_update (shape);
}

/**
 * dia_shape_arc:
 * @shape: 
 * @begin: 
 * @middle: 
 * @end: 
 *
 * Not implemented yet.
 **/
void
dia_shape_arc (DiaShape *shape,
	       DiaPoint *begin, DiaPoint *middle, DiaPoint *end)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	request_update (shape);
}

/**
 * dia_shape_path_set_fill_color:
 * @shape: 
 * @fill_color: 
 *
 * Set the color that is to be used if the shape is to be filled. The fill
 * style can be set with dia_shape_path_set_fill().
 **/
void
dia_shape_path_set_fill_color (DiaShape *shape, DiaColor fill_color)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	((DiaShapePath*) shape)->fill_color = fill_color;

	request_update (shape);
}

/**
 * dia_shape_path_set_line_width:
 * @shape: 
 * @line_width: 
 *
 * Set the line width for a path like shape.
 **/
void
dia_shape_path_set_line_width (DiaShape *shape, gdouble line_width) 
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);
	g_return_if_fail (line_width > 0.0);

	((DiaShapePath*) shape)->line_width = line_width;

	request_update (shape);
}


/**
 * dia_shape_path_set_fill:
 * @shape: 
 * @fill: 
 *
 * Set the fill style for the path.
 **/
void
dia_shape_path_set_fill (DiaShape *shape, DiaFillStyle fill)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	((DiaShapePath*) shape)->fill = fill;

	request_update (shape);
}


/**
 * dia_shape_path_set_cyclic:
 * @shape: 
 * @cyclic: 
 *
 * Set the path to cyclic. Cyclic paths are closed.
 **/
void
dia_shape_path_set_cyclic (DiaShape *shape, gboolean cyclic)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	((DiaShapePath*) shape)->cyclic = cyclic;

	request_update (shape);
}

/**
 * dia_shape_path_set_clipping:
 * @shape: 
 * @clipping: 
 *
 * If the clipping attribute is set, the shape will be used as a clipping path
 * Note that clipping paths can also contain a path including filling.
 **/
void
dia_shape_path_set_clipping (DiaShape *shape, gboolean clipping)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	((DiaShapePath*) shape)->clipping = clipping;

	request_update (shape);
}

/**
 * dia_shape_path_set_dash:
 * @shape: 
 * @offset: 
 * @n_dash: 
 * @dash: 
 *
 * Set the dash style for the line.
 **/
void
dia_shape_path_set_dash (DiaShape *shape, gdouble offset,
		    guint n_dash, gdouble *dash)
{
	DiaShapePath* path = (DiaShapePath*) shape;
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	if (path->dash.dash)
		g_free (path->dash.dash);

	path->dash.offset = offset;
	path->dash.n_dash = n_dash;
	if (n_dash > 0) {
		path->dash.dash = g_new (gdouble, n_dash);
		memcpy (path->dash.dash, dash, n_dash * sizeof(gdouble));
	} else
		path->dash.dash = NULL;

	request_update (shape);
}


/**
 * dia_shape_path_set_join:
 * @shape: 
 * @join: 
 *
 * Set the join style for a line.
 **/
void
dia_shape_path_set_join (DiaShape *shape, DiaJoinStyle join)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	((DiaShapePath*) shape)->join = join;

	request_update (shape);
}

/**
 * dia_shape_path_set_cap:
 * @shape: 
 * @cap: 
 *
 * The the line end (cap) style for a line, thsi property is not very useful
 * if a path is set to cyclic.
 **/
void
dia_shape_path_set_cap (DiaShape *shape, DiaCapStyle cap)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_PATH);

	((DiaShapePath*) shape)->cap = cap;

	request_update (shape);
}

/**
 * dia_shape_path_is_clip_path:
 * @shape: 
 *
 *
 *
 * Return value: TRUE if the path is indeed a clip path.
 **/
gboolean
dia_shape_path_is_clip_path (DiaShape *shape)
{
	return ((shape->type == DIA_SHAPE_PATH) && ((DiaShapePath*) shape)->clipping);
}

/*
 * Bezier
 */
static void
dia_shape_bezier_free (DiaShapeBezier *bezier)
{
	if (bezier->bpath);
		art_free (bezier->bpath);
	bezier->bpath = NULL;
}

/**
 * dia_shape_bezier:
 * @shape: 
 * @start: starting point for the bezier curve.
 * @n_points: the number of points, should be a multiple of 3.
 * @points: Array of @n_points points
 *
 * Create a bezier shape. @points should contain the amount of points as
 * specified by @n_points. For each point in the bezier curve three points
 * are used: one for the actual point on the line and two for the weight
 * of the curve.
 **/
void
dia_shape_bezier (DiaShape *shape, DiaPoint *start,
		  guint n_points, DiaPoint *points)
{
	DiaShapeBezier *bezier = (DiaShapeBezier*) shape;
	ArtBpath *path;
	guint i, n;
	
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);
	g_return_if_fail (n_points % 3 == 0);

	if (bezier->bpath)
		path = bezier->bpath = art_renew (bezier->bpath, ArtBpath, n_points / 3 + 2);
	else
		path = bezier->bpath = art_new (ArtBpath, n_points / 3 + 2);

	path[0].code = ART_MOVETO;
	path[0].x1 = 0.0;
	path[0].y1 = 0.0;
	path[0].x2 = 0.0;
	path[0].y2 = 0.0;
	path[0].x3 = start->x;
	path[0].y3 = start->y;

	for (i = 1, n = 0; n < n_points; i++) {
		path[i].code = ART_CURVETO;
		path[i].x1 = points[n].x;
		path[i].y1 = points[n++].y;
		path[i].x2 = points[n].x;
		path[i].y2 = points[n++].y;
		path[i].x3 = points[n].x;
		path[i].y3 = points[n++].y;
	}
	path[i].code = ART_END;
	path[i].x1 = 0.0;
	path[i].y1 = 0.0;
	path[i].x2 = 0.0;
	path[i].y2 = 0.0;
	path[i].x3 = 0.0; //points[n_points - 1].x;
	path[i].y3 = 0.0; //points[n_points - 1].y;

	request_update (shape);
}

void
dia_shape_bezier_set_fill_color (DiaShape *shape, DiaColor fill_color)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	((DiaShapeBezier*) shape)->fill_color = fill_color;

	request_update (shape);
}

void
dia_shape_bezier_set_fill (DiaShape *shape, DiaFillStyle fill)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	((DiaShapeBezier*) shape)->fill = fill;

	request_update (shape);
}

void
dia_shape_bezier_set_line_width (DiaShape *shape, gdouble line_width) 
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);
	g_return_if_fail (line_width > 0.0);

	((DiaShapeBezier*) shape)->line_width = line_width;

	request_update (shape);
}

void
dia_shape_bezier_set_join (DiaShape *shape, DiaJoinStyle join)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	((DiaShapeBezier*) shape)->join = join;

	request_update (shape);
}

void
dia_shape_bezier_set_cap  (DiaShape *shape, DiaCapStyle cap)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	((DiaShapeBezier*) shape)->cap = cap;

	request_update (shape);
}

void
dia_shape_bezier_set_cyclic (DiaShape *shape, gboolean cyclic)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	((DiaShapeBezier*) shape)->cyclic = cyclic;

	request_update (shape);
}

void
dia_shape_bezier_set_clipping (DiaShape *shape, gboolean clipping)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	((DiaShapeBezier*) shape)->clipping = clipping;

	request_update (shape);
}

void
dia_shape_bezier_set_dash (DiaShape *shape, gdouble offset, guint n_dash,
			   gdouble *dash)
{
	DiaShapeBezier* path = (DiaShapeBezier*) shape;
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_BEZIER);

	if (path->dash.dash)
		g_free (path->dash.dash);

	path->dash.offset = offset;
	path->dash.n_dash = n_dash;
	if (n_dash > 0) {
		path->dash.dash = g_new (gdouble, n_dash);
		memcpy (path->dash.dash, dash, n_dash * sizeof(gdouble));
	} else
		path->dash.dash = NULL;

	request_update (shape);
}

gboolean
dia_shape_bezier_is_clip_path (DiaShape *shape)
{
	return ((shape->type == DIA_SHAPE_BEZIER) && ((DiaShapeBezier*) shape)->clipping);
}

/*
 * Ellipse
 */
static void
dia_shape_ellipse_free (DiaShapeEllipse *ellipse)
{
}

void
dia_shape_ellipse (DiaShape *shape, DiaPoint *center,
		   gdouble width, gdouble height)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_ELLIPSE);
	g_return_if_fail (center != NULL);

	((DiaShapeEllipse*) shape)->center = *center;
	((DiaShapeEllipse*) shape)->width = width;
	((DiaShapeEllipse*) shape)->height = height;


	request_update (shape);
}

void
dia_shape_ellipse_set_fill_color (DiaShape *shape, DiaColor fill_color)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_ELLIPSE);

	((DiaShapeEllipse*) shape)->fill_color = fill_color;

	request_update (shape);
}

void
dia_shape_ellipse_set_line_width (DiaShape *shape, gdouble line_width) 
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_ELLIPSE);
	g_return_if_fail (line_width > 0.0);

	((DiaShapeEllipse*) shape)->line_width = line_width;

	request_update (shape);
}


void
dia_shape_ellipse_set_fill (DiaShape *shape, DiaFillStyle fill)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_ELLIPSE);

	((DiaShapeEllipse*) shape)->fill = fill;

	request_update (shape);
}

void
dia_shape_ellipse_set_clipping (DiaShape *shape, gboolean clipping)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_ELLIPSE);

	((DiaShapeEllipse*) shape)->clipping = clipping;

	request_update (shape);
}

void
dia_shape_ellipse_set_dash (DiaShape *shape, gdouble offset, guint n_dash,
			   gdouble *dash)
{
	DiaShapeEllipse* ellipse = (DiaShapeEllipse*) shape;
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_ELLIPSE);

	if (ellipse->dash.dash)
		g_free (ellipse->dash.dash);

	ellipse->dash.offset = offset;
	ellipse->dash.n_dash = n_dash;
	if (n_dash > 0) {
		ellipse->dash.dash = g_new (gdouble, n_dash);
		memcpy (ellipse->dash.dash, dash, n_dash * sizeof(gdouble));
	} else
		ellipse->dash.dash = NULL;

	request_update (shape);
}

gboolean
dia_shape_ellipse_is_clip_path (DiaShape *shape)
{
	return ((shape->type == DIA_SHAPE_ELLIPSE) && ((DiaShapeEllipse*) shape)->clipping);
}

/*
 * Text
 */
static void
dia_shape_text_free (DiaShapeText *text)
{
	if (text->text && text->need_free)
		g_free (text->text);
	text->text = NULL;
	if (text->font_desc)
		pango_font_description_free (text->font_desc);
	text->font_desc = NULL;
}

static void
dia_shape_text_real_set_text (DiaShapeText *stext, const gchar* text)
{
	if (stext->text && stext->need_free)
		g_free (stext->text);
	stext->text = (gchar*) text;
}

void
dia_shape_text (DiaShape *shape, PangoFontDescription *font_desc,
		const gchar *text)
{
	DiaShapeText *stext = (DiaShapeText*) shape;

	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (text != NULL);
	
	dia_shape_text_real_set_text (stext, text ? g_strdup (text) : NULL);
	stext->need_free = TRUE;
	dia_shape_text_set_font_description (shape, font_desc);

	request_update (shape);
}

void
dia_shape_text_set_font_description (DiaShape *shape,
				     PangoFontDescription *font_desc)
{
	DiaShapeText *text = (DiaShapeText*) shape;

	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	if (text->font_desc) {
		pango_font_description_free (text->font_desc);
		text->font_desc = NULL;
	}
	if (font_desc != NULL)
		text->font_desc = pango_font_description_copy (font_desc);

	request_update (shape);
}

void
dia_shape_text_set_text (DiaShape *shape, const gchar *text)
{
	DiaShapeText *stext = (DiaShapeText*) shape;

	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (text != NULL);

	dia_shape_text_real_set_text (stext, text ? g_strdup (text) : NULL);
	stext->need_free = TRUE;

	request_update (shape);
}

void
dia_shape_text_set_static_text (DiaShape *shape, const gchar *text)
{
	DiaShapeText *stext = (DiaShapeText*) shape;

	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (text != NULL);

	dia_shape_text_real_set_text (stext, text);
	stext->need_free = FALSE;

	request_update (shape);
}

void
dia_shape_text_set_affine (DiaShape *shape, gdouble affine[6])
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (affine != NULL);

	memcpy (((DiaShapeText*) shape)->affine, affine, 6 * sizeof (gdouble));

	request_update (shape);
}

void
dia_shape_text_set_pos (DiaShape *shape, DiaPoint *pos)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	((DiaShapeText*) shape)->affine[4] = pos->x;
	((DiaShapeText*) shape)->affine[5] = pos->y;

	request_update (shape);
}

/**
 * dia_shape_text_set_max_width:
 * @shape: 
 * @width: 
 *
 * Set the max width of the text box. If the text is wider than @width,
 * the text is clipped at @width.
 **/
void
dia_shape_text_set_max_width (DiaShape *shape, gdouble width)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (width >= 0);

	((DiaShapeText*) shape)->max_width = width;

	request_update (shape);
}

/**
 * dia_shape_text_set_max_height:
 * @shape: 
 * @height: 
 *
 * Set the height of the text box. 
 **/
void
dia_shape_text_set_max_height (DiaShape *shape, gdouble height)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (height >= 0);

	((DiaShapeText*) shape)->max_height = height;

	request_update (shape);
}

/**
 * dia_shape_text_set_text_width:
 * @shape: 
 * @width: 
 *
 * Set the width of the text. Text will be word wrapped if that option is
 * enabled and the text is actually longer than the @text_width.
 **/
void
dia_shape_text_set_text_width (DiaShape *shape, gdouble width)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);
	g_return_if_fail (width >= 0);

	((DiaShapeText*) shape)->text_width = width;

	request_update (shape);
}

/**
 * dia_shape_text_set_line_spacing:
 * @shape: 
 * @line_spacing: 
 *
 *
 **/
void
dia_shape_text_set_line_spacing (DiaShape *shape, gdouble line_spacing)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	((DiaShapeText*) shape)->line_spacing = line_spacing;

	request_update (shape);
}

/**
 * dia_shape_text_set_justify:
 * @shape: 
 * @justify: 
 *
 *
 **/
void
dia_shape_text_set_justify (DiaShape *shape, gboolean justify)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	((DiaShapeText*) shape)->justify = justify;

	request_update (shape);
}

void
dia_shape_text_set_markup (DiaShape *shape, gboolean markup)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	((DiaShapeText*) shape)->markup = markup;

	request_update (shape);
}

/**
 * dia_shape_text_set_wrap_mode:
 * @shape: 
 * @wrap_mode: 
 *
 * Set the wrapping mode for the text shape. 
 **/
void
dia_shape_text_set_wrap_mode (DiaShape *shape, DiaWrapMode wrap_mode)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	((DiaShapeText*) shape)->wrap_mode = wrap_mode;

	request_update (shape);
}

/**
 * dia_shape_text_set_alignment:
 * @shape: 
 * @alignment: 
 *
 * Set the text alignment for a shape (left, right, center, justified).
 **/
void
dia_shape_text_set_alignment (DiaShape *shape, PangoAlignment alignment)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_TEXT);

	((DiaShapeText*) shape)->alignment = alignment;

	request_update (shape);
}

/**
 * dia_shape_text_to_pango_layout:
 * @shape: 
 * @fill: Add information of teh @shape to the #PangoLayout.
 *
 * Create a #PangoLayout based on the information of the @shape. The maximum
 * width and height, nor the affine transformation are added to the layout.
 *
 * Return value: A new #PangoLayout.
 **/
PangoLayout*
dia_shape_text_to_pango_layout (DiaShape *shape, gboolean fill)
{
	PangoLayout *layout;

	g_return_val_if_fail (shape != NULL, NULL);
	g_return_val_if_fail (shape->type == DIA_SHAPE_TEXT, NULL);

	layout = dia_canvas_get_pango_layout ();
	g_return_val_if_fail (layout != NULL, NULL);

	if (fill)
		dia_shape_text_fill_pango_layout (shape, layout);

	return layout;
}

/**
 * dia_shape_text_fill_pango_layout:
 * @shape: 
 * @layout: 
 *
 * Create a #PangoLayout object from a #DiaShapeText.
 **/
void
dia_shape_text_fill_pango_layout (DiaShape *shape, PangoLayout *layout)
{
	DiaShapeText *text = (DiaShapeText*) shape;
	PangoFontDescription *font_desc;
	
	if (text->font_desc) 
		font_desc = pango_font_description_copy_static (text->font_desc);
	else
		font_desc = pango_font_description_copy_static
				(pango_context_get_font_description
					 (pango_layout_get_context (layout)));

	pango_layout_set_font_description (layout, font_desc);
	pango_font_description_free (font_desc);

	if (text->text) {
		if (text->markup)
			pango_layout_set_markup (layout, text->text, -1);
		else
			pango_layout_set_text (layout, text->text, -1);
	}
	if (text->text_width > 0.0)
		pango_layout_set_width (layout, ceil(text->text_width * PANGO_SCALE));
	else
		pango_layout_set_width (layout, -1);

	pango_layout_set_justify (layout, text->justify);

	switch (text->wrap_mode) {
	case DIA_WRAP_CHAR:
		pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
		break;
	default:
		pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
		break;
	}

	pango_layout_set_spacing (layout, (gint) text->line_spacing * PANGO_SCALE);
	pango_layout_set_alignment (layout, text->alignment);
}

/**
 * dia_shape_text_cursor_from_pos:
 * @shape: A #DiaShapeText
 * @pos: cursor position, in item relative coordinates.
 * @cursor: Set the cursor.
 *
 * Find the cursor position belonging to the position @pos. 
 *
 * Return value: 
 **/
gboolean
dia_shape_text_cursor_from_pos (DiaShape *shape, DiaPoint *pos, gint *cursor)
{
	DiaShapeText *text = (DiaShapeText*) shape;
	PangoLayout *layout;
	DiaPoint cursor_pos;
	gint index, trailing;
	gboolean in;
	gdouble inv[6];

	g_return_val_if_fail (shape != NULL, FALSE);
	g_return_val_if_fail (shape->type == DIA_SHAPE_TEXT, FALSE);
	g_return_val_if_fail (pos != NULL, FALSE);
	g_return_val_if_fail (cursor != NULL, FALSE);

	g_warning (G_STRLOC": Do no longer use this function!!");
	
	layout = dia_shape_text_to_pango_layout (shape, TRUE);
	g_assert (layout != NULL);

	art_affine_invert (inv, text->affine);

	/* Compensate centered and right aligned text: */
	if (text->alignment != PANGO_ALIGN_LEFT) {
		gint w;
		gdouble dw;
		pango_layout_get_size (layout, &w, NULL);
		pango_layout_set_width (layout, w);
		w = (w + PANGO_SCALE / 2) / PANGO_SCALE;
		dw = (gdouble) w - ceil (text->max_width);
		if (text->alignment == PANGO_ALIGN_CENTER)
			dw /= 2.0;
		inv[4] += dw;
	}

	cursor_pos.x = pos->x * inv[0] + pos->y * inv[2] + inv[4];
	cursor_pos.y = pos->x * inv[1] + pos->y * inv[3] + inv[5];
	
	in = pango_layout_xy_to_index (layout,
				       (int) (cursor_pos.x * PANGO_SCALE),
				       (int) (cursor_pos.y * PANGO_SCALE),
				       &index, &trailing);

	g_object_unref (layout);

	*cursor = index + trailing;

	return in;
}

/*
 * Image
 */
static void
dia_shape_image_free (DiaShapeImage *image)
{
	if (image->pixbuf)
		gdk_pixbuf_unref (image->pixbuf);
	image->pixbuf = NULL;
}

void
dia_shape_image (DiaShape *shape, GdkPixbuf *image)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_IMAGE);
	g_return_if_fail (GDK_IS_PIXBUF (image));

	if (((DiaShapeImage*)shape)->pixbuf)
		gdk_pixbuf_unref (((DiaShapeImage*)shape)->pixbuf);

	((DiaShapeImage*)shape)->pixbuf = image;
	gdk_pixbuf_ref (image);

	request_update (shape);
}

void
dia_shape_image_set_pos (DiaShape *shape, DiaPoint *pos)
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_IMAGE);
	g_return_if_fail (pos != NULL);

	((DiaShapeImage*) shape)->affine[4] = pos->x;
	((DiaShapeImage*) shape)->affine[5] = pos->y;

	request_update (shape);
}

void
dia_shape_image_set_affine (DiaShape *shape, gdouble affine[6])
{
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_IMAGE);
	g_return_if_fail (affine != NULL);

	memcpy (((DiaShapeImage*) shape)->affine, affine, 6 * sizeof (gdouble));

	request_update (shape);
}

/*
 * Clip path
 */
void
dia_shape_clip (DiaShape *shape, gdouble left, gdouble top,
		gdouble right, gdouble bottom)
{
	g_warning ("DiaShapeClip is now obsolete, use DiaShapePath with clipping==TRUE");
	g_return_if_fail (shape != NULL);
	g_return_if_fail (shape->type == DIA_SHAPE_CLIP);

	((DiaShapeClip*) shape)->clip.x0 = left;
	((DiaShapeClip*) shape)->clip.y0 = top;
	((DiaShapeClip*) shape)->clip.x1 = right;
	((DiaShapeClip*) shape)->clip.y1 = bottom;

	request_update (shape);
}

