/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Cyril Concolato - Jean le Feuvre
 *			Copyright (c) Telecom ParisTech 2005-2023
 *					All rights reserved
 *
 *  This file is part of GPAC / Scene Compositor sub-project
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *
 *  GPAC 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 library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "visual_manager.h"
#include "nodes_stacks.h"

#if !defined(GPAC_DISABLE_COMPOSITOR)

#ifdef GPAC_DISABLE_SVG

Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc)
{
	return 0;
}

#else

Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc)
{
	u32 check_fill, check_stroke;
	Bool check_over, check_outline, check_vis, inside;
	GF_Rect rc;
	u8 ptr_evt;

	ptr_evt = *tr_state->svg_props->pointer_events;

	if (ptr_evt==SVG_POINTEREVENTS_NONE) {
		return 0;
	}

	if (glyph_rc) {
		rc = *glyph_rc;
	} else {
		gf_path_get_bounds(drawable->path, &rc);
	}
	inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0;

	if (ptr_evt==SVG_POINTEREVENTS_BOUNDINGBOX) return inside;

	check_fill = check_stroke = check_over = check_outline = check_vis = 0;
	/*
	check_vis: if set, return FALSE when visible property is not "visible"
	check_fill:
		if 1, checks whether point is over path,
		if 2, checks if the path is painted (even with fill-opacity=0) before
	check_stroke:
		if 1, checks whether point is over path outline,
		if 2, checks if the path outline is painted (even with stroke-opacity=0) before
	*/
	switch (ptr_evt) {
	case SVG_POINTEREVENTS_VISIBLE:
		check_vis = 1;
		check_fill = 1;
		check_stroke = 1;
		break;
	case SVG_POINTEREVENTS_VISIBLEFILL:
		check_vis = 1;
		check_fill = 1;
		break;
	case SVG_POINTEREVENTS_VISIBLESTROKE:
		check_vis = 1;
		check_stroke = 1;
		break;
	case SVG_POINTEREVENTS_VISIBLEPAINTED:
		check_vis = 1;
		check_fill = 2;
		check_stroke = 2;
		break;
	case SVG_POINTEREVENTS_FILL:
		check_fill = 1;
		break;
	case SVG_POINTEREVENTS_STROKE:
		check_stroke = 1;
		break;
	case SVG_POINTEREVENTS_ALL:
		check_fill = 1;
		check_stroke = 1;
		break;
	case SVG_POINTEREVENTS_PAINTED:
		check_fill = 2;
		check_stroke = 2;
		break;
	default:
		return 0;
	}

	/*!!watchout!! asp2D.width is 0 if stroke not visible due to painting properties - we must override this
	for picking*/
	if (check_stroke==1) {
		asp->pen_props.width = tr_state->svg_props->stroke_width ? tr_state->svg_props->stroke_width->value : 0;
	}
	if (!asp->pen_props.width) check_stroke = 0;

	if (check_stroke) {
		/*rough estimation of stroke bounding box to avoid fetching the stroke each time*/
		if (!inside) {
			Fixed width = asp->pen_props.width;
			rc.x -= width;
			rc.y += width;
			rc.width += 2*width;
			rc.height += 2*width;
			inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0;
			if (!inside) return 0;
		}
	} else if (!inside) {
		return 0;
	}

	if (check_vis) {
		if (*tr_state->svg_props->visibility!=SVG_VISIBILITY_VISIBLE) return 0;
	}

	if (check_fill) {
		/*painted or don't care about fill*/
		if ((check_fill!=2) || asp->fill_texture || asp->fill_color) {
			if (glyph_rc) return 1;
			/*point is over path*/
			if (gf_path_point_over(drawable->path, x, y)) return 1;
		}
	}
	if (check_stroke) {
		/*not painted or don't care about stroke*/
		if ((check_stroke!=2) || asp->line_texture || asp->line_color) {
			StrikeInfo2D *si;
			if (glyph_rc) return 1;
			si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, asp, tr_state->appear, NULL, 0, NULL);
			/*point is over outline*/
			if (si && si->outline && gf_path_point_over(si->outline, x, y))
				return 1;
		}
	}
	return 0;
}


void svg_clone_use_stack(GF_Compositor *compositor, GF_TraverseState *tr_state)
{
	u32 i, count;
	count = gf_list_count(tr_state->use_stack);
	gf_list_reset(compositor->hit_use_stack);
	for (i=0; i<count; i++) {
		GF_Node *node = gf_list_get(tr_state->use_stack, i);
		gf_list_add(compositor->hit_use_stack, node);
	}
}

