/*
 *                            COPYRIGHT
 *
 *  librnd, modular 2D CAD framework
 *  Copyright (C) 2026 Tibor 'Igor2' Palinkas
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/librnd
 *    lead developer: http://repo.hu/projects/librnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

/* Export a page or a project (multiple pages) in a self-contained web2.0
   html+css+js+svg, with zoom+pan, paging and search */

#include <librnd/rnd_config.h>
#include <errno.h>
#include <librnd/core/safe_fs.h>
#include <librnd/core/rnd_printf.h>
#include <librnd/core/compat_misc.h>
#include <librnd/hid/hid_init.h>

#include "lht_template.h"
#include "web2_templ_lht.c"

#include "draw_web2.h"


static int web2_insert(FILE *f, const char *prefix, char *name, lht_err_t *err, void *user_data);

/* Install mouse bindings on each page */
static int web2_insert_mouse_bindings(FILE *f, const char *prefix, char *name, lht_err_t *err, rnd_web2_t *pctx)
{
	int orig = pctx->page_idx;

	for(pctx->page_idx = 1; pctx->page_idx <= orig; pctx->page_idx++)
		rnd_lht_temp_exec(pctx->outf, "", pctx->temp, "mouse_bindings", web2_insert, err, pctx);

	pctx->page_idx = orig;
	return 0;
}

/* Print layer buttons */
static int web2_insert_layer_buttons(FILE *f, const char *prefix, char *name, lht_err_t *err, rnd_web2_t *pctx)
{
	int n;
	for(n = 0; n < pctx->layer_names.used; n+=2) {
		const char *id   = pctx->layer_names.array[n];
		const char *name = pctx->layer_names.array[n+1];
		fprintf(pctx->outf, "						              <button class=\"layer-toggle active\" onclick=\"toggleLayer('%s', this)\">%s</button><br>\n", id, name);
	}
	return 0;
}

/* Print <title> content for the whole html being exported (single file or multifile) */
static int web2_insert_html_title(FILE *f, const char *prefix, char *name, lht_err_t *err, rnd_web2_t *pctx)
{
	fprintf(pctx->outf, "%s web2 export: %s", rnd_app.package, rnd_hid_export_fn(pctx->hidlib->loadname));
	return 0;
}

static int web2_insert_search_name_map(FILE *f, const char *prefix, char *name, lht_err_t *err, rnd_web2_t *pctx)
{
	int n;
	for(n = 0; n < pctx->search.used; n+=2) {
		const char *name = pctx->search.array[n];
		int page = (int)pctx->search.array[n+1];
		fprintf(pctx->outf, "				          \"%s\": [\"highlight-%s-pp%d\"]", name, name, page);
		if (n+2 < pctx->search.used)
			fprintf(pctx->outf, ",\n");
		else
			fprintf(pctx->outf, "\n");
	}
	return 0;
}

static int web2_insert_search_page_map(FILE *f, const char *prefix, char *name, lht_err_t *err, rnd_web2_t *pctx)
{
	int n;
	for(n = 0; n < pctx->search.used; n+=2) {
		const char *name = pctx->search.array[n];
		int page = (int)pctx->search.array[n+1];
		fprintf(pctx->outf, "				          \"%s\": %d", name, page-1);
		if (n+2 < pctx->search.used)
			fprintf(pctx->outf, ",\n");
		else
			fprintf(pctx->outf, "\n");
	}
	return 0;
}

static int web2_insert_page_view_boxes(FILE *f, const char *prefix, char *name, lht_err_t *err, rnd_web2_t *pctx)
{
	int n;

	for(n = 0; n < pctx->page_hidlib.used; n++) {
		rnd_design_t *dsg = pctx->page_hidlib.array[n];
		rnd_fprintf(pctx->outf,
			"				      let vb%d = { x: %mm, y: %mm, w: %mm, h: %mm };\n",
			n+1, dsg->dwg.X1, dsg->dwg.Y1, dsg->dwg.X2 - dsg->dwg.X1, dsg->dwg.Y2 - dsg->dwg.Y1
		);
	}

	fprintf(pctx->outf, "				      let page_vb = [ ");

	for(n = 0; n < pctx->page_hidlib.used; n++) {
		if (n > 0)
			fprintf(pctx->outf, ", ");
		fprintf(pctx->outf, "vb%d", n+1);
	}
	fprintf(pctx->outf, "];\n");

	return 0;
}

