/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - attribute table tEDAx import/export
 *  Copyright (C) 2024 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 <ctype.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/plugins.h>
#include <librnd/core/error.h>
#include <librnd/core/hidlib.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_attrib.h>
#include <librnd/hid/hid_nogui.h>
#include <librnd/hid/hid_init.h>
#include <libcschem/config.h>
#include <libcschem/plug_io.h>
#include <libcschem/util_export.h>
#include <plugins/lib_attbl/lib_attbl.h>
#include <sch-rnd/export.h>

#include <plugins/lib_tedax/write.h>
#include <plugins/lib_tedax/parse.h>


/*** format write ***/

typedef struct tdx_ctx_s {
	attbl_file_ctx_t fctx;
	unsigned main_hdr:1;
} tdx_ctx_t;

static void tdx_print_tbl_head(void *uctx, const char *type, const char **hdr)
{
	tdx_ctx_t *pctx = uctx;
	const char **h;

	attbl_file_open(&pctx->fctx, type);
	if (pctx->fctx.f == NULL)
		return;

	if (!pctx->main_hdr) {
		if (!pctx->fctx.multi_file)
			pctx->main_hdr = 1;
		fprintf(pctx->fctx.f, "tEDAx v1\n");
	}

	fprintf(pctx->fctx.f, "begin table v1 %s\n", type);
	fprintf(pctx->fctx.f, " row header\n");
	for(h = hdr; *h != NULL; h++) {
		fprintf(pctx->fctx.f, "  col ");
		tedax_fprint_escape(pctx->fctx.f, *h);
		fputc('\n', pctx->fctx.f);
	}
}

static void tdx_print_tbl_foot(void *uctx, const char *type, const char **hdr)
{
	tdx_ctx_t *pctx = uctx;
	fprintf(pctx->fctx.f, "end table\n\n");
}

static void tdx_print_row(void *uctx, const char *type, const char *uuid, const char **cols, const char **hdr)
{
	tdx_ctx_t *pctx = uctx;
	const char **h, **c;

	if (pctx->fctx.f == NULL)
		return;

	fprintf(pctx->fctx.f, " row data\n");
	for(h = hdr, c = cols; *h != NULL; h++,c++) {
		fprintf(pctx->fctx.f, "  col ");
		tedax_fprint_escape(pctx->fctx.f, *c);
		fputc('\n', pctx->fctx.f);
	}
}

static int attbl_write_tdx(rnd_design_t *design, const char *fn, int is_prj, attbl_model_t model, csch_abstract_t *abst, int multi)
{
	tdx_ctx_t pctx = {0};

	if (attbl_file_init(&pctx.fctx, design, fn, multi) != 0)
		return 1;

	csch_attbl_export(design, is_prj, model, abst, tdx_print_tbl_head, tdx_print_row, tdx_print_tbl_foot, &pctx);

	attbl_file_uninit(&pctx.fctx);
	return 0;
}

/*** Import ***/

static int attbl_tdx_test_parse(FILE *f, const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	char *line, line_[515];
	int n;

	if (!(type & CSCH_IOTYP_ATTBL))
		return -1;

	/* first non-comment line must be the header */
	for(n = 0; n < 128; n++) {
		line = fgets(line_, sizeof(line_), f);
		if (line == NULL) return -1; /* refuse: header not found */
		if (*line == '#') continue;
		if (strncmp(line, "tEDAx", 5) != 0) return -1; /* refuse: header not found */
		line += 5;
		while(isspace(*line)) line++;
		if (strncmp(line, "v1", 2) != 0) return -1; /* refuse: wrong header version */
		break;
	}

	/* look for a table */
	for(n = 0; n < 128; n++) {
		line = fgets(line_, sizeof(line_), f);
		if (line == NULL) return -1; /* refuse: table */
		if (*line == '#') continue;
		if (strncmp(line, "begin", 5) == 0) {
			line += 5;
			while(isspace(*line)) line++;
			if (strncmp(line, "table ", 6) == 0)
				return 0; /* accept */
		}
	}

	return -1;
}