#ifndef GPAC_DISABLE_3D

void svg_drawable_3d_pick(Drawable *drawable, GF_TraverseState *tr_state, DrawAspect2D *asp)
{
	SFVec3f local_pt, world_pt, vdiff;
	SFVec3f hit_normal;
	SFVec2f text_coords;
	u32 i, count;
	Fixed sqdist;
	Bool node_is_over;
	GF_Compositor *compositor;
	GF_Matrix mx;
	GF_Ray r;

	compositor = tr_state->visual->compositor;

	r = tr_state->ray;
	gf_mx_copy(mx, tr_state->model_matrix);
	gf_mx_inverse(&mx);
	gf_mx_apply_ray(&mx, &r);

	/*if we already have a hit point don't check anything below...*/
	if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) {
		GF_Plane p;
		GF_BBox box;
		SFVec3f hit = compositor->hit_world_point;
		gf_mx_apply_vec(&mx, &hit);
		p.normal = r.dir;
		p.d = -1 * gf_vec_dot(p.normal, hit);
		gf_bbox_from_rect(&box, &drawable->path->bbox);

		if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] bounding box of node %s (DEF %s) below current hit point - skipping\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node)));
			return;
		}
	}
	node_is_over = 0;
	if (compositor_get_2d_plane_intersection(&r, &local_pt)) {
		node_is_over = svg_drawable_is_over(drawable, local_pt.x, local_pt.y, asp, tr_state, NULL);
	}

	if (!node_is_over) return;

	hit_normal.x = hit_normal.y = 0;
	hit_normal.z = FIX_ONE;
	text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2;
	text_coords.y = gf_divfix(local_pt.y, drawable->path->bbox.height) + FIX_ONE/2;

	/*check distance from user and keep the closest hitpoint*/
	world_pt = local_pt;
	gf_mx_apply_vec(&tr_state->model_matrix, &world_pt);

	for (i=0; i<tr_state->num_clip_planes; i++) {
		if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is not in clipper half space\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node)));
			return;
		}
	}

	gf_vec_diff(vdiff, world_pt, tr_state->ray.orig);
	sqdist = gf_vec_lensq(vdiff);
	if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILON<sqdist)) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is farther (%g) than current pick (%g)\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node), FIX2FLT(sqdist), FIX2FLT(compositor->hit_square_dist)));
		return;
	}

	compositor->hit_square_dist = sqdist;

	/*also stack any VRML sensors present at the current level. If the event is not catched
	by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
	gf_list_reset(compositor->sensors);
	count = gf_list_count(tr_state->vrml_sensors);
	for (i=0; i<count; i++) {
		gf_list_add(compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
	}

	gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix);
	gf_mx_copy(compositor->hit_local_to_world, mx);
	compositor->hit_local_point = local_pt;
	compositor->hit_world_point = world_pt;
	compositor->hit_world_ray = tr_state->ray;
	compositor->hit_normal = hit_normal;
	compositor->hit_texcoords = text_coords;

	svg_clone_use_stack(compositor, tr_state);
	/*not use in SVG patterns*/
	compositor->hit_appear = NULL;
	compositor->hit_node = drawable->node;
	compositor->hit_text = NULL;
	compositor->hit_use_dom_events = 1;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is under mouse - hit %g %g %g\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node),
	                                      FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z)));
}

#endif

void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state)
{
	DrawAspect2D asp;
	GF_Matrix2D inv_2d;
	Fixed x, y;
	Bool picked = 0;
	GF_Compositor *compositor = tr_state->visual->compositor;
	SVGPropertiesPointers backup_props;
	GF_Matrix2D backup_matrix;
	GF_Matrix mx_3d;
	SVGAllAttributes all_atts;

	if (!drawable->path) return;

	gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);

	memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers));
	gf_svg_apply_inheritance(&all_atts, tr_state->svg_props);
	if (compositor_svg_is_display_off(tr_state->svg_props)) return;

	compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);

	memset(&asp, 0, sizeof(DrawAspect2D));
	drawable_get_aspect_2d_svg(node, &asp, tr_state);

#ifndef GPAC_DISABLE_3D
	if (tr_state->visual->type_3d) {
		svg_drawable_3d_pick(drawable, tr_state, &asp);
		compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
		memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
		return;
	}
