/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - daignostics/debug
 *  Copyright (C) 2022,2025 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022)
 *
 *  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/sch-rnd
 *    contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html
 *    mailing list: http://www.repo.hu/projects/sch-rnd/contact.html
 */


#include <libcschem/config.h>
#include <librnd/core/actions.h>
#include <librnd/core/plugins.h>
#include <librnd/core/conf.h>
#include <librnd/core/error.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/project.h>
#include <librnd/hid/hid_export.h>
#include <librnd/hid/hid_init.h>
#include <libcschem/event.h>
#include <libcschem/undo.h>
#include <libcschem/plug_library.h>
#include <libcschem/plug_io.h>
#include <libcschem/compile.h>
#include <libcschem/abstract.h>
#include <sch-rnd/font.h>
#include <sch-rnd/util_sheet.h>

extern long sch_rnd_font_score_debug;
static const char csch_acts_FontFind[] = "FontFind(name, style)\n";
static const char csch_acth_FontFind[] = "Finds a font by the same name:style combo pens use";
static fgw_error_t csch_act_FontFind(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *name, *style;
	void *font;

	RND_ACT_CONVARG(1, FGW_STR, FontFind, name = argv[1].val.str);
	RND_ACT_CONVARG(2, FGW_STR, FontFind, style = argv[2].val.str);

	sch_rnd_font_score_debug++;
	font = sch_rnd_font_lookup(name, style);
	rnd_message(RND_MSG_INFO, "FontFind: %p\n", font);
	sch_rnd_font_score_debug--;

	RND_ACT_IRES(0);
	return 0;
}

static const char csch_acts_UndoSplit[] = "UndoSplit()\n";
static const char csch_acth_UndoSplit[] = "Renumber undo serials so they can be undone separately";
static fgw_error_t csch_act_UndoSplit(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	rnd_design_t *hl = RND_ACT_DESIGN;
	csch_sheet_t *sheet = (csch_sheet_t *)hl;
	uundo_item_t *i;
	uundo_serial_t ser, base = 0, cnt;

	ser = -1;

	for(i = sheet->undo.head; i != NULL; i = i->next) {
		if (ser != i->serial) {
			base += 10000;
			ser = i->serial;
			cnt = 0;
		}
		i->serial = base + cnt;
		cnt++;
	}
	sheet->undo.serial = base + cnt;

	rnd_event(&sheet->hidlib, CSCH_EVENT_UNDO_POST, "i", CSCH_UNDO_EV_UNDO);

	RND_ACT_IRES(0);
	return 0;
}

