/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre
 *			Copyright (c) Telecom ParisTech 2007-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 <gpac/utf.h>

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

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

typedef struct
{
	u16 *unicode;
	u16 uni_len;

	GF_Glyph glyph;
	GF_Font *font;
} SVG_GlyphStack;


/*translate string to glyph sequence*/
static GF_Err svg_font_get_glyphs(void *udta, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *lang, Bool *is_rtl)
{
	u32 prev_c;
	u32 len;
	u32 i, gl_idx;
	u16 *utf_res;
	GF_Node *node = (GF_Node *)udta;
	GF_ChildNodeItem *child;
	char *utf8 = (char*) utf_string;

	/*FIXME - use glyphs unicode attributes for glyph substitution*/
	len = utf_string ? (u32) strlen(utf_string) : 0;
	if (!len) {
		*io_glyph_buffer_size = 0;
		return GF_OK;
	}

	if (*io_glyph_buffer_size < len+1) {
		*io_glyph_buffer_size = (u32) len+1;
		return GF_BUFFER_TOO_SMALL;
	}

	len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char**)&utf8);
	if (len == GF_UTF8_FAIL) return GF_IO_ERR;
	/*should not happen*/
	if (utf8) return GF_IO_ERR;

	/*perform bidi relayout*/
	utf_res = (u16 *) glyph_buffer;
	*is_rtl = gf_utf8_reorder_bidi(utf_res, len);

	/*move 16bit buffer to 32bit*/
	for (i=len; i>0; i--) {
		glyph_buffer[i-1] = utf_res[i-1];
	}

	gl_idx = 0;
	prev_c = 0;
	for (i=0; i<len; i++) {
		SVG_GlyphStack *missing_glyph = NULL;
		SVG_GlyphStack *st = NULL;
		child = ((GF_ParentNode *) node)->children;
		while (child) {
			u32 tag = gf_node_get_tag(child->node);
			if (tag==TAG_SVG_missing_glyph) {
				missing_glyph = gf_node_get_private(child->node);
			} else if (tag ==TAG_SVG_glyph) {
				Bool glyph_ok = 0;
				SVGAllAttributes atts;

				st = gf_node_get_private(child->node);
				if (!st) {
					child = child->next;
					continue;
				}

				if (st->glyph.utf_name==glyph_buffer[i]) {
					u32 j, count;
					gf_svg_flatten_attributes((SVG_Element*)child->node, &atts);
					if (!lang) {
						glyph_ok = 1;
					} else {
						if (!atts.lang) {
							glyph_ok = 1;
						} else {
							count = gf_list_count(*atts.lang);
							for (j=0; j<count; j++) {
								char *name = gf_list_get(*atts.lang, j);
								if (!stricmp(name, lang) || strstr(lang, name)) {
									glyph_ok = 1;
									break;
								}
							}
						}
					}
					if (atts.arabic_form) {
						Bool first = (!prev_c || (prev_c==' ')) ? 1 : 0;
						Bool last = ((i+1==len) || (glyph_buffer[i+1]==' ') ) ? 1 : 0;
						if (!strcmp(*atts.arabic_form, "isolated")) {
							if (!first || !last) glyph_ok = 0;
						}
						if (!strcmp(*atts.arabic_form, "initial")) {
							if (!first) glyph_ok = 0;
						}
						if (!strcmp(*atts.arabic_form, "medial")) {
							if (first || last) glyph_ok = 0;
						}
						if (!strcmp(*atts.arabic_form, "terminal")) {
							if (!last) glyph_ok = 0;
						}
					}
					if (glyph_ok) break;
				}
				/*perform glyph substitution*/
				else if (st->uni_len>1) {
					u32 j;
					for (j=0; j<st->uni_len; j++) {
						if (i+j>=len) break;
						if (glyph_buffer[i+j] != st->unicode[j]) break;
					}
					if (j==st->uni_len)
						break;
				}
				st = NULL;
			}
			child = child->next;
		}
		prev_c = glyph_buffer[i];

		if (!st)
			st = missing_glyph;
		glyph_buffer[gl_idx] = st ? st->glyph.ID : 0;
		if (st && st->uni_len>1) i++;

		gl_idx++;
	}
	*io_glyph_buffer_size = /* len = */ gl_idx;

	return GF_OK;
}

/*loads glyph by name - returns NULL if glyph cannot be found*/
static GF_Glyph *svg_font_load_glyph(void *udta, u32 glyph_name)
{
	GF_ChildNodeItem *child = ((GF_ParentNode *) udta)->children;

	while (child) {
		if (gf_node_get_tag(child->node)==TAG_SVG_glyph) {
			SVG_GlyphStack *st = gf_node_get_private(child->node);
			if (st->glyph.ID==glyph_name) {
				return &st->glyph;
			}
		}
		child = child->next;
	}

	return NULL;
}

