/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre
 *			Copyright (c) Telecom ParisTech 2000-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 "nodes_stacks.h"
#include "visual_manager.h"

#if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR)

static void ifs2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
{
	u32 i;
	SFVec2f *pts;
	u32 ci_count, c_count;
	Bool started;
	M_IndexedFaceSet2D *ifs2D;
	M_Coordinate2D *coord;

	if (! gf_node_dirty_get(node)) return;

	ifs2D = (M_IndexedFaceSet2D *)node;
	coord = (M_Coordinate2D *)ifs2D->coord;
	drawable_reset_path(stack);
	gf_node_dirty_clear(node, 0);
	drawable_mark_modified(stack, tr_state);


	c_count = coord->point.count;
	ci_count = ifs2D->coordIndex.count;
	pts = coord->point.vals;

	if (ci_count > 0) {
		started = 0;
		for (i=0; i < ci_count; i++) {
			if (ifs2D->coordIndex.vals[i] == -1) {
				gf_path_close(stack->path);
				started = 0;
			} else if (!started) {
				started = 1;
				gf_path_add_move_to_vec(stack->path, &pts[ifs2D->coordIndex.vals[i]]);
			} else {
				gf_path_add_line_to_vec(stack->path, &pts[ifs2D->coordIndex.vals[i]]);
			}
		}
		if (started) gf_path_close(stack->path);
	} else if (c_count) {
		gf_path_add_move_to_vec(stack->path, &pts[0]);
		for (i=1; i < c_count; i++) {
			gf_path_add_line_to_vec(stack->path, &pts[i]);
		}
		gf_path_close(stack->path);
	}
}


static void IFS2D_Draw(GF_Node *node, GF_TraverseState *tr_state)
{
	u32 i, count, ci_count;
#if 0 //unused
	u32 j, ind_col, num_col;
	SFVec2f center, end;
	SFColor col_cen;
	GF_EVGStencil *grad;
	u32 *colors;
#endif
	SFVec2f start;
	SFVec2f *pts;
	SFColor col;
	Fixed alpha;
	DrawableContext *ctx = tr_state->ctx;
	M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node;
	M_Coordinate2D *coord = (M_Coordinate2D*) ifs2D->coord;
	M_Color *color = (M_Color *) ifs2D->color;

	col.red = col.green = col.blue = 0;
	/*simple case, no color specified*/
	if (!ifs2D->color) {
		visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state);
		visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state);
		return;
	}

	/*if default face use first color*/
	ci_count = ifs2D->coordIndex.count;
	pts = coord->point.vals;

	if (ci_count == 0) {
		col = (ifs2D->colorIndex.count > 0) ? color->color.vals[ifs2D->colorIndex.vals[0]] : color->color.vals[0];

		alpha = INT2FIX(GF_COL_A(ctx->aspect.fill_color)) / 255;
		if (!alpha || !ctx->aspect.pen_props.width) {
			alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255;
			ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue);
		} else {
			ctx->aspect.fill_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue);
		}
		visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state);
		visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state);
		return;
	}

	/*we have color per faces so we need N path :(*/
	if (! ifs2D->colorPerVertex) {
		GF_Path *path = gf_path_new();

		count = 0;
		i = 0;
		while (1) {
			gf_path_reset(path);
			start = pts[ifs2D->coordIndex.vals[i]];
			gf_path_add_move_to(path, start.x, start.y);
			i++;

			while (ifs2D->coordIndex.vals[i] != -1) {
				start = pts[ifs2D->coordIndex.vals[i]];
				gf_path_add_line_to(path, start.x, start.y);
				i++;
				if (i >= ci_count) break;
			}
			/*close in ALL cases because even if the start/end points are the same the line join needs to be present*/
			gf_path_close(path);

			col = (ifs2D->colorIndex.count > 0) ? color->color.vals[ifs2D->colorIndex.vals[count]] : color->color.vals[count];

			alpha = INT2FIX(GF_COL_A(ctx->aspect.fill_color)) / 255;
			if (!alpha) {
				alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255;
				ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue);
			} else {
				ctx->aspect.fill_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue);
			}

			visual_2d_texture_path(tr_state->visual, path, ctx, tr_state);
			visual_2d_draw_path(tr_state->visual, path, ctx, NULL, NULL, tr_state);
			count++;
			i++;
			if (i >= ci_count) break;
			ctx->flags &= ~CTX_PATH_FILLED;
			ctx->flags &= ~CTX_PATH_STROKE;
		}
		gf_path_del(path);
		return;
	}

	/*final case, color per vertex means gradient fill/strike*/
	/*not supported, fill default*/
	visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state);
	visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state);
	return;


