/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - tedax_footprint 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., 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 <libcschem/config.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <math.h>
#include <genvector/gds_char.h>

#include <librnd/core/math_helper.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/core/error.h>
#include <librnd/core/misc_util.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/plugins.h>
#include <librnd/core/safe_fs.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_nogui.h>
#include <librnd/hid/hid_init.h>
#include <librnd/hid/hid_attrib.h>

#include <libcschem/concrete.h>
#include <libcschem/util_export.h>

#include <sch-rnd/draw.h>
#include <sch-rnd/export.h>


static rnd_hid_t tedax_footprint_hid;

const char *tedax_footprint_cookie = "export_tedax_footprint";

typedef struct tedax_footprint_s {
	FILE *outf;
} tedax_footprint_t;

static tedax_footprint_t tctx_, *tctx = &tctx_;

static const rnd_export_opt_t tedax_footprint_attribute_list[] = {
	{"outfile", "Graphics output file",
	 RND_HATT_STRING, 0, 0, {0, 0, 0}, 0},
#define HA_tedax_footprintfile 0

	{"layers", "List of layers to export or \"GUI\" for exporting what's visible on the GUI at the moment or empty for default export layer visibility",
	 RND_HATT_STRING, 0, 0, {0, 0, 0}, 0},
#define HA_layers 1

	{"view", "If not empty, switch to view and compile before exporting",
	 RND_HATT_STRING, 0, 0, {0, 0, 0}, 0},
#define HA_view 2
};

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

#define OLAYER "secondary silk"
#define TR(v)  (((double)P2C(v)/1000.0))
#define TRX(v) (((double)P2C(v)/1000.0))
#define TRY(v) (((double)P2C(v)/1000.0))

static rnd_hid_attr_val_t tedax_footprint_values[NUM_OPTIONS];

static char *out_filename;

