/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - tEDAx netlist export
 *  Copyright (C) 2020, 2023 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 <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 etdx_net;

static int tdx_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, "tedax") == 0) || (rnd_strcasecmp(fmt, "tdx") == 0))
		return 100;
	if (rnd_strcasecmp(fmt, "net") == 0)
		return 90;
	return 0;
}

static void tdx_fprint_escape(FILE *f, const char *val)
{
	if ((val == NULL) || (*val == '\0')) {
		fputc('-', f);
		return;
	}
	for(; *val != '\0'; val++) {
		switch(*val) {
			case '\\': fputc('\\', f); fputc('\\', f); break;
			case '\n': fputc('\\', f); fputc('n', f); break;
			case '\r': fputc('\\', f); fputc('r', f); break;
			case '\t': fputc('\\', f); fputc('t', f); break;
			case ' ': fputc('\\', f); fputc(' ', f); break;
			default:
				fputc(*val, f);
		}
	}
}

static void export_attr(FILE *f, const char *cmd, const char *key, const char *val, const char *val2)
{
	if ((key == NULL) || (val == NULL))
		return;
	fprintf(f, "%s ", cmd);
	tdx_fprint_escape(f, key);
	fputc(' ', f);
	tdx_fprint_escape(f, val);
	if (val2 != NULL) {
		fputc(' ', f);
		tdx_fprint_escape(f, val2);
	}
	fputc('\n', f);
}

/* Export comptag and nettag for attributes that have export_name */
static void export_custom_attrs(FILE *f, const char *name, csch_ahdr_t *obj, const char *cmd)
{
	htsp_entry_t *e;
	for(e = htsp_first(&obj->attr); e != NULL; e = htsp_next(&obj->attr, e)) {
		csch_attrib_t *a = e->value;
		if ((a->export_name != NULL) && (a->val != NULL))
			export_attr(f, cmd, name, a->export_name, a->val);
	}
}

static void tdx_export_comps(FILE *f, csch_abstract_t *abs)
{
	htsp_entry_t *e, *p;
	for(e = htsp_first(&abs->comps); e != NULL; e = htsp_next(&abs->comps, e)) {
		csch_acomp_t *comp = e->value;
		const char *refdes = sch_nle_get_refdes(comp);

		if (refdes == NULL)
			continue;

		if (comp->hdr.omit)
			continue;

		export_attr(f, "footprint", refdes, sch_nle_get_alt_attr(&comp->hdr.attr, "display/footprint", "footprint", NULL), NULL);
		export_attr(f, "value", refdes, sch_nle_get_alt_attr(&comp->hdr.attr, "display/value", "value", NULL), NULL);
		export_attr(f, "device", refdes, sch_nle_get_alt_attr(&comp->hdr.attr, "display/device", "device", NULL), NULL);
		export_custom_attrs(f, refdes, &comp->hdr, "comptag");
		for(p = htsp_first(&comp->ports); p != NULL; p = htsp_next(&comp->ports, p)) {
			const csch_aport_t *port = p->value;
			const char *pinnum = sch_nle_get_pinnum(port);
			const char *pinname = sch_nle_get_alt_attr(&port->hdr.attr, "display/pinname", "name", NULL);

			if (pinname == NULL)
				continue;

			if (strpbrk(pinnum, " \t") != NULL) { /* space separated list of pinnums */
				char *s, *next, *tmp = rnd_strdup(pinnum);
				for(s = tmp; s != NULL; s = next) {
					next = strpbrk(s, " \t");
					if (next != NULL) {
						*next = '\0';
						next++;
					}
					while(isspace(*s)) s++;
					if (*s == '\0') break;
					export_attr(f, "pinname", refdes, s, pinname);
				}
				free(tmp);
			}
			else
				export_attr(f, "pinname", refdes, pinnum, pinname);
		}
	}
}

static void tdx_export_nets(FILE *f, csch_abstract_t *abs)
{
	htsp_entry_t *e;
	long n;
	char *s, *next;
	gds_t tmp = {0};

	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);

		if (net->hdr.omit) continue;

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

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

			pinnum = sch_nle_get_pinnum(port);
			if (pinnum == NULL) {
				rnd_message(RND_MSG_ERROR, "tdx: 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, "tdx: can't determine port's parent component refdes\n");*/
				continue;
			}

			/* split up pinnum at space and create one or more conn lines connecting
			   each pin to the given net */
			tmp.used = 0;
			gds_append_str(&tmp, pinnum);
			for(s = tmp.array; s != NULL; s = next) {
				/* strip leading whitespace and seps */
				while(isspace(*s)) s++;
				if (*s == '\0')
					break;

				/* split string at next sep */
				next = strpbrk(s, " \t\r\n");
				if (next != NULL) {
					*next = '\0';
					next++;
				}
				fprintf(f, "conn ");
				tdx_fprint_escape(f, netname);
				fputc(' ', f);
				tdx_fprint_escape(f, refdes);
				fputc(' ', f);
				tdx_fprint_escape(f, s);
				fputc('\n', f);
			}
		}
		export_custom_attrs(f, netname, &net->hdr, "nettag");
	}
	gds_uninit(&tmp);
}


static int tdx_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;

	fprintf(f, "tEDAx v1\n");
	fprintf(f, "begin netlist v1 ");
	tdx_fprint_escape(f, "<TODO: name>");
	fprintf(f, "\n");

	tdx_export_comps(f, abs);
	tdx_export_nets(f, abs);

	fprintf(f, "end netlist\n\n");
	fclose(f);
	return 0;
}

#include "hid_impl.c"

int pplg_check_ver_export_tedax(int ver_needed) { return 0; }

void pplg_uninit_export_tedax(void)
{
	csch_plug_io_unregister(&etdx_net);
	rnd_export_remove_opts_by_cookie(tedax_cookie);
	rnd_hid_remove_hid(&tedax_hid);
}

int pplg_init_export_tedax(void)
{
	RND_API_CHK_VER;

	etdx_net.name = "export to tEDAx";
	etdx_net.export_prio = tdx_export_prio;
	etdx_net.export_project_abst = tdx_export_project_abst;
	etdx_net.ext_export_project = ".tdx";
	csch_plug_io_register(&etdx_net);


	rnd_hid_nogui_init(&tedax_hid);

	tedax_hid.struct_size = sizeof(rnd_hid_t);
	tedax_hid.name = "tedax";
	tedax_hid.description = "Exports project's tEDAx netlist";
	tedax_hid.exporter = 1;

	tedax_hid.get_export_options = tedax_get_export_options;
	tedax_hid.do_export = tedax_do_export;
	tedax_hid.parse_arguments = tedax_parse_arguments;
	tedax_hid.argument_array = tedax_values;

	tedax_hid.usage = tedax_usage;

	rnd_hid_register_hid(&tedax_hid);
	rnd_hid_load_defaults(&tedax_hid, tedax_options, NUM_OPTIONS);


	return 0;
}

