#ifndef __NR_ARENA_SHAPE_H__
#define __NR_ARENA_SHAPE_H__

/*
 * RGBA display list system for inkscape
 *
 * Author:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *
 * Copyright (C) 2001-2002 Lauris Kaplinski
 * Copyright (C) 2001 Ximian, Inc.
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#define NR_TYPE_ARENA_SHAPE (nr_arena_shape_get_type ())
#define NR_ARENA_SHAPE(obj) (NR_CHECK_INSTANCE_CAST ((obj), NR_TYPE_ARENA_SHAPE, NRArenaShape))
#define NR_IS_ARENA_SHAPE(obj) (NR_CHECK_INSTANCE_TYPE ((obj), NR_TYPE_ARENA_SHAPE))

#include "display/curve.h"
#include "display/canvas-bpath.h"
#include "forward.h"
#include "sp-paint-server.h"
#include "nr-arena-item.h"

#include "../color.h"

#include "../livarot/Shape.h"

NRType nr_arena_shape_get_type (void);

struct NRArenaShape : public NRArenaItem {
	class Paint {
	public:
		enum Type {
			NONE,
			COLOR,
			SERVER
		};

		Paint() : _type(NONE), _server(NULL) {
			sp_color_set_rgb_rgba32(&_color, 0);
		}
		Paint(Paint const &p) { _assign(p); }
		~Paint() { clear(); }

		Type type() const { return _type; }
		SPPaintServer *server() const { return _server; }
		SPColor const &color() const { return _color; }

		Paint &operator=(Paint const &p) {
			set(p);
			return *this;
		}

		void set(Paint const &p) {
			clear();
			_assign(p);
		}
		void set(SPColor const &color) {
			clear();
			_type = COLOR;
			sp_color_copy(&_color, &color);
		}
		void set(SPPaintServer *server) {
			clear();
			if (server) {
				_type = SERVER;
				_server = server;
				sp_object_ref(_server, NULL);
			}
		}
		void clear() {
			if ( _type == SERVER ) {
				sp_object_unref(_server, NULL);
				_server = NULL;
			}
			_type = NONE;
		}

	private:
		Type _type;
		SPColor _color;
		SPPaintServer *_server;

		void _assign(Paint const &p) {
			_type = p._type;
			_server = p._server;
			sp_color_copy(&_color, &p._color);
			if (_server) {
				sp_object_ref(_server, NULL);
			}
		}
	};

	enum FillRule {
		EVEN_ODD,
		NONZERO
	};

	enum CapType {
		ROUND_CAP,
		SQUARE_CAP,
		BUTT_CAP
	};

	enum JoinType {
		ROUND_JOIN,
		BEVEL_JOIN,
		MITRE_JOIN
	};

	/* Shape data */
	SPCurve *curve;
	SPStyle *style;
	NRRect paintbox;
	/* State data */
	NR::Matrix ctm;
	
	SPPainter *fill_painter;
	SPPainter *stroke_painter;
	// the 2 cached polygons, for rasterizations uses
	Shape *fill_shp;
	Shape *stroke_shp;
	// delayed_shp=true means the *_shp polygons are not computed yet
	// they'll be computed on demand in *_render(), *_pick() or *_clip()
	// the goal is to not uncross polygons that are outside the viewing region
	bool    delayed_shp;
	// approximate bounding box, for the case when the polygons have been delayed
	NRRectL approx_bbox;
	// cache for transformations: cached_fill and cached_stroke are
	// polygons computed for the cached_fctm and cache_sctm respectively
	// when the transformation changes interactively (tracked by the
	// SP_OBJECT_USER_MODIFIED_FLAG_B), we check if it's an isometry wrt
	// the cached ctm. if it's an isometry, just apply it to the cached
	// polygon to get the *_shp polygon.  Otherwise, recompute so this
	// works fine for translation and rotation, but not scaling and
	// skewing
	NR::Matrix cached_fctm;
	NR::Matrix cached_sctm;
	
	Shape    *cached_fill;
	Shape    *cached_stroke;
	/* Markers */
	NRArenaItem *markers;

	static NRArenaShape *create(NRArena *arena) {
		NRArenaShape *obj=reinterpret_cast<NRArenaShape *>(nr_object_new(NR_TYPE_ARENA_SHAPE));
		obj->init(arena);
		return obj;
	}

	void setFill(SPPaintServer *server);
	void setFill(SPColor const &color);
	void setFillOpacity(double opacity);
	void setFillRule(FillRule rule);

	void setStroke(SPPaintServer *server);
	void setStroke(SPColor const &color);
	void setStrokeOpacity(double opacity);
	void setStrokeWidth(double width);
	void setLineCap(CapType cap);
	void setLineJoin(JoinType join);
	void setMitreLimit(double limit);

        void setPaintBox(NR::Rect const &pbox);

	void _invalidateCachedFill() {
		if (cached_fill) {
			delete cached_fill;
			cached_fill = NULL;
		}
	}
	void _invalidateCachedStroke() {
		if (cached_stroke) {
			delete cached_stroke;
			cached_stroke = NULL;
		}
	}

	struct Style {
		Style() : opacity(0.0) {}
		Paint paint;
		double opacity;
	};
	struct FillStyle : public Style {
		FillStyle() : rule(EVEN_ODD) {}
		FillRule rule;
	} _fill;
	struct StrokeStyle : public Style {
		StrokeStyle()
		: cap(ROUND_CAP), join(ROUND_JOIN),
		  width(0.0), mitre_limit(0.0)
		{}

		CapType cap;
		JoinType join;
		double width;
		double mitre_limit;
	} _stroke;
};

struct NRArenaShapeClass {
	NRArenaItemClass parent_class;
};

void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve, bool justTrans);
void nr_arena_shape_set_style (NRArenaShape *shape, SPStyle *style);
void nr_arena_shape_set_paintbox (NRArenaShape *shape, const NRRect *pbox);

#endif