static const rnd_export_opt_t *tedax_footprint_get_export_options(rnd_hid_t *hid, int *n, rnd_design_t *dsg, void *appspec)
{
	const char *val = tedax_footprint_values[HA_tedax_footprintfile].str;

	if ((dsg != NULL) && ((val == NULL) || (*val == '\0')))
		csch_derive_default_filename(dsg, sch_rnd_export_appspec_prj(appspec), &tedax_footprint_values[HA_tedax_footprintfile], ".tdx");

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

void tedax_footprint_hid_export_to_file(rnd_design_t *hl, FILE * the_file, rnd_hid_attr_val_t * options, rnd_xform_t *xform)
{
	rnd_hid_expose_ctx_t ctx;

	ctx.design = hl;
	ctx.view.X1 = hl->dwg.X1;
	ctx.view.Y1 = hl->dwg.Y1;
	ctx.view.X2 = hl->dwg.X2;
	ctx.view.Y2 = hl->dwg.Y2;

	tctx->outf = the_file;

	rnd_app.expose_main(&tedax_footprint_hid, &ctx, xform);
}

static int tedax_footprint_do_export_sheet(rnd_hid_t *hid, rnd_design_t *dsg, rnd_hid_attr_val_t *options, sch_rnd_export_appspec_t *appspec, int *ovr)
{
	rnd_xform_t xform = {0};
	FILE *f = NULL;

	out_filename = cschem_export_filename(dsg, options[HA_tedax_footprintfile].str, NULL, appspec->fn_page_suffix, ".tedax_footprint");

	f = rnd_fopen_askovr(dsg, out_filename, "w", ovr);
	if (f == NULL) {
		int ern = errno;
		rnd_message(RND_MSG_ERROR, "tedax_footprint_do_export(): failed to open %s: %s\n", out_filename, strerror(ern));
		free(out_filename);
		return -1;
	}
	free(out_filename);

	tctx->outf = f;
	fprintf(f, "tEDAx v1\nbegin footprint v1 schematic\n");

	sch_rnd_set_export_layers(&xform, options[HA_layers].str);
	tedax_footprint_hid_export_to_file(dsg, tctx->outf, options, &xform);

	fprintf(tctx->outf, "end footprint\n");
	fclose(tctx->outf);

	tctx->outf = NULL;
	return 0;
}


static void tedax_footprint_do_export(rnd_hid_t *hid, rnd_design_t *design, rnd_hid_attr_val_t *options, void *appspec_)
{
	rnd_design_t *hl = design;
	csch_sheet_t *sheet = (csch_sheet_t *)hl;
	void *view_cookie;
	sch_rnd_export_appspec_t *appspec = (appspec_ == NULL) ? &sch_rnd_no_appspec : appspec_;

	if (!options) {
		tedax_footprint_get_export_options(hid, 0, design, appspec);
		options = tedax_footprint_values;
	}

	if (cschem_export_compile_pre((csch_project_t *)sheet->hidlib.project, options[HA_view].str, &view_cookie) != 0)
		return;

	sch_rnd_export_project_or_sheet(hid, design, options, appspec, tedax_footprint_do_export_sheet);


	cschem_export_compile_post((csch_project_t *)sheet->hidlib.project, &view_cookie);
}

static int tedax_footprint_parse_arguments(rnd_hid_t *hid, int *argc, char ***argv)
{
	rnd_export_register_opts2(hid, tedax_footprint_attribute_list, sizeof(tedax_footprint_attribute_list) / sizeof(tedax_footprint_attribute_list[0]), tedax_footprint_cookie, 0);
	return rnd_hid_parse_command_line(argc, argv);
}

static int tedax_footprint_set_layer_group(rnd_hid_t *hid, rnd_design_t *design, rnd_layergrp_id_t group, const char *purpose, int purpi, rnd_layer_id_t layer, unsigned int flags, int is_empty, rnd_xform_t **xform)
{
	if (is_empty)
		return 0;

	return 1;
}

static void tedax_footprint_set_drawing_mode(rnd_hid_t *hid, rnd_composite_op_t op, rnd_bool direct, const rnd_box_t *screen)
{
}

struct rnd_hid_gc_s {
	int width;
};

rnd_hid_gc_t rnd_tedax_footprint_make_gc(rnd_hid_t *hid)
{
	rnd_hid_gc_t rv = (rnd_hid_gc_t)calloc(sizeof(struct rnd_hid_gc_s), 1);
	rv->width = 1;
	return rv;
}

void rnd_tedax_footprint_destroy_gc(rnd_hid_gc_t gc)
{
	free(gc);
}

static void tedax_footprint_set_color(rnd_hid_gc_t gc, const rnd_color_t *color)
{
}

void rnd_tedax_footprint_set_draw_xor(rnd_hid_gc_t gc, int xor_)
{
}

void rnd_tedax_footprint_set_crosshair(rnd_hid_t *hid, rnd_coord_t x, rnd_coord_t y, rnd_set_crosshair_t a)
{
}

void rnd_tedax_footprint_set_line_cap(rnd_hid_gc_t gc, rnd_cap_style_t style)
{
}

void rnd_tedax_footprint_set_line_width(rnd_hid_gc_t gc, rnd_coord_t width)
{
	gc->width = width;
}


static void tedax_footprint_draw_rect(rnd_hid_gc_t gc, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	fprintf(tctx->outf, "line " OLAYER " - %f %f %f %f %f 0\n", TRX(x1), TRY(y1), TRX(x2), TRY(y1), TR(gc->width));
	fprintf(tctx->outf, "line " OLAYER " - %f %f %f %f %f 0\n", TRX(x2), TRY(y1), TRX(x2), TRY(y2), TR(gc->width));
	fprintf(tctx->outf, "line " OLAYER " - %f %f %f %f %f 0\n", TRX(x2), TRY(y2), TRX(x1), TRY(y2), TR(gc->width));
	fprintf(tctx->outf, "line " OLAYER " - %f %f %f %f %f 0\n", TRX(x2), TRY(y1), TRX(x1), TRY(y1), TR(gc->width));
}

static void tedax_footprint_fill_rect(rnd_hid_gc_t gc, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	fprintf(tctx->outf, "polygon " OLAYER " - 0 4 %f %f %f %f %f %f %f %f\n",
		TRX(x1), TRY(y1), TRX(x2), TRY(y1), TRX(x2), TRY(y2), TRX(x1), TRY(y2));
}

static void tedax_footprint_draw_line(rnd_hid_gc_t gc, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	fprintf(tctx->outf, "line " OLAYER " - %f %f %f %f %f 0\n",
		TRX(x1), TRY(y1), TRX(x2), TRY(y2), TR(gc->width));
}

static void tedax_footprint_draw_arc(rnd_hid_gc_t gc, rnd_coord_t cx, rnd_coord_t cy, rnd_coord_t width, rnd_coord_t height, rnd_angle_t start_angle, rnd_angle_t delta_angle)
{
	rnd_coord_t radius = (width+height)/2.0;
	double oradius = TR(radius);
	if (oradius < 0.1)
		return;

	fprintf(tctx->outf, "arc " OLAYER " - %f %f %f %f %f %f 0\n",
		TRX(cx), TRY(cy), oradius, start_angle, delta_angle, TR(gc->width));
}

static void tedax_footprint_fill_circle(rnd_hid_gc_t gc, rnd_coord_t cx, rnd_coord_t cy, rnd_coord_t radius)
{
	fprintf(tctx->outf, "arc " OLAYER " - %f %f %f 0 360 0.1 0\n",
		TRX(cx), TRY(cy), TR(radius));
}

static void tedax_footprint_fill_polygon_offs(rnd_hid_gc_t gc, int n_coords, rnd_coord_t *x, rnd_coord_t *y, rnd_coord_t dx, rnd_coord_t dy)
{
	int n;

	/* respect line length limit: with a pair of %.2f we can export about 60 coords */
	if (n_coords > 60) {
		rnd_message(RND_MSG_ERROR, "tedax_footprint_fill_polygon_offs(): can't export polygon: too many corners\n");
		return;
	}

	fprintf(tctx->outf, "polygon " OLAYER " - 0 %d", n_coords);
	for(n = 0; n < n_coords; n++)
		fprintf(tctx->outf, " %.2f %.2f", TRX(x[n]), TRY(y[n]));
	fprintf(tctx->outf, "\n");
}

static void tedax_footprint_fill_polygon(rnd_hid_gc_t gc, int n_coords, rnd_coord_t *x, rnd_coord_t *y)
{
	tedax_footprint_fill_polygon_offs(gc, n_coords, x, y, 0, 0);
}

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

int pplg_check_ver_export_tedax_footprint(int ver_needed) { return 0; }

void pplg_uninit_export_tedax_footprint(void)
{
	rnd_export_remove_opts_by_cookie(tedax_footprint_cookie);
	rnd_hid_remove_hid(&tedax_footprint_hid);
}

int pplg_init_export_tedax_footprint(void)
{
	RND_API_CHK_VER;

	memset(&tedax_footprint_hid, 0, sizeof(rnd_hid_t));

	rnd_hid_nogui_init(&tedax_footprint_hid);

	tedax_footprint_hid.struct_size = sizeof(rnd_hid_t);
	tedax_footprint_hid.name = "tedax_footprint";
	tedax_footprint_hid.description = "Export drawing as a tEDAx footprint on silk";
	tedax_footprint_hid.exporter = 1;

	tedax_footprint_hid.get_export_options = tedax_footprint_get_export_options;
	tedax_footprint_hid.do_export = tedax_footprint_do_export;
	tedax_footprint_hid.parse_arguments = tedax_footprint_parse_arguments;
	tedax_footprint_hid.set_layer_group = tedax_footprint_set_layer_group;
	tedax_footprint_hid.make_gc = rnd_tedax_footprint_make_gc;
	tedax_footprint_hid.destroy_gc = rnd_tedax_footprint_destroy_gc;
	tedax_footprint_hid.set_drawing_mode = tedax_footprint_set_drawing_mode;
	tedax_footprint_hid.set_color = tedax_footprint_set_color;
	tedax_footprint_hid.set_line_cap = rnd_tedax_footprint_set_line_cap;
	tedax_footprint_hid.set_line_width = rnd_tedax_footprint_set_line_width;
	tedax_footprint_hid.set_draw_xor = rnd_tedax_footprint_set_draw_xor;
	tedax_footprint_hid.draw_line = tedax_footprint_draw_line;
	tedax_footprint_hid.draw_arc = tedax_footprint_draw_arc;
	tedax_footprint_hid.draw_rect = tedax_footprint_draw_rect;
	tedax_footprint_hid.fill_circle = tedax_footprint_fill_circle;
	tedax_footprint_hid.fill_polygon = tedax_footprint_fill_polygon;
	tedax_footprint_hid.fill_polygon_offs = tedax_footprint_fill_polygon_offs;
	tedax_footprint_hid.fill_rect = tedax_footprint_fill_rect;
	tedax_footprint_hid.set_crosshair = rnd_tedax_footprint_set_crosshair;
	tedax_footprint_hid.argument_array = tedax_footprint_values;

	tedax_footprint_hid.usage = tedax_footprint_usage;

	rnd_hid_register_hid(&tedax_footprint_hid);
	rnd_hid_load_defaults(&tedax_footprint_hid, tedax_footprint_attribute_list, NUM_OPTIONS);

	return 0;
}