#endif
	gf_mx2d_copy(inv_2d, tr_state->transform);
	gf_mx2d_inverse(&inv_2d);
	x = tr_state->ray.orig.x;
	y = tr_state->ray.orig.y;
	gf_mx2d_apply_coords(&inv_2d, &x, &y);

	picked = svg_drawable_is_over(drawable, x, y, &asp, tr_state, NULL);

	if (picked) {
		u32 count, i;
		compositor->hit_local_point.x = x;
		compositor->hit_local_point.y = y;
		compositor->hit_local_point.z = 0;

		gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform);
		gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d);

		compositor->hit_node = drawable->node;
		compositor->hit_use_dom_events = 1;
		compositor->hit_normal.x = compositor->hit_normal.y = 0;
		compositor->hit_normal.z = FIX_ONE;
		compositor->hit_texcoords.x = gf_divfix(x, drawable->path->bbox.width) + FIX_ONE/2;
		compositor->hit_texcoords.y = gf_divfix(y, drawable->path->bbox.height) + FIX_ONE/2;
		svg_clone_use_stack(compositor, tr_state);
		/*not use in SVG patterns*/
		compositor->hit_appear = NULL;

		/*also stack any VRML sensors present at the current level. If the event is not catched
		by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
		gf_list_reset(tr_state->visual->compositor->sensors);
		count = gf_list_count(tr_state->vrml_sensors);
		for (i=0; i<count; i++) {
			gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
		}

		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s is under mouse - hit %g %g 0\n", gf_node_get_log_name(drawable->node), FIX2FLT(x), FIX2FLT(y)));
	}

	compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
}

static void svg_drawable_traverse(GF_Node *node, void *rs, Bool is_destroy,
                                  void (*rebuild_path)(GF_Node *, Drawable *, SVGAllAttributes *),
                                  Bool is_svg_rect, Bool is_svg_path)
{
	GF_Matrix2D backup_matrix;
	GF_Matrix mx_3d;
	DrawableContext *ctx;
	SVGPropertiesPointers backup_props;
	u32 backup_flags;
	Drawable *drawable = (Drawable *)gf_node_get_private(node);
	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
	SVGAllAttributes all_atts;

	if (is_destroy) {
#if USE_GF_PATH
		/* The path is the same as the one in the SVG node, don't delete it here */
		if (is_svg_path) drawable->path = NULL;
#endif
		drawable_node_del(node);
		return;
	}
	gf_assert(tr_state->traversing_mode!=TRAVERSE_DRAW_2D);


	if (tr_state->traversing_mode==TRAVERSE_PICK) {
		svg_drawable_pick(node, drawable, tr_state);
		return;
	}

	/*flatten attributes and apply animations + inheritance*/
	gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
	if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags))
		return;

	/* Recreates the path (i.e the shape) only if the node is dirty */
	if (gf_node_dirty_get(node) & GF_SG_SVG_GEOMETRY_DIRTY) {
		/*the rebuild function is responsible for cleaning the path*/
		rebuild_path(node, drawable, &all_atts);
		gf_node_dirty_clear(node, GF_SG_SVG_GEOMETRY_DIRTY);
		drawable_mark_modified(drawable, tr_state);
	}
	if (drawable->path) {
		if (*(tr_state->svg_props->fill_rule)==GF_PATH_FILL_ZERO_NONZERO) {
			if (!(drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO)) {
				drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO;
				drawable_mark_modified(drawable, tr_state);
			}
		} else {
			if (drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO) {
				drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
				drawable_mark_modified(drawable, tr_state);
			}
		}
	}

	if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
		if (! compositor_svg_is_display_off(tr_state->svg_props)) {
			DrawAspect2D asp;
			gf_path_get_bounds(drawable->path, &tr_state->bounds);
			if (!tr_state->ignore_strike) {
				memset(&asp, 0, sizeof(DrawAspect2D));
				drawable_get_aspect_2d_svg(node, &asp, tr_state);
				if (asp.pen_props.width) {
					StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, NULL, drawable->path, 0, NULL);
					if (si && si->outline) {
						gf_path_get_bounds(si->outline, &tr_state->bounds);
					}
				}
			}
			compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, NULL);
			if (!tr_state->abort_bounds_traverse)
				gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds);
			gf_sc_get_nodes_bounds(node, NULL, tr_state, NULL);

			compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, NULL);
		}
	} else if (tr_state->traversing_mode == TRAVERSE_SORT) {
		/*reset our flags - this may break reuse of nodes and change-detection in dirty-rect algo */
		gf_node_dirty_clear(node, 0);

		if (!compositor_svg_is_display_off(tr_state->svg_props) &&
		        ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) {

			compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);

			ctx = drawable_init_context_svg(drawable, tr_state, all_atts.clip_path);
			if (ctx) {
				if (is_svg_rect) {
					if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {}
					else if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) {}
					else if (ctx->transform.m[1] || ctx->transform.m[3]) {}
					else {
						ctx->flags &= ~CTX_IS_TRANSPARENT;
						if (!ctx->aspect.pen_props.width)
							ctx->flags |= CTX_NO_ANTIALIAS;
					}
				}

				if (all_atts.pathLength && all_atts.pathLength->type==SVG_NUMBER_VALUE)
					ctx->aspect.pen_props.path_length = all_atts.pathLength->value;

#ifndef GPAC_DISABLE_3D
				if (tr_state->visual->type_3d) {
					if (!drawable->mesh) {
						drawable->mesh = new_mesh();
						if (drawable->path) mesh_from_path(drawable->mesh, drawable->path);
					}
					visual_3d_draw_from_context(ctx, tr_state);
					ctx->drawable = NULL;
				} else
#endif
				{
					drawable_finalize_sort(ctx, tr_state, NULL);
				}
			}
			compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
		}
	}

	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
	tr_state->svg_flags = backup_flags;
}