#if 0 //deprecated
	path = gf_path_new();

	ind_col = 0;
	i = 0;
	while (1) {
		gf_path_reset(path);
		start = pts[ifs2D->coordIndex.vals[i]];
		center = start;
		gf_path_add_move_to(path, start.x, start.y);
		num_col = 1;
		i+=1;
		while (ifs2D->coordIndex.vals[i] != -1) {
			end = pts[ifs2D->coordIndex.vals[i]];
			gf_path_add_line_to(path, end.x, end.y);
			i++;
			center.x += end.x;
			center.y += end.y;
			num_col ++;
			if (i >= ci_count) break;
		}
		gf_path_close(path);
		num_col++;

		alpha = INT2FIX(GF_COL_A(ctx->aspect.fill_color) ) / 255;

		colors = (u32*)gf_malloc(sizeof(u32) * num_col);
		col_cen.blue = col_cen.red = col_cen.green = 0;
		for (j=0; j<num_col-1; j++) {
			if (ifs2D->colorIndex.count > ind_col + j) {
				col = color->color.vals[ifs2D->colorIndex.vals[ind_col + j]];
			} else if (ci_count > ind_col + j) {
				col = color->color.vals[ifs2D->coordIndex.vals[ind_col + j]];
			}
			colors[j] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue);
			col_cen.blue += col.blue;
			col_cen.green += col.green;
			col_cen.red += col.red;
		}
		colors[num_col-1] = colors[0];

		if (ifs2D->colorIndex.count > ind_col) {
			col = color->color.vals[ifs2D->colorIndex.vals[ind_col]];
		} else if (ci_count > ind_col) {
			col = color->color.vals[ifs2D->coordIndex.vals[ind_col]];
		}
		col_cen.blue += col.blue;
		col_cen.green += col.green;
		col_cen.red += col.red;

		gf_evg_stencil_set_vertex_path(grad, path);
		gf_evg_stencil_set_vertex_colors(grad, colors, num_col);

		gf_free(colors);

		col_cen.blue /= num_col;
		col_cen.green /= num_col;
		col_cen.red /= num_col;
		center.x /= num_col;
		center.y /= num_col;
		gf_evg_stencil_set_vertex_center(grad, center.x, center.y, GF_COL_ARGB_FIXED(alpha, col_cen.red, col_cen.green, col_cen.blue) );

		gf_evg_stencil_set_matrix(grad, &ctx->transform);

		/*draw*/
		visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, grad, grad, tr_state);

		gf_evg_stencil_delete(grad);

		//goto next point
		i++;
		ind_col += num_col + 1;
		if (i >= ci_count) break;
		grad = gf_evg_stencil_new(GF_STENCIL_VERTEX_GRADIENT);
		ctx->flags &= ~CTX_PATH_FILLED;
		ctx->flags &= ~CTX_PATH_STROKE;
	}
	gf_path_del(path);
#endif


}