static int attbl_tdx_import(FILE *f, const char *fn, const char *fmt, csch_sheet_t *sheet)
{
	attbl_import_t ictx = {0};
	char *line, line_[515], buf[515];
	char *argv[8];
	int res = 0, got_hdr = 0, in_tbl = 0, open_row = 0;

	/* first non-comment line must be the header */
	while((line = fgets(line_, sizeof(line_), f)) != NULL) {
		int argc = tedax_getline_mem(&line, buf, sizeof(buf), argv, sizeof(argv)/sizeof(argv[0]));

		if (argc == 0)
			continue;
		if (argc < 0) {
			res = -1;
			break;
		}

		if (!got_hdr) {
			if ((strcmp(argv[0], "tEDAx") != 0) || (strcmp(argv[1], "v1") != 0)) {
				rnd_message(RND_MSG_ERROR, "attbl_tds_import: 'tEDAx v1' header missing\n");
				res = -1;
				break;
			}
			got_hdr = 1;
		}
		else {
			if (!in_tbl) {
				if ((argc < 2) || (strcmp(argv[0], "begin") != 0) || (strcmp(argv[1], "table") != 0)) {
					rnd_message(RND_MSG_ERROR, "attbl_tds_import: only table blocks are accepted\n");
					res = -1;
					break;
				}
				attbl_import_begin(&ictx, &sheet->hidlib);
				in_tbl = 1;
				open_row = 0;
			}
			else {
				if ((argc >= 2) && (strcmp(argv[0], "end") == 0) && (strcmp(argv[1], "table") == 0)) {
					attbl_import_endline(&ictx);
					in_tbl = 0;
					open_row = 0;
				}
				else if (strcmp(argv[0], "row") == 0) {
					if (open_row)
						attbl_import_endline(&ictx);
					open_row = 1;
				}
				else if ((argc >= 2) && (strcmp(argv[0], "col") == 0)) {
					const char *data = argv[1];
					if ((data[0] == '-') && (data[1] == '\0'))
						data = "";
					if (attbl_import_cell(&ictx, data) != 0) {
						res = -1;
						break;
					}
				}
			}
		}
	}

	attbl_import_uninit(&ictx);
	return res;
}

/*** HID and import/exporter glue ***/

static int attbl_tdx_export_prio(const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	if (!(type & CSCH_IOTYP_ATTBL))
		return 0;

	if ((fmt != NULL) && ((rnd_strcasecmp(fmt, "tdx") == 0) || (rnd_strcasecmp(fmt, "tedax") == 0)))
		return 100;

	return 0;
}

static int attbl_tdx_import_prio(const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	if (!(type & CSCH_IOTYP_ATTBL))
		return 0;

	if ((fmt == NULL) || (*fmt == '\0'))
		return 50; /* autodetect */

	if ((rnd_strcasecmp(fmt, "tdx") == 0) || (rnd_strcasecmp(fmt, "tedax") == 0))
		return 100;

	return 0; /* requested a different format explicitly */
}


static const char attbl_tdx_cookie[] = "attbl_tdx export hid";

static rnd_export_opt_t attbl_tdx_options[] = {
	{"outfile", "Name of the tEDAx output file",
	 RND_HATT_STRING, 0, 0, {0, 0, 0}, 0},
#define HA_outfile 0

	{"model", "Name of the model to export",
	 RND_HATT_ENUM, 0, 0, {0, 0, 0}, attbl_models},
#define HA_model 1

	{"multi", "Write multiple files: save one file per table, inserting table name in the file name",
	 RND_HATT_BOOL, 0, 0, {0, 0, 0}, 0},
#define HA_multi 2

	{"view", "Name of the view to export (use first view when not specified); only in project's abstract model export",
	 RND_HATT_STRING, 0, 0, {0, 0, 0}, 0},
#define HA_view 3

	{"", "EXTEDIT",
	 RND_HATT_BEGIN_VBOX, 0, 0, {0, "tdx", 0, 0, {0}, attbl_extedit_dad}, 0}
#define HA_extedit 4
};

#define NUM_OPTIONS (sizeof(attbl_tdx_options)/sizeof(attbl_tdx_options[0]))

static rnd_hid_attr_val_t attbl_tdx_values[NUM_OPTIONS];

static sch_rnd_export_appspec_t appspec0;

static const rnd_export_opt_t *attbl_tdx_get_export_options(rnd_hid_t *hid, int *n, rnd_design_t *dsg, void *appspec_)
{
	const char *val = attbl_tdx_values[HA_outfile].str;

	if ((dsg != NULL) && ((val == NULL) || (*val == '\0')))
		csch_derive_default_filename(dsg, 1, &attbl_tdx_values[HA_outfile], ".tdx");

	if (n)
		*n = NUM_OPTIONS;
	return attbl_tdx_options;
}