static int web2_insert(FILE *f, const char *prefix, char *name, lht_err_t *err, void *user_data)
{
	rnd_web2_t *pctx = user_data;

	switch(*name) {
		case 's':
			if (strcmp(name, "search_name_map") == 0) web2_insert_search_name_map(f, prefix, name, err, pctx);
			if (strcmp(name, "search_page_map") == 0) web2_insert_search_page_map(f, prefix, name, err, pctx);
			break;
		case 'p':
			if (strcmp(name, "page_title") == 0) fprintf(f, "%s", pctx->page_title);
			else if (strcmp(name, "page_index") == 0) fprintf(f, "%d", pctx->page_idx);
			else if (strcmp(name, "page_view_boxes") == 0) return web2_insert_page_view_boxes(f, prefix, name, err, pctx);
			break;
		case 'm':
			if (strcmp(name, "mouse_bindings") == 0) return web2_insert_mouse_bindings(f, prefix, name, err, pctx);
			break;
		case 'l':
			if (strcmp(name, "layer_buttons") == 0) return web2_insert_layer_buttons(f, prefix, name, err, pctx);
			break;
		case 'h':
			if (strcmp(name, "html_title") == 0) return web2_insert_html_title(f, prefix, name, err, pctx);
			break;
	}
	return 0;
}


static void free_layer_names(rnd_web2_t *pctx)
{
	int n;

	for(n = 0; n < pctx->layer_names.used; n++)
		free(pctx->layer_names.array[n]);

	pctx->layer_names.used = 0;
}

static void free_search(rnd_web2_t *pctx)
{
	int n;

	for(n = 0; n < pctx->search.used; n+=2)
		free(pctx->search.array[n]);

	pctx->layer_names.used = 0;
}


int rnd_web2_init(rnd_web2_t *pctx, rnd_design_t *hidlib, FILE *f, const char *templ_fn)
{
	char *errmsg;

	memset(pctx, 0, sizeof(rnd_web2_t));
	pctx->hidlib = pctx->svg.hidlib = hidlib;
	pctx->outf = pctx->svg.outf = f;

	if (templ_fn != NULL) {
		char *real_fn;
		real_fn = rnd_fopen_check(hidlib, templ_fn, "r");
		if (real_fn != NULL)
			pctx->temp = lht_dom_load(real_fn, &errmsg);
		free(real_fn);
	}
	else
		pctx->temp = lht_dom_load_string(web2_templ_default_arr, "<embedded template>", &errmsg);

	return pctx->temp == NULL ? -1 : 0;
}


int rnd_web2_uninit(rnd_web2_t *pctx)
{
	free_layer_names(pctx);
	free_search(pctx);
	vtp0_uninit(&pctx->layer_names);
	vtp0_uninit(&pctx->search);
	vtp0_uninit(&pctx->page_hidlib);
	lht_dom_uninit(pctx->temp);
	return 0;
}

int rnd_web2_new_file(rnd_web2_t *pctx, FILE *f, const char *fn)
{
	int ern = errno;

	if (pctx->outf != NULL) {
		rnd_web2_print_footer(pctx);
		fclose(pctx->outf);
	}

	if (f == NULL) {
		rnd_message(RND_MSG_ERROR, "rnd_web2_new_file(): failed to open %s: %s\n", fn, strerror(ern));
		perror(fn);
		return -1;
	}

	pctx->outf = f;
	return 0;
}

int rnd_web2_print_header(rnd_web2_t *pctx)
{
	lht_err_t err;

	return rnd_lht_temp_exec(pctx->outf, "", pctx->temp, "header", web2_insert, &err, pctx);
}


int rnd_web2_print_footer(rnd_web2_t *pctx)
{
	lht_err_t err;

	return rnd_lht_temp_exec(pctx->outf, "", pctx->temp, "footer", web2_insert, &err, pctx);
}

int rnd_web2_print_page_pre(rnd_web2_t *pctx, const char *page_title)
{
	lht_err_t err;
	char tmp[128];
	int rv;

	pctx->page_idx++;
	pctx->page_title = page_title;
	rv = rnd_lht_temp_exec(pctx->outf, "", pctx->temp, "page_pre", web2_insert, &err, pctx);
	rnd_snprintf(tmp, sizeof(tmp), "id=\"mainSvg-%d\"", pctx->page_idx);
	rnd_svg_header_(&pctx->svg, tmp);

	vtp0_append(&pctx->page_hidlib, pctx->svg.hidlib);

	return rv;
}

int rnd_web2_print_page_post(rnd_web2_t *pctx)
{
	lht_err_t err;

	rnd_svg_footer(&pctx->svg);
	return rnd_lht_temp_exec(pctx->outf, "", pctx->temp, "page_post", web2_insert, &err, pctx);
}


void rnd_web2_layer_group_map(rnd_web2_t *pctx, int enable)
{
	if (enable)
		free_layer_names(pctx);
	pctx->mapping = enable;
}