static void TraverseIFS2D(GF_Node *node, void *rs, Bool is_destroy)
{
	DrawableContext *ctx;
	M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node;
	Drawable *stack = (Drawable *)gf_node_get_private(node);
	GF_TraverseState *tr_state = (GF_TraverseState *)rs;

	if (is_destroy) {
		drawable_node_del(node);
		return;
	}
	if (!ifs2D->coord) return;
	if ((gf_node_get_tag(ifs2D->coord) != TAG_MPEG4_Coordinate) && (gf_node_get_tag(ifs2D->coord) != TAG_MPEG4_Coordinate2D))
		return;

	ifs2d_check_changes(node, stack, tr_state);

	switch (tr_state->traversing_mode) {
	case TRAVERSE_DRAW_2D:
		IFS2D_Draw(node, tr_state);
		return;
#ifndef GPAC_DISABLE_3D
	case TRAVERSE_DRAW_3D:
	{
		DrawAspect2D asp;

		if (!stack->mesh) {
			stack->mesh = new_mesh();
			mesh_new_ifs2d(stack->mesh, node);
		}

		memset(&asp, 0, sizeof(DrawAspect2D));
		drawable_get_aspect_2d_mpeg4(node, &asp, tr_state);
		if (ifs2D->color && !GF_COL_A(asp.fill_color) ) {
			/*use special func to disable outline recompute and handle recompute ourselves*/
			StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, stack, &asp, tr_state->appear, NULL, 0, tr_state);
			if (!si->mesh_outline) {
				si->mesh_outline = new_mesh();
				mesh_new_ils(si->mesh_outline, ifs2D->coord, &ifs2D->coordIndex, ifs2D->color, &ifs2D->colorIndex, ifs2D->colorPerVertex, 1);
			}
			visual_3d_mesh_strike(tr_state, si->mesh_outline, asp.pen_props.width, asp.line_scale, asp.pen_props.dash);
		} else {
			visual_3d_draw_2d_with_aspect(stack, tr_state, &asp);
		}
		return;
	}
#endif
	case TRAVERSE_PICK:
		vrml_drawable_pick(stack, tr_state);
		return;
	case TRAVERSE_GET_BOUNDS:
		gf_path_get_bounds(stack->path, &tr_state->bounds);
		return;
	case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
		if (tr_state->visual->type_3d) return;
#endif

		ctx = drawable_init_context_mpeg4(stack, tr_state);
		if (!ctx) return;
		drawable_finalize_sort(ctx, tr_state, NULL);
		return;
	}
}

static void IFS2D_SetColorIndex(GF_Node *node, GF_Route *route)
{
	M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node;
	if (node) {
		gf_sg_vrml_field_copy(&ifs2D->colorIndex, &ifs2D->set_colorIndex, GF_SG_VRML_MFINT32);
		gf_sg_vrml_mf_reset(&ifs2D->set_colorIndex, GF_SG_VRML_MFINT32);
	}
}

static void IFS2D_SetCoordIndex(GF_Node *node, GF_Route *route)
{
	M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node;
	if (node) {
		gf_sg_vrml_field_copy(&ifs2D->coordIndex, &ifs2D->set_coordIndex, GF_SG_VRML_MFINT32);
		gf_sg_vrml_mf_reset(&ifs2D->set_coordIndex, GF_SG_VRML_MFINT32);
	}
}

void compositor_init_indexed_face_set2d(GF_Compositor *compositor, GF_Node *node)
{
	M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node;
	Drawable *stack = drawable_stack_new(compositor, node);
	stack->flags = DRAWABLE_USE_TRAVERSE_DRAW;
	gf_node_set_callback_function(node, TraverseIFS2D);
	ifs2D->on_set_colorIndex = IFS2D_SetColorIndex;
	ifs2D->on_set_coordIndex = IFS2D_SetCoordIndex;

#ifdef GPAC_ENABLE_COVERAGE
	if (gf_sys_is_cov_mode()) {
		IFS2D_SetCoordIndex(NULL, NULL);
		IFS2D_SetColorIndex(NULL, NULL);
	}
#endif

}

#endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR)