static void svg_traverse_font(GF_Node *node, void *rs, Bool is_destroy)
{
	if (is_destroy) {
		GF_Font *font = gf_node_get_private(node);
		if (font) {
			gf_font_manager_unregister_font(font->ft_mgr, font);
			if (font->name) gf_free(font->name);
			gf_free(font);
		}
	}
}

static void svg_font_on_load(GF_Node *handler, GF_DOM_Event *event, GF_Node *observer)
{
	GF_Font *font;
	gf_assert(event->currentTarget->ptr_type==GF_DOM_EVENT_TARGET_NODE);
	gf_assert(gf_node_get_tag((GF_Node*)event->currentTarget->ptr)==TAG_SVG_font);
	font = gf_node_get_private((GF_Node*)event->currentTarget->ptr);
	font->not_loaded = 0;

	/*brute-force signaling that all fonts have changed and texts must be recomputed*/
	font->compositor->reset_fonts = 1;
	gf_sc_next_frame_state(font->compositor, GF_SC_DRAW_FRAME);
	font->compositor->fonts_pending--;
}

void compositor_init_svg_font(GF_Compositor *compositor, GF_Node *node)
{
	SVG_handlerElement *handler;
	GF_Err e;
	SVGAllAttributes atts;
	GF_Font *font;
	GF_Node *node_font = gf_node_get_parent(node, 0);
	if (!node_font) return;

	if (gf_node_get_tag(node_font)!=TAG_SVG_font) return;

	gf_svg_flatten_attributes((SVG_Element*)node, &atts);
	if (!atts.font_family) return;

	/*register font to font manager*/
	GF_SAFEALLOC(font, GF_Font);
	if (!font) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg font\n"));
		return;
	}
	e = gf_font_manager_register_font(compositor->font_manager, font);
	if (e) {
		gf_free(font);
		return;
	}
	font->ft_mgr = compositor->font_manager;
	font->compositor = compositor;

	font->get_glyphs = svg_font_get_glyphs;
	font->load_glyph = svg_font_load_glyph;
	font->udta = node_font;
	gf_node_set_private(node_font, font);
	gf_node_set_callback_function(node_font, svg_traverse_font);
	font->name = gf_strdup(atts.font_family->value);

	font->em_size = atts.units_per_em ? FIX2INT( gf_ceil(atts.units_per_em->value) ) : 1000;
	/*Inconsistency between SVG 1.2 and 1.1
		when not specify, ascent and descent are computed based on font.vert-origin-y, WHICH DOES NOT EXIST
	IN Tiny 1.2 !!! We assume it to be 0.
	*/
	font->ascent = atts.ascent ? FIX2INT( gf_ceil(atts.ascent->value) ) : 0;
	if (!font->ascent) font->ascent = font->em_size;
	font->descent = atts.descent ? FIX2INT( gf_ceil(atts.descent->value) ) : 0;
	font->baseline = atts.alphabetic ? FIX2INT( gf_ceil(atts.alphabetic->value) ) : 0;
	font->line_spacing = font->em_size;
	font->styles = 0;
	if (atts.font_style) {
		switch (*atts.font_style) {
		case SVG_FONTSTYLE_ITALIC:
			font->styles |= GF_FONT_ITALIC;
			break;
		case SVG_FONTSTYLE_OBLIQUE:
			font->styles |= GF_FONT_OBLIQUE;
			break;
		}
	}
	if (atts.font_variant && (*atts.font_variant ==SVG_FONTVARIANT_SMALLCAPS))
		font->styles |= GF_FONT_SMALLCAPS;

	if (atts.font_weight) {
		switch(*atts.font_weight) {
		case SVG_FONTWEIGHT_100:
			font->styles |= GF_FONT_WEIGHT_100;
			break;
		case SVG_FONTWEIGHT_LIGHTER:
			font->styles |= GF_FONT_WEIGHT_LIGHTER;
			break;
		case SVG_FONTWEIGHT_200:
			font->styles |= GF_FONT_WEIGHT_200;
			break;
		case SVG_FONTWEIGHT_300:
			font->styles |= GF_FONT_WEIGHT_300;
			break;
		case SVG_FONTWEIGHT_400:
			font->styles |= GF_FONT_WEIGHT_400;
			break;
		case SVG_FONTWEIGHT_NORMAL:
			font->styles |= GF_FONT_WEIGHT_NORMAL;
			break;
		case SVG_FONTWEIGHT_500:
			font->styles |= GF_FONT_WEIGHT_500;
			break;
		case SVG_FONTWEIGHT_600:
			font->styles |= GF_FONT_WEIGHT_600;
			break;
		case SVG_FONTWEIGHT_700:
			font->styles |= GF_FONT_WEIGHT_700;
			break;
		case SVG_FONTWEIGHT_BOLD:
			font->styles |= GF_FONT_WEIGHT_BOLD;
			break;
		case SVG_FONTWEIGHT_800:
			font->styles |= GF_FONT_WEIGHT_800;
			break;
		case SVG_FONTWEIGHT_900:
			font->styles |= GF_FONT_WEIGHT_900;
			break;
		case SVG_FONTWEIGHT_BOLDER:
			font->styles |= GF_FONT_WEIGHT_BOLDER;
			break;
		}
	}

	gf_svg_flatten_attributes((SVG_Element*)node_font, &atts);
	font->max_advance_h = atts.horiz_adv_x ? FIX2INT( gf_ceil(atts.horiz_adv_x->value) ) : 0;

	font->not_loaded = 1;

	/*wait for onLoad event before activating the font, otherwise we may not have all the glyphs*/
	handler = gf_dom_listener_build(node_font, GF_EVENT_LOAD, 0);
	handler->handle_event = svg_font_on_load;
}