void rnd_web2_layer_group_begin(rnd_web2_t *pctx, long group, const char *name, int is_our_mask)
{
	if (pctx->mapping) {
		char *lgn = rnd_strdup_printf("layer_%ld_%s", group, name);

		vtp0_append(&pctx->layer_names, lgn);
		vtp0_append(&pctx->layer_names, rnd_strdup(name));

		return;
	}
	rnd_svg_layer_group_begin(&pctx->svg, group, name, is_our_mask);
}


void rnd_web2_background(rnd_web2_t *pctx)
{
	rnd_svg_background(&pctx->svg);
}

void rnd_web2_search_append(rnd_web2_t *pctx, const char *name, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	while(pctx->svg.group_open) {
		rnd_svg_group_close(&pctx->svg);
		pctx->svg.group_open--;
	}

	/* Hardwired stroke/fill is to make sure the boxes are invisible in an export;
	   in interactive mode class is going to override these */
	rnd_fprintf(pctx->outf,
		"	                  <rect id=\"highlight-%s-pp%d\" class=\"highlight-rect\" x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" stroke=\"none\" fill=\"none\"/>\n",
		name, pctx->page_idx, x1, y1, x2-x1, y2-y1);
	vtp0_append(&pctx->search, rnd_strdup(name));
	vtp0_append(&pctx->search, (void *)pctx->page_idx);
}


#define SKIP if (pctx->mapping) return;

/* standard HID API */
rnd_hid_gc_t rnd_web2_make_gc(rnd_hid_t *hid)                      { return rnd_svg_make_gc(hid); }
void rnd_web2_destroy_gc(rnd_hid_gc_t gc)                          { rnd_svg_destroy_gc(gc); }
void rnd_web2_set_line_cap(rnd_hid_gc_t gc, rnd_cap_style_t style) { rnd_svg_set_line_cap(gc, style); }
void rnd_web2_set_line_width(rnd_hid_gc_t gc, rnd_coord_t width)   { rnd_svg_set_line_width(gc, width); }
void rnd_web2_set_draw_xor(rnd_hid_gc_t gc, int xor_)              { rnd_svg_set_draw_xor(gc, xor_); }

void rnd_web2_set_crosshair(rnd_hid_t *hid, rnd_coord_t x, rnd_coord_t y, rnd_set_crosshair_t action)
{
	rnd_svg_set_crosshair(hid, x, y, action);
}


/* standard HID API with extra pctx first arg */
void rnd_web2_set_drawing_mode(rnd_web2_t *pctx, rnd_hid_t *hid, rnd_composite_op_t op, rnd_bool direct, const rnd_box_t *screen)
{
	SKIP;
	rnd_svg_set_drawing_mode(&pctx->svg, hid, op, direct, screen);
}

void rnd_web2_set_color(rnd_web2_t *pctx, rnd_hid_gc_t gc, const rnd_color_t *color)
{
	SKIP;
	rnd_svg_set_color(&pctx->svg, gc, color);
}

void rnd_web2_draw_rect(rnd_web2_t *pctx, rnd_hid_gc_t gc, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	SKIP;
	rnd_svg_draw_rect(&pctx->svg, gc, x1, y1, x2, y2);
}

void rnd_web2_draw_line(rnd_web2_t *pctx, rnd_hid_gc_t gc, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	SKIP;
	rnd_svg_draw_line(&pctx->svg, gc, x1, y1, x2, y2);
}

void rnd_web2_draw_arc(rnd_web2_t *pctx, rnd_hid_gc_t gc, rnd_coord_t cx, rnd_coord_t cy, rnd_coord_t width, rnd_coord_t height, rnd_angle_t start_angle, rnd_angle_t delta_angle)
{
	SKIP;
	rnd_svg_draw_arc(&pctx->svg, gc, cx, cy, width, height, start_angle, delta_angle);
}

void rnd_web2_fill_circle(rnd_web2_t *pctx, rnd_hid_gc_t gc, rnd_coord_t cx, rnd_coord_t cy, rnd_coord_t radius)
{
	SKIP;
	rnd_svg_fill_circle(&pctx->svg, gc, cx, cy, radius);
}

void rnd_web2_fill_polygon_offs(rnd_web2_t *pctx, rnd_hid_gc_t gc, int n_coords, rnd_coord_t *x, rnd_coord_t *y, rnd_coord_t dx, rnd_coord_t dy)
{
	SKIP;
	rnd_svg_fill_polygon_offs(&pctx->svg, gc, n_coords, x, y, dx, dy);
}

void rnd_web2_fill_rect(rnd_web2_t *pctx, rnd_hid_gc_t gc, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	SKIP;
	rnd_svg_fill_rect(&pctx->svg, gc, x1, y1, x2, y2);
}