#define DLF_PREFIX "<DumpLibSymbol> "
static const char csch_acts_DumpLibSymbol[] = "DumpLibSymbol(symbolname, [bbox|origin|svg])\n";
static const char csch_acth_DumpLibSymbol[] = "print symbol file and metadata to stdout";
static fgw_error_t csch_act_DumpLibSymbol(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	rnd_design_t *hl = RND_ACT_DESIGN;
	const char *symname, *opt, *basename;
	int rv, n, want_bbox = 0, want_origin = 0, want_svg = 0;
	csch_lib_master_t *master;
	csch_lib_t *libe;
	csch_sheet_t *sheet = (csch_sheet_t *)hl, tmpsheet = {0};
	char *freeme = NULL, *param;
	csch_lib_type_t mask;

	RND_ACT_CONVARG(1, FGW_STR, DumpLibSymbol, symname = argv[1].val.str);

	for(n = 2; n < argc; n++) {
		RND_ACT_CONVARG(n, FGW_STR, DumpLibSymbol, opt = argv[n].val.str);
		if (strcmp(opt, "bbox") == 0) want_bbox = 1;
		else if (strcmp(opt, "origin") == 0) want_origin = 1;
		else if (strcmp(opt, "svg") == 0) want_svg = 1;
		else RND_ACT_FAIL(DumpLibSymbol);
	}

	master = csch_lib_get_master("symbol", 0);
	if (master == NULL) {
		printf(DLF_PREFIX "error failed to get 'master' for the symbol library\n");
		return -1;
	}

	param = strchr(symname, '(');
	if (param != NULL) {
		char *end;

		basename = freeme = rnd_strdup(symname);
		param = freeme + (param - symname);
		*param = '\0';
		param++;
		end = strrchr(param, ')');
		if (end != NULL)
			*end = '\0';
		mask = CSCH_SLIB_PARAMETRIC;
	}
	else {
		basename = symname;
		mask = CSCH_SLIB_STATIC;
	}

	libe = csch_lib_search_master(master, basename, mask);
	if (libe == NULL) {
		printf(DLF_PREFIX "error symbol not found\n");
		RND_ACT_IRES(1);
		goto quit;
	}

	csch_sheet_init(&tmpsheet, (csch_project_t *)sheet->hidlib.project);
	rv = csch_lib_load(sheet, &tmpsheet, libe, param);
	if (rv == 0) {
		htip_entry_t *e;
		csch_cgrp_t *sym = NULL;

		for(e = htip_first(&tmpsheet.direct.id2obj); e != NULL; e = htip_next(&tmpsheet.direct.id2obj, e)) {
			csch_cgrp_t *obj = e->value;
			if (csch_obj_is_grp(&obj->hdr)) {
				sym = obj;
				break;
			}
		}

		if (sym != NULL) {
			printf(DLF_PREFIX "data begin\n");
			if (csch_save_grp(sym, "/dev/stdout", "lihata", 0) != 0)
				printf(DLF_PREFIX "error failed to export symbol group\n");
			printf(DLF_PREFIX "data end\n");

			if (want_bbox)
				rnd_printf(DLF_PREFIX "bbox coord %mI %mI %mI %mI\n", sym->hdr.bbox.x1, sym->hdr.bbox.y1, sym->hdr.bbox.x2, sym->hdr.bbox.y2);
			if (want_origin)
				rnd_printf(DLF_PREFIX "origin coord %mI %mI\n", sym->x - sym->hdr.bbox.x1, sym->y - sym->hdr.bbox.y1);

			if (want_svg) {
				const char *name;

				/* replace name if it's just a template */
				name = csch_attrib_get_str(&sym->attr, "name");
				if ((name == NULL) || (strchr(name, '?') != NULL)) {
					csch_source_arg_t *src = csch_attrib_src_c("fn", 0, 0, NULL);
					csch_attrib_set(&sym->attr, 0, "name", "refdes", src, NULL);
				}

				rnd_exporter = rnd_hid_find_exporter("svg");
				if (rnd_exporter != NULL) {
					int argc = 0;
					char *argv[4], **a;
					csch_abstract_t atmp = {0};

					argv[argc++] = "--outfile";
					argv[argc++] = "/dev/stdout";
					argv[argc] = NULL;
					a = argv;

					sch_rnd_sheet_setup(&tmpsheet, SCH_RND_SSC_PENS | SCH_RND_SSC_PEN_MARK_DEFAULT, NULL, NULL);
					rnd_event(&tmpsheet.hidlib, CSCH_EVENT_SHEET_POSTLOAD, NULL);
					rnd_event(&tmpsheet.hidlib, RND_EVENT_LOAD_POST, NULL);

					csch_abstract_init(&atmp);
					rv = csch_compile_project((csch_project_t *)sheet->hidlib.project, -1, &atmp, 1);
					csch_abstract_uninit(&atmp);

					if (rv == 0) {
						rnd_event(hl, RND_EVENT_EXPORT_SESSION_BEGIN, NULL);
						rnd_exporter->parse_arguments(rnd_exporter, &argc, &a);
						printf(DLF_PREFIX "svg begin\n");
						rnd_exporter->do_export(rnd_exporter, &tmpsheet.hidlib, NULL, NULL);
						printf(DLF_PREFIX "svg end\n");
					}
					else
						printf(DLF_PREFIX "error failed to compile the temporary symbol sheet\n");

					rnd_event(hl, RND_EVENT_EXPORT_SESSION_END, NULL);
					rnd_exporter = NULL;
				}
				else
					printf(DLF_PREFIX "error failed to find the svg export plugin\n");
			}

			RND_ACT_IRES(0);
		}
		else
			printf(DLF_PREFIX "error failed to find the symbol group after load\n");
	}
	else
		printf(DLF_PREFIX "error symbol not found\n");

	rnd_project_remove_design(sheet->hidlib.project, &tmpsheet.hidlib);
	csch_sheet_uninit(&tmpsheet);


	quit:;
	free(freeme);
	return 0;
}


static const char csch_acts_d1[] = "d1()\n";
static const char csch_acth_d1[] = "debug action for development";
static fgw_error_t csch_act_d1(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	RND_ACT_IRES(0);
	return 0;
}


rnd_action_t diag_action_list[] = {
	{"FontFind", csch_act_FontFind, csch_acth_FontFind, csch_acts_FontFind},
	{"UndoSplit", csch_act_UndoSplit, csch_acth_UndoSplit, csch_acts_UndoSplit},
	{"DumpLibSymbol", csch_act_DumpLibSymbol, csch_acth_DumpLibSymbol, csch_acts_DumpLibSymbol},
	{"d1", csch_act_d1, csch_acth_d1, csch_acts_d1}
};

static const char *diag_cookie = "diag plugin";

int pplg_check_ver_diag(int ver_needed) { return 0; }

void pplg_uninit_diag(void)
{
	rnd_remove_actions_by_cookie(diag_cookie);
}

int pplg_init_diag(void)
{
	RND_API_CHK_VER;

	RND_REGISTER_ACTIONS(diag_action_list, diag_cookie)
	return 0;
}