static void svg_traverse_glyph(GF_Node *node, void *rs, Bool is_destroy)
{
	if (is_destroy) {
		GF_Font *font;
		GF_Glyph *prev_glyph, *a_glyph;
		SVG_GlyphStack *st = gf_node_get_private(node);
		if (st->unicode) gf_free(st->unicode);

		font = st->font;
		prev_glyph = NULL;
		a_glyph = font->glyph;
		while (a_glyph) {
			if (a_glyph == &st->glyph) break;
			prev_glyph = a_glyph;
			a_glyph = a_glyph->next;
		}
		if (prev_glyph) {
			prev_glyph->next = st->glyph.next;
		} else {
			font->glyph = st->glyph.next;
		}
		gf_free(st);
	}
}

void compositor_init_svg_glyph(GF_Compositor *compositor, GF_Node *node)
{
	u16 utf_name[20];
	u8 *utf8;
	u32 len;
	GF_Rect rc;
	GF_Glyph *glyph;
	GF_Font *font;
	SVG_GlyphStack *st;
	SVGAllAttributes atts;
	GF_Node *node_font = gf_node_get_parent(node, 0);

	/*locate the font node*/
	if (node_font) node_font = gf_node_get_parent(node, 0);
	if (!node_font || (gf_node_get_tag(node_font)!=TAG_SVG_font) ) return;
	font = gf_node_get_private(node_font);
	if (!font) return;

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

	if (gf_node_get_tag(node)==TAG_SVG_missing_glyph) {
		GF_SAFEALLOC(st, SVG_GlyphStack);
		if (!st) return;
		goto reg_common;
	}
	/*we must have unicode specified*/
	if (!atts.unicode) return;

	GF_SAFEALLOC(st, SVG_GlyphStack);
	if (!st) return;
	utf8 = (u8 *) *atts.unicode;
	len = gf_utf8_mbstowcs(utf_name, 200, (const char **) &utf8);
	if (len == GF_UTF8_FAIL) return;
	/*this is a single glyph*/
	if (len==1) {
		st->glyph.utf_name = utf_name[0];
		st->uni_len = 1;
	} else {
		st->glyph.utf_name = (u32) (PTR_TO_U_CAST st);
		st->unicode = gf_malloc(sizeof(u16)*len);
		st->uni_len = (u16) len;
		memcpy(st->unicode, utf_name, sizeof(u16)*len);
	}

reg_common:
	st->glyph.ID = (u32)(PTR_TO_U_CAST st);
	st->font = font;
	st->glyph.horiz_advance = font->max_advance_h;
	if (atts.horiz_adv_x) st->glyph.horiz_advance = FIX2INT( gf_ceil(atts.horiz_adv_x->value) );
	if (atts.d) {
		st->glyph.path = atts.d;
		gf_path_get_bounds(atts.d, &rc);
		st->glyph.width = FIX2INT( gf_ceil(rc.width) );
		st->glyph.height = FIX2INT( gf_ceil(rc.height) );
	}
	st->glyph.vert_advance = st->glyph.height;
	if (!st->glyph.vert_advance)
		st->glyph.vert_advance = font->max_advance_v;

	/*register glyph*/
	if (!font->glyph) {
		font->glyph = &st->glyph;
	} else {
		glyph = font->glyph;
		while (glyph->next) glyph = glyph->next;
		glyph->next = &st->glyph;
	}

	gf_node_set_private(node, st);
	gf_node_set_callback_function(node, svg_traverse_glyph);
}


