/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - attribute table lihata 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 <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 <plugins/io_lihata/write.h>
#include <plugins/io_lihata/read.h>
#include <sch-rnd/export.h>

/*** format write ***/

typedef struct lht_ctx_s {
	attbl_file_ctx_t fctx;
	unsigned main_hdr:1;
} lht_ctx_t;

static void lht_print_tbl_head(void *uctx, const char *type, const char **hdr)
{
	lht_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, "li:sch-rnd-attbl-v1 {\n");
	}


	fprintf(pctx->fctx.f, " li:table {\n");
	fprintf(pctx->fctx.f, "  li:row_header {\n");
	for(h = hdr; *h != NULL; h++) {
		fprintf(pctx->fctx.f, "   col=");
		lht_print_str(pctx->fctx.f, *h);
		fputc('\n', pctx->fctx.f);
	}
	fprintf(pctx->fctx.f, "  }\n");
}

static void lht_print_tbl_foot(void *uctx, const char *type, const char **hdr)
{
	lht_ctx_t *pctx = uctx;
	fprintf(pctx->fctx.f, " }\n");
	if (pctx->fctx.multi_file)
		fprintf(pctx->fctx.f, "}\n");
}

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

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

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

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

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

	csch_attbl_export(design, is_prj, model, abst, lht_print_tbl_head, lht_print_row, lht_print_tbl_foot, &pctx);

	if (!pctx.fctx.multi_file)
		fprintf(pctx.fctx.f, "}\n");

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

/*** Import ***/

static int attbl_lht_test_parse(FILE *f, const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	if (!(type & CSCH_IOTYP_ATTBL))
		return -1;

	return io_lihata_test_parse(f, fn, fmt, CSCH_IOTYP_ATTBL);
}

static int attbl_lht_import(FILE *f, const char *fn, const char *fmt, csch_sheet_t *sheet)
{
	attbl_import_t ictx = {0};
	lht_doc_t *doc;
	lht_node_t *t, *r, *c;
	char *errmsg = NULL;
	int res = -1;

	doc = lht_dom_load(fn, &errmsg);
	if (doc == NULL) {
		rnd_message(RND_MSG_ERROR, "attbl_lht_import: failed to parse lihata document '%s': %s\n", fn, errmsg);
		free(errmsg);
		return -1;
	}

	if (doc->root->type != LHT_LIST) {
		rnd_message(RND_MSG_ERROR, "attbl_lht_import: broken tree in '%s': root node must be a list\n", fn);
		goto error;
	}

	for(t = doc->root->data.list.first; t != NULL; t = t->next) {
		if ((t->type == LHT_LIST) && (strcmp(t->name, "table") == 0)) {
			attbl_import_begin(&ictx, &sheet->hidlib);

			for(r = t->data.list.first; r != NULL; r = r->next) {
				if ((r->type == LHT_LIST) && (strncmp(r->name, "row_", 4) == 0)) {
					for(c = r->data.list.first; c != NULL; c = c->next) {
						const char *data = "";
						if (c->type == LHT_TEXT)
							data = c->data.text.value;
						if (attbl_import_cell(&ictx, data) != 0)
							goto error;
					}
					attbl_import_endline(&ictx);
				}
			}
		}
	}

	attbl_import_uninit(&ictx);

	res = 0;
	error:;
	lht_dom_uninit(doc);
	free(errmsg);
	return res;
}

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

static int attbl_lht_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, "lht") == 0) || (rnd_strcasecmp(fmt, "lihata") == 0)))
		return 100;

	return 0;
}

static int attbl_lht_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, "lht") == 0) || (rnd_strcasecmp(fmt, "lihata") == 0))
		return 100;

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


static const char attbl_lht_cookie[] = "attbl_lht export hid";

static rnd_export_opt_t attbl_lht_options[] = {
	{"outfile", "Name of the lihata 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, "lht", 0, 0, {0}, attbl_extedit_dad}, 0}
#define HA_extedit 4
};

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

static rnd_hid_attr_val_t attbl_lht_values[NUM_OPTIONS];

static sch_rnd_export_appspec_t appspec0;

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

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

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

static void attbl_lht_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_lht_get_export_options(hid, 0, design, appspec_);
		options = attbl_lht_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_lht(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_lht_usage(rnd_hid_t *hid, const char *topic)
{
	fprintf(stderr, "\nAttribute table lihata exporter command line arguments:\n\n");
	rnd_hid_usage(attbl_lht_options, sizeof(attbl_lht_options) / sizeof(attbl_lht_options[0]));
	fprintf(stderr, "\nUsage: sch-rnd [generic_options] -x attbl_lht [options] foo.rs\n\n");
	return 0;
}


static int attbl_lht_parse_arguments(rnd_hid_t *hid, int *argc, char ***argv)
{
	rnd_export_register_opts2(hid, attbl_lht_options, sizeof(attbl_lht_options) / sizeof(attbl_lht_options[0]), attbl_lht_cookie, 0);
	return rnd_hid_parse_command_line(argc, argv);
}

rnd_hid_t attbl_lht_hid = {0};


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

/***/

static csch_plug_io_t elht;

int pplg_check_ver_attbl_lht(int ver_needed) { return 0; }

void pplg_uninit_attbl_lht(void)
{
	csch_plug_io_unregister(&elht);
	rnd_export_remove_opts_by_cookie(attbl_lht_cookie);
	rnd_hid_remove_hid(&attbl_lht_hid);
}

int pplg_init_attbl_lht(void)
{
	RND_API_CHK_VER;

	elht.name = "export/import attribute table lihata";
	elht.export_prio = attbl_lht_export_prio;
	elht.import_prio = attbl_lht_import_prio;
	elht.export_project_abst = attbl_lht_export_project_lht;
	elht.test_parse = attbl_lht_test_parse;
	elht.import_attbl = attbl_lht_import;
	elht.ext_export_project = ".lht";
	csch_plug_io_register(&elht);

	rnd_hid_nogui_init(&attbl_lht_hid);

	attbl_lht_hid.struct_size = sizeof(rnd_hid_t);
	attbl_lht_hid.name = "attbl_lht";
	attbl_lht_hid.description = "Export attribute table in lihata";
	attbl_lht_hid.exporter = 1;

	attbl_lht_hid.get_export_options = attbl_lht_get_export_options;
	attbl_lht_hid.do_export = attbl_lht_do_export;
	attbl_lht_hid.parse_arguments = attbl_lht_parse_arguments;
	attbl_lht_hid.argument_array = attbl_lht_values;

	attbl_lht_hid.usage = attbl_lht_usage;

	rnd_hid_register_hid(&attbl_lht_hid);
	rnd_hid_load_defaults(&attbl_lht_hid, attbl_lht_options, NUM_OPTIONS);

	return 0;
}

