/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - Fritzing netlist export
 *  Copyright (C) 2025 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/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 <stdio.h>
#include <string.h>
#include <ctype.h>
#include <genht/htpi.h>
#include <genht/hash.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/safe_fs.h>
#include <librnd/core/plugins.h>
#include <librnd/core/error.h>
#include <libcschem/config.h>
#include <libcschem/plug_io.h>

#include <plugins/lib_netlist_exp/lib_netlist_exp.h>

static csch_plug_io_t efritzing_net;

static int fritzing_export_prio(const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	if (type != CSCH_IOTYP_NETLIST)
		return 0;
	if (rnd_strcasecmp(fmt, "fritzing") == 0)
		return 100;
	return 0;
}

/* assign an unique ID to each component exported */
static int compids;
htpi_t compidht;
static int compid(void *comp)
{
	int id = htpi_get(&compidht, comp);
	if (id == 0) {
		id = ++compids;
		htpi_set(&compidht, comp, id);
	}
	return id;
}

/* Export abstract nets and their connections; there's no field for
   netname in the format (written as a comment). It is also impossible to
   write attributes of any kind in any context. */
static void fritzing_export_nets(FILE *f, csch_abstract_t *abs)
{
	htsp_entry_t *e;
	long n;

	for(e = htsp_first(&abs->nets); e != NULL; e = htsp_next(&abs->nets, e)) {
		csch_anet_t *net = e->value;
		const char *netname = sch_nle_get_netname(net), *dev;
		int exportable = 0;

		if (net->hdr.omit) continue;

		for(n = 0; (n < net->conns.used) && (!exportable) ; n++) {
			csch_aport_t *port = net->conns.array[n];
			const char *refdes = NULL, *pinnums;

			if (port->hdr.type != CSCH_ATYPE_PORT) {
				rnd_message(RND_MSG_ERROR, "fritzing: invalid connection (object type)\n");
				continue;
			}

			pinnums = sch_nle_get_pinnum(port);
			if (pinnums == NULL) {
				rnd_message(RND_MSG_ERROR, "fritzing: can't determine port's pin number\n");
				continue;
			}

			if (port->parent != NULL) {
				refdes = sch_nle_get_refdes(port->parent);
				if (port->parent->hdr.omit)
					continue; /* omit component */
			}
			if (refdes == NULL) {
				/* This is not an error: no refdes means: do not export (e.g. gnd) */
/*				rnd_message(RND_MSG_ERROR, "fritzing: can't determine port's parent component refdes\n");*/
				continue;
			}

			SCH_NLE_FOREACH_PINNUM(pinnums, my_num,
			{
				(void)my_num;
				exportable = 1;
			});
		}
		
		if (!exportable) continue;

		fprintf(f, " <net><!-- %s -->\n", netname);

		for(n = 0; n < net->conns.used; n++) {
			csch_aport_t *port = net->conns.array[n];
			const char *refdes = NULL, *pinnums, *pinname;
			if (port->hdr.type != CSCH_ATYPE_PORT)
				continue; /* error message generated already */

			pinnums = sch_nle_get_pinnum(port);
			if (pinnums == NULL)
				continue;

			if (port->parent != NULL) {
				refdes = sch_nle_get_refdes(port->parent);
				if (port->parent->hdr.omit)
					continue; /* omit component */
			}
			if (refdes == NULL)
				continue;

			pinname = sch_nle_get_alt_attr(&port->hdr.attr, SCH_NLE_ALTATTR_PINNAME);
			dev = sch_nle_get_alt_attr(&port->parent->hdr.attr, SCH_NLE_ALTATTR_DEVICE);
			if (dev == NULL) dev = sch_nle_get_alt_attr(&port->parent->hdr.attr, SCH_NLE_ALTATTR_FOOTPRINT);
			if (dev == NULL) dev = "n/a";

			/* split up pinnum at space and create one or more conn lines connecting
			   each pin to the given net */
			SCH_NLE_FOREACH_PINNUM(pinnums, my_num,
				{
					fprintf(f, "  <connector name=\"%s\" id=\"connector%s\">\n", pinname, my_num);
					fprintf(f, "   <part label=\"%s\" id=\"%d\" title=\"%s\"/>\n", refdes, compid(port->parent), dev);
					fprintf(f, "  </connector>\n");
				}
			);

		}
		fprintf(f, " </net>\n");
	}
}


/* Export netlist from the abstract model */
static int fritzing_export_project_abst(const char *fn, const char *fmt, csch_abstract_t *abs, rnd_hid_attr_val_t *options)
{
	TODO("get hidlib as an arg")
	rnd_design_t *hidlib = NULL;
	FILE *f = rnd_fopen(hidlib, fn, "w");
	if (f == NULL)
		return -1;

	compids = 50000;
	htpi_init(&compidht, ptrhash, ptrkeyeq);

	fprintf(f, "<?xml version='1.0' encoding='UTF-8'?>\n");
	fprintf(f, "<!-- Created with sch-rnd (http://sch-rnd.repo.hu/) -->\n");
	fprintf(f, "<netlist sketch=\"sch-rnd_schematics\" date=\"Mon Mar 16 01:02:03 2025\">\n"); /* use static date for reproducible files */

	fritzing_export_nets(f, abs);

	fprintf(f, "</netlist>\n");
	fclose(f);

	htpi_uninit(&compidht);
	return 0;
}

#include "hid_impl.c"

int pplg_check_ver_export_fritzing(int ver_needed) { return 0; }

void pplg_uninit_export_fritzing(void)
{
	csch_plug_io_unregister(&efritzing_net);
	rnd_export_remove_opts_by_cookie(fritzing_cookie);
	rnd_hid_remove_hid(&fritzing_hid);
}

int pplg_init_export_fritzing(void)
{
	RND_API_CHK_VER;

	efritzing_net.name = "export to Fritzing";
	efritzing_net.export_prio = fritzing_export_prio;
	efritzing_net.export_project_abst = fritzing_export_project_abst;
	efritzing_net.ext_export_project = ".xml";
	csch_plug_io_register(&efritzing_net);


	rnd_hid_nogui_init(&fritzing_hid);

	fritzing_hid.struct_size = sizeof(rnd_hid_t);
	fritzing_hid.name = "fritzing";
	fritzing_hid.description = "Exports project's Fritzing netlist";
	fritzing_hid.exporter = 1;

	fritzing_hid.get_export_options = fritzing_get_export_options;
	fritzing_hid.do_export = fritzing_do_export;
	fritzing_hid.parse_arguments = fritzing_parse_arguments;
	fritzing_hid.argument_array = fritzing_values;

	fritzing_hid.usage = fritzing_usage;

	rnd_hid_register_hid(&fritzing_hid);
	rnd_hid_load_defaults(&fritzing_hid, fritzing_options, NUM_OPTIONS);


	return 0;
}