typedef struct
{
	GF_Font *font;
	GF_Font *alias;
	GF_Compositor *compositor;
	GF_MediaObject *mo;
} FontURIStack;

static Bool svg_font_uri_check(GF_Node *node, FontURIStack *st)
{
	GF_Font *font;
	GF_Node *font_elt;
	SVGAllAttributes atts;
	gf_svg_flatten_attributes((SVG_Element*)node, &atts);
	if (!atts.xlink_href) return 0;

	if (atts.xlink_href->type == XMLRI_ELEMENTID) {
		if (!atts.xlink_href->target) atts.xlink_href->target = gf_sg_find_node_by_name(gf_node_get_graph(node), atts.xlink_href->string+1);
	} else {
		GF_SceneGraph *ext_sg;
		char *font_name = strchr(atts.xlink_href->string, '#');
		if (!font_name) return 0;
		if (!st->mo) {
			st->mo = gf_mo_load_xlink_resource(node, 0, 0, -1);
			if (!st->mo) {
				st->compositor->fonts_pending--;
				return 0;
			}
		}
		ext_sg = gf_mo_get_scenegraph(st->mo);
		if (!ext_sg) {
			st->compositor->fonts_pending--;
			return 0;
		}
		atts.xlink_href->target = gf_sg_find_node_by_name(ext_sg, font_name+1);
		if (!atts.xlink_href->target) {
			st->compositor->fonts_pending--;
			return 0;
		}
	}
	font_elt = atts.xlink_href->target;
	if (gf_node_get_tag(font_elt) != TAG_SVG_font) {
		st->compositor->fonts_pending--;
		return 0;
	}
	font = gf_node_get_private(font_elt);
	if (!font) {
		st->compositor->fonts_pending--;
		return 0;
	}

	st->alias = font;

	gf_mo_is_done(st->mo);
	font->not_loaded = 0;
	return 1;
}

GF_Font *svg_font_uri_get_alias(void *udta)
{
	GF_Node *node = (GF_Node *)udta;
	FontURIStack *st = gf_node_get_private(node);
	if (!st->alias && !svg_font_uri_check(node, st)) {
		return NULL;
	}
	return st->alias;
}

static void svg_traverse_font_face_uri(GF_Node *node, void *rs, Bool is_destroy)
{
	if (is_destroy) {
		FontURIStack *st = gf_node_get_private(node);
		if (st) {
			gf_font_manager_unregister_font(st->font->ft_mgr, st->font);
			if (st->font->name) gf_free(st->font->name);
			gf_free(st->font);
			if (st->mo) gf_mo_unload_xlink_resource(node, st->mo);
			gf_free(st);
		}
	}
}

void compositor_init_svg_font_face_uri(GF_Compositor *compositor, GF_Node *node)
{
	GF_Node *par;
	GF_Font *font;
	FontURIStack *stack;
	GF_Err e;
	SVGAllAttributes atts;

	/*check parent is a font-face-src*/
	par = gf_node_get_parent(node, 0);
	if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face_src)) return;
	/*check parent's parent is a font-face*/
	par = gf_node_get_parent(par, 0);
	if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face)) return;


	gf_svg_flatten_attributes((SVG_Element*)node, &atts);
	if (!atts.xlink_href) return;

	/*get font familly*/
	gf_svg_flatten_attributes((SVG_Element*)par, &atts);
	if (!atts.font_family) return;

	/*if font with the same name exists, don't load*/
	if (gf_compositor_svg_set_font(compositor->font_manager, atts.font_family->value, 0, 1) != NULL)
		return;

	/*register font to font manager*/
	GF_SAFEALLOC(font, GF_Font);
	if (!font) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate font for svg font face URI\n"));
		return;
	}
	
	e = gf_font_manager_register_font(compositor->font_manager, font);
	if (e) {
		gf_free(font);
		return;
	}
	GF_SAFEALLOC(stack, FontURIStack);
	if (!stack) {
		gf_free(font);
		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg font face URI stack\n"));
		return;
	}
	stack->font = font;
	stack->compositor = compositor;

	font->ft_mgr = compositor->font_manager;

	font->get_alias = svg_font_uri_get_alias;
	font->udta = node;
	font->name = gf_strdup(atts.font_family->value);
	gf_node_set_private(node, stack);
	gf_node_set_callback_function(node, svg_traverse_font_face_uri);

	font->not_loaded = 1;
	compositor->fonts_pending++;
	svg_font_uri_check(node, stack);
}


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