static GF_Err svg_rect_add_arc(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed cx, Fixed cy, Fixed rx, Fixed ry)
{
	Fixed angle, start_angle, end_angle, sweep, _vx, _vy, start_x, start_y;
	s32 i, num_steps;

	if (!gp->n_points) return GF_BAD_PARAM;

	start_x = gp->points[gp->n_points-1].x;
	start_y = gp->points[gp->n_points-1].y;

	//start angle and end angle
	start_angle = gf_atan2(start_y-cy, start_x-cx);
	end_angle = gf_atan2(end_y-cy, end_x-cx);
	sweep = end_angle - start_angle;

	if (sweep<0) sweep += 2*GF_PI;

	num_steps = 16;
	for (i=1; i<=num_steps; i++) {
		angle = start_angle + sweep*i/num_steps;
		_vx = cx + gf_mulfix(rx, gf_cos(angle));
		_vy = cy + gf_mulfix(ry, gf_sin(angle));
		gf_path_add_line_to(gp, _vx, _vy);
	}
	return GF_OK;
}

static void svg_rect_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
	Fixed rx = (atts->rx ? atts->rx->value : 0);
	Fixed ry = (atts->ry ? atts->ry->value : 0);
	Fixed x = (atts->x ? atts->x->value : 0);
	Fixed y = (atts->y ? atts->y->value : 0);
	Fixed width = (atts->width ? atts->width->value : 0);
	Fixed height = (atts->height ? atts->height->value : 0);

	drawable_reset_path(stack);
	if (!width || !height) return;

	/*we follow SVG 1.1 and not 1.2 !!*/
	if (rx || ry) {
		Fixed cx, cy;
		if (rx >= width/2) rx = width/2;
		if (ry >= height/2) ry = height/2;
		if (rx == 0) rx = ry;
		if (ry == 0) ry = rx;
		gf_path_add_move_to(stack->path, x+rx, y);

		if (width-rx!=rx)
			gf_path_add_line_to(stack->path, x+width-rx, y);

		cx = x+width-rx;
		cy = y+ry;
		svg_rect_add_arc(stack->path, x+width, y+ry, cx, cy, rx, ry);

		if (height-ry!=ry)
			gf_path_add_line_to(stack->path, x+width, y+height-ry);

		cx = x+width-rx;
		cy = y+height-ry;
		svg_rect_add_arc(stack->path, x+width-rx, y+height, cx, cy, rx, ry);

		if (width-rx!=rx)
			gf_path_add_line_to(stack->path, x+rx, y+height);

		cx = x+rx;
		cy = y+height-ry;
		svg_rect_add_arc(stack->path, x, y+height-ry, cx, cy, rx, ry);

		if (height-ry!=ry)
			gf_path_add_line_to(stack->path, x, y+ry);

		cx = x+rx;
		cy = y+ry;
		svg_rect_add_arc(stack->path, x+rx, y, cx, cy, rx, ry);

		gf_path_close(stack->path);
	} else {
		gf_path_add_move_to(stack->path, x, y);
		gf_path_add_line_to(stack->path, x+width, y);
		gf_path_add_line_to(stack->path, x+width, y+height);
		gf_path_add_line_to(stack->path, x, y+height);
		gf_path_close(stack->path);
	}
}

static void svg_traverse_rect(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_rect_rebuild, 1, 0);
}

void compositor_init_svg_rect(GF_Compositor *compositor, GF_Node *node)
{
	drawable_stack_new(compositor, node);
	gf_node_set_callback_function(node, svg_traverse_rect);
}