static void attbl_tdx_do_export(rnd_hid_t *hid, rnd_design_t *design, rnd_hid_attr_val_t *options, void *appspec_)
{
	sch_rnd_export_appspec_t *appspec = appspec_ == NULL ? &appspec0 : appspec_;
	csch_abstract_t abst;
	int is_abst, is_prj;

	if (!options) {
		attbl_tdx_get_export_options(hid, 0, design, appspec_);
		options = attbl_tdx_values;
	}

	is_abst = ATTBL_MODEL_IS_ABST(options[HA_model].lng);
	if (attbl_do_export_begin(design, options[HA_view].str, options[HA_model].lng, &abst) != 0)
		return;

	is_prj = appspec->exp_prj || is_abst; /* abstract always needs to be exported as project */
	attbl_write_tdx(design, options[HA_outfile].str, is_prj, options[HA_model].lng, (is_abst ? &abst : NULL), !!options[HA_multi].lng);

	attbl_do_export_end(&abst, options[HA_model].lng);
}

static int attbl_tdx_usage(rnd_hid_t *hid, const char *topic)
{
	fprintf(stderr, "\nAttribute table tEDAx exporter command line arguments:\n\n");
	rnd_hid_usage(attbl_tdx_options, sizeof(attbl_tdx_options) / sizeof(attbl_tdx_options[0]));
	fprintf(stderr, "\nUsage: sch-rnd [generic_options] -x attbl_tdx [options] foo.rs\n\n");
	return 0;
}


static int attbl_tdx_parse_arguments(rnd_hid_t *hid, int *argc, char ***argv)
{
	rnd_export_register_opts2(hid, attbl_tdx_options, sizeof(attbl_tdx_options) / sizeof(attbl_tdx_options[0]), attbl_tdx_cookie, 0);
	return rnd_hid_parse_command_line(argc, argv);
}

rnd_hid_t attbl_tdx_hid = {0};


static int attbl_tdx_export_project_tedax(const char *fn, const char *fmt, csch_abstract_t *abst, rnd_hid_attr_val_t *options)
{
	return attbl_write_tdx(rnd_multi_get_current(), fn, 1, ATTBLM_ABSTRACT, abst, !!options[HA_multi].lng);
}

/***/

static csch_plug_io_t etdx;

int pplg_check_ver_attbl_tedax(int ver_needed) { return 0; }

void pplg_uninit_attbl_tedax(void)
{
	csch_plug_io_unregister(&etdx);
	rnd_export_remove_opts_by_cookie(attbl_tdx_cookie);
	rnd_hid_remove_hid(&attbl_tdx_hid);
}

int pplg_init_attbl_tedax(void)
{
	RND_API_CHK_VER;

	etdx.name = "export/import attribute table tEDAx";
	etdx.export_prio = attbl_tdx_export_prio;
	etdx.import_prio = attbl_tdx_import_prio;
	etdx.export_project_abst = attbl_tdx_export_project_tedax;
	etdx.test_parse = attbl_tdx_test_parse;
	etdx.import_attbl = attbl_tdx_import;
	etdx.ext_export_project = ".tdx";
	csch_plug_io_register(&etdx);

	rnd_hid_nogui_init(&attbl_tdx_hid);

	attbl_tdx_hid.struct_size = sizeof(rnd_hid_t);
	attbl_tdx_hid.name = "attbl_tedax";
	attbl_tdx_hid.description = "Export attribute table in tEDAx";
	attbl_tdx_hid.exporter = 1;

	attbl_tdx_hid.get_export_options = attbl_tdx_get_export_options;
	attbl_tdx_hid.do_export = attbl_tdx_do_export;
	attbl_tdx_hid.parse_arguments = attbl_tdx_parse_arguments;
	attbl_tdx_hid.argument_array = attbl_tdx_values;

	attbl_tdx_hid.usage = attbl_tdx_usage;

	rnd_hid_register_hid(&attbl_tdx_hid);
	rnd_hid_load_defaults(&attbl_tdx_hid, attbl_tdx_options, NUM_OPTIONS);

	return 0;
}