static void svg_circle_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
	Fixed r = 2*(atts->r ? atts->r->value : 0);
	drawable_reset_path(stack);
	gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0), (atts->cy ? atts->cy->value : 0), r, r);
}

static void svg_traverse_circle(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_circle_rebuild, 0, 0);
}

void compositor_init_svg_circle(GF_Compositor *compositor, GF_Node *node)
{
	drawable_stack_new(compositor, node);
	gf_node_set_callback_function(node, svg_traverse_circle);
}

static void svg_ellipse_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
	drawable_reset_path(stack);
	gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0),
	                    (atts->cy ? atts->cy->value : 0),
	                    (atts->rx ? 2*atts->rx->value : 0),
	                    (atts->ry ? 2*atts->ry->value : 0));
}
static void svg_traverse_ellipse(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_ellipse_rebuild, 0, 0);
}

void compositor_init_svg_ellipse(GF_Compositor *compositor, GF_Node *node)
{
	drawable_stack_new(compositor, node);
	gf_node_set_callback_function(node, svg_traverse_ellipse);
}

static void svg_line_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
	drawable_reset_path(stack);
	gf_path_add_move_to(stack->path, (atts->x1 ? atts->x1->value : 0), (atts->y1 ? atts->y1->value : 0));
	gf_path_add_line_to(stack->path, (atts->x2 ? atts->x2->value : 0), (atts->y2 ? atts->y2->value : 0));
}
static void svg_traverse_line(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_line_rebuild, 0, 0);
}

void compositor_init_svg_line(GF_Compositor *compositor, GF_Node *node)
{
	drawable_stack_new(compositor, node);
	gf_node_set_callback_function(node, svg_traverse_line);
}

static void svg_polyline_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
	u32 i, nbPoints;
	drawable_reset_path(stack);
	if (atts->points)
		nbPoints = gf_list_count(*atts->points);
	else
		nbPoints = 0;

	if (nbPoints) {
		SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0);
		gf_path_add_move_to(stack->path, p->x, p->y);
		for (i = 1; i < nbPoints; i++) {
			p = (SVG_Point *)gf_list_get(*atts->points, i);
			gf_path_add_line_to(stack->path, p->x, p->y);
		}
	} else {
		gf_path_add_move_to(stack->path, 0, 0);
	}
}
static void svg_traverse_polyline(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_polyline_rebuild, 0, 0);
}

void compositor_init_svg_polyline(GF_Compositor *compositor, GF_Node *node)
{
	drawable_stack_new(compositor, node);
	gf_node_set_callback_function(node, svg_traverse_polyline);
}

static void svg_polygon_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
	u32 i, nbPoints;
	drawable_reset_path(stack);
	if (atts->points)
		nbPoints = gf_list_count(*atts->points);
	else
		nbPoints = 0;

	if (nbPoints) {
		SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0);
		gf_path_add_move_to(stack->path, p->x, p->y);
		for (i = 1; i < nbPoints; i++) {
			p = (SVG_Point *)gf_list_get(*atts->points, i);
			gf_path_add_line_to(stack->path, p->x, p->y);
		}
	} else {
		gf_path_add_move_to(stack->path, 0, 0);
	}
	/*according to the spec, the polygon path is closed*/
	gf_path_close(stack->path);
}
static void svg_traverse_polygon(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_polygon_rebuild, 0, 0);
}

void compositor_init_svg_polygon(GF_Compositor *compositor, GF_Node *node)
{
	drawable_stack_new(compositor, node);
	gf_node_set_callback_function(node, svg_traverse_polygon);
}


static void svg_path_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts)
{
#if USE_GF_PATH
	drawable_reset_path_outline(stack);
	stack->path = atts->d;
#else
	drawable_reset_path(stack);
	gf_svg_path_build(stack->path, atts->d->commands, atts->d->points);
#endif
}

static void svg_traverse_path(GF_Node *node, void *rs, Bool is_destroy)
{
	svg_drawable_traverse(node, rs, is_destroy, svg_path_rebuild, 0, 1);
}

void compositor_init_svg_path(GF_Compositor *compositor, GF_Node *node)
{
	Drawable *dr = drawable_stack_new(compositor, node);
	gf_path_del(dr->path);
	dr->path = NULL;
	gf_node_set_callback_function(node, svg_traverse_path);
}

#endif //defined(GPAC_DISABLE_SVG)

#endif //!defined(GPAC_DISABLE_COMPOSITOR)
