/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - BXL file format support
 *  Copyright (C) 2025 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust Fund in 2025)
 *
 *  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
 *
 *  Originally copied from pcb-rnd's io_bxl/read.c by the same author.
 */

#include "config.h"

#include <stdio.h>
#include <assert.h>

#include <genht/hash.h>
#include <librnd/core/error.h>
#include <librnd/core/safe_fs.h>
#include <librnd/core/compat_misc.h>
#include <libcschem/operation.h>
#include <libcschem/cnc_text.h>

#include "read.h"
#include "bxl.h"
#include "bxl_lex.h"
#include "bxl_gram.h"

#define SKIP             if (!ctx->in_target_sym) return
#define SKIP_FREE(ptr)   if (!ctx->in_target_sym) { free(ptr); return; }

void sch_bxl_symbol_begin(sch_bxl_ctx_t *ctx, const char *name)
{
	if (ctx->subsymname != NULL)
		ctx->in_target_sym = (strcmp(ctx->subsymname, name) == 0);
}

void sch_bxl_symbol_end(sch_bxl_ctx_t *ctx)
{
	ctx->in_target_sym = 0;
}

void sch_bxl_reset(sch_bxl_ctx_t *ctx)
{
	free(ctx->state.attr_key);
	free(ctx->state.attr_val);
	memset(&ctx->state, 0, sizeof(ctx->state));
}

void sch_bxl_reset_symbol(sch_bxl_ctx_t *ctx)
{
	SKIP;
	memset(&ctx->sym_state, 0, sizeof(ctx->sym_state));
}

void sch_bxl_set_justify(sch_bxl_ctx_t *ctx, const char *str)
{
	/* special case for single center */
	if (rnd_strcasecmp(str, "center") == 0) { ctx->state.hjust = ctx->state.vjust = PCB_BXL_JUST_CENTER; return; }

	if (rnd_strncasecmp(str, "lower", 5) == 0)       { ctx->state.vjust = PCB_BXL_JUST_BOTTOM; str+=5; }
	else if (rnd_strncasecmp(str, "upper", 5) == 0)  { ctx->state.vjust = PCB_BXL_JUST_TOP; str+=5; }
	else if (rnd_strncasecmp(str, "center", 6) == 0) { ctx->state.vjust = PCB_BXL_JUST_CENTER; str+=6; }

	if (rnd_strncasecmp(str, "left", 4) == 0)        ctx->state.hjust = PCB_BXL_JUST_LEFT;
	else if (rnd_strncasecmp(str, "right", 5) == 0)  ctx->state.hjust = PCB_BXL_JUST_RIGHT;
	else if (rnd_strncasecmp(str, "center", 6) == 0) ctx->state.hjust = PCB_BXL_JUST_CENTER;
}

void sch_bxl_set_attr_val(sch_bxl_ctx_t *ctx, char *key, char *val)
{
	free(ctx->state.attr_key);
	free(ctx->state.attr_val);

	ctx->state.attr_key = key;
	ctx->state.attr_val = val;
}

void sch_bxl_add_line(sch_bxl_ctx_t *ctx)
{
	rnd_coord_t width;
	SKIP;
	width = ctx->state.width;
	if (width == 0)
		width = 1;

	csch_alien_mkline(&ctx->alien, ctx->grp, ctx->state.origin_x, ctx->state.origin_y, ctx->state.endp_x, ctx->state.endp_y, "sym-decor");
}

void sch_bxl_add_arc(sch_bxl_ctx_t *ctx)
{
	rnd_coord_t width;
	SKIP;
	width = ctx->state.width;
	if (width == 0)
		width = 1;
	rnd_message(RND_MSG_ERROR, "Arc in symbol not yet supported; please mail your bxl file to the developer!\n");
	TODO("pcb_arc_new(ctx->state.layer, "
		"ctx->state.origin_x, ctx->state.origin_y,"
		"ctx->state.radius, ctx->state.radius,"
		"ctx->state.arc_start, ctx->state.arc_delta,"
		"width, 0, pcb_flag_make(PCB_FLAG_CLEARLINE), 0);");
}

static csch_text_t *create_dyntext(sch_bxl_ctx_t *ctx, csch_cgrp_t *parent, const char *pen_name, char *str, double x, double y, double rot)
{
	csch_text_t *text;

	text = (csch_text_t *)csch_alien_mktext(&ctx->alien, parent, x, y, pen_name);
	text->text = str;
	text->dyntext = 1;
	if (ctx->pin.rot == 180) {
		text->spec_mirx = 1;
		text->spec_rot = -180;
	}
	return text;
}

void sch_bxl_add_text(sch_bxl_ctx_t *ctx)
{
	int tlen;
	rnd_coord_t thickness;
	double scxy;

	SKIP;

	if (!ctx->state.is_text && (ctx->state.attr_key != NULL)) {
		csch_source_arg_t *src;
		int is_refdes = (rnd_strcasecmp(ctx->state.attr_key, "refdes") == 0);

		if (is_refdes) {
			strcpy(ctx->state.attr_key, "name");

			/* make sure the text object is created properly */
			free(ctx->state.text_str);
			ctx->state.text_str = rnd_strdup("%../A.name%");
			ctx->state.is_visible = 1;
		}

		src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
		csch_attrib_set(&ctx->grp->attr, CSCH_ATP_USER_DEFAULT, ctx->state.attr_key, ctx->state.attr_val, src, NULL);
	}

	if (ctx->state.is_visible && (ctx->state.text_str != NULL)) {
		csch_text_t *txt;

		txt = create_dyntext(ctx, ctx->grp, "sym-primary", ctx->state.text_str, ctx->state.origin_x, ctx->state.origin_y, ctx->state.rot);
		txt->hdr.floater = 1;
		/* don't free ctx->state.text_str, ownership passed to the text object */
	}
	else
		free(ctx->state.text_str);

	ctx->state.text_str = NULL;
	ctx->state.is_visible = 0;
}

void sch_bxl_set_text_str(sch_bxl_ctx_t *ctx, char *str)
{
	SKIP_FREE(str);
	free(ctx->state.text_str);
	ctx->state.text_str = str;
}


void sch_bxl_poly_begin(sch_bxl_ctx_t *ctx)
{
	SKIP;
	rnd_message(RND_MSG_ERROR, "Polygon in symbol not yet supported; please mail your bxl file to the developer!\n");
	ctx->state.delayed_poly = 1;
}

void sch_bxl_poly_add_vertex(sch_bxl_ctx_t *ctx, rnd_coord_t x, rnd_coord_t y)
{
	SKIP;
	assert(ctx->state.poly != NULL);
	TODO("pcb_poly_point_new(ctx->state.poly, x + ctx->state.origin_x, y + ctx->state.origin_y);");
}

void sch_bxl_poly_end(sch_bxl_ctx_t *ctx)
{
	SKIP;
	assert(ctx->state.poly != NULL);
#if 0
	if (pcb_poly_is_valid(ctx->state.poly)) {
		pcb_add_poly_on_layer(ctx->state.layer, ctx->state.poly);
/*		pcb_poly_init_clip(ctx->subc->data, ctx->state.layer, ctx->state.poly);*/
	}
	else {
		ctx->warn.poly_broken++;
		pcb_poly_free(ctx->state.poly);
	}
#endif

	ctx->state.poly = NULL;
	ctx->state.delayed_poly = 0;
}

void sch_bxl_text_style_begin(sch_bxl_ctx_t *ctx, char *name)
{
	sch_bxl_text_style_t *ts = htsp_get(&ctx->text_name2style, name);
	if (ts == NULL) {
		ts = calloc(sizeof(sch_bxl_text_style_t), 1);
		htsp_set(&ctx->text_name2style, name, ts); /* name is not free'd at the caller */
	}
	else
		rnd_message(RND_MSG_WARNING, "bxl footprint error: text style '%s' is redefined; second definition will override first\n", name);
	ctx->state.text_style = ts;
}

void sch_bxl_text_style_end(sch_bxl_ctx_t *ctx)
{
	ctx->state.text_style = NULL;
}

void sch_bxl_set_text_style(sch_bxl_ctx_t *ctx, const char *name)
{
	ctx->state.text_style = htsp_get(&ctx->text_name2style, name);
	if (ctx->state.text_style == NULL)
		rnd_message(RND_MSG_WARNING, "bxl footprint error: text style '%s' not defined (using default style)\n", name);
}


void sch_bxl_set_text_style_ref(sch_bxl_ctx_t *ctx, const char *name)
{
	ctx->state.text_style = htsp_get(&ctx->text_name2style, name);
	if (ctx->state.text_style == NULL)
		rnd_message(RND_MSG_WARNING, "bxl footprint error: text style '%s' not defined (using default style)\n", name);
}

static void pin_free(sch_bxl_ctx_t *ctx)
{
	free(ctx->pin.name);
	free(ctx->pin.des);
	memset(&ctx->pin, 0, sizeof(ctx->pin));
}

void sch_bxl_pin_begin(sch_bxl_ctx_t *ctx)
{
	SKIP;
	pin_free(ctx);
}

void sch_bxl_pin_des(sch_bxl_ctx_t *ctx, char *des, double x, double y)
{
	free(ctx->pin.des);
	ctx->pin.des = des;
	ctx->pin.des_x = x;
	ctx->pin.des_y = y;
}

void sch_bxl_pin_name(sch_bxl_ctx_t *ctx, char *name, double x, double y)
{
	free(ctx->pin.name);
	ctx->pin.name = name;
	ctx->pin.name_x = x;
	ctx->pin.name_y = y;
}


void sch_bxl_pin_end(sch_bxl_ctx_t *ctx)
{
	csch_chdr_t *term;
	csch_source_arg_t *src;
	csch_attribs_t *attr;
	char tmp[128];
	const char *real_name;

	SKIP;

	src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
	term = csch_alien_mkpin_line(&ctx->alien, src, ctx->grp, 0, 0, ctx->pin.length, 0);

	/* attributes */
	attr = &((csch_cgrp_t *)term)->attr;

	src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
	sprintf(tmp, "%d", ctx->pin.num);
	csch_attrib_set(attr, CSCH_ATP_USER_DEFAULT, "pcb/pinnum", tmp, src, NULL);
	real_name = tmp;

	if ((ctx->pin.name != NULL) && (*ctx->pin.des != '\0')) {
		src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
		csch_attrib_set(attr, CSCH_ATP_USER_DEFAULT, "-bxl/des", ctx->pin.des, src, NULL);
		real_name = ctx->pin.des;
	}


	if ((ctx->pin.name != NULL) && (*ctx->pin.name != '\0')) {
		src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
		csch_attrib_set(attr, CSCH_ATP_USER_DEFAULT, "-bxl/name", ctx->pin.name, src, NULL);
		real_name = ctx->pin.name;
	}

	src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
	csch_attrib_set(attr, CSCH_ATP_USER_DEFAULT, "name", real_name, src, NULL);


	/* attrib text objects */
	create_dyntext(ctx, (csch_cgrp_t *)term, "term-primary", rnd_strdup("%../a.display/name%"), 0, 0, ctx->pin.rot);


	/* placement */
	if (ctx->pin.rot != 0)
		csch_rotate(ctx->alien.sheet, term, 0, 0, ctx->pin.rot, 0);

	csch_move(ctx->alien.sheet, term,
		csch_alien_coord_x(&ctx->alien, ctx->pin.origin_x),
		csch_alien_coord_y(&ctx->alien, ctx->pin.origin_y), 0);



	pin_free(ctx);
}


#define WARN_CNT(_count_, args) \
do { \
	long cnt = (bctx->warn._count_); \
	if (cnt > 0) rnd_message args; \
} while(0)


void sch_bxl_init(sch_bxl_ctx_t *bctx, const char *subsymname)
{
	memset(bctx, 0, sizeof(sch_bxl_ctx_t));

	if (subsymname == NULL)
		bctx->in_target_sym = 1; /* read the first one if the user didn't name any */
	bctx->subsymname = subsymname;

	htsp_init(&bctx->text_name2style, strhash_case, strkeyeq_case);
}

void sch_bxl_uninit(sch_bxl_ctx_t *bctx)
{
	htsp_entry_t *e;

TODO("verify warnings");
#if 0
	/* emit all accumulated warnings */
	WARN_CNT(poly_broken,        (RND_MSG_WARNING, "footprint contains %ld invalid polygons (polygons ignored)\n", cnt));
	WARN_CNT(property_null_obj,  (RND_MSG_WARNING, "footprint contains %ld properties that could not be attached to any object\n", cnt));
	WARN_CNT(property_nosep,     (RND_MSG_WARNING, "footprint contains %ld properties without separator between key and value\n", cnt));
#endif

	pin_free(bctx);

	for(e = htsp_first(&bctx->text_name2style); e != NULL; e = htsp_next(&bctx->text_name2style, e)) {
		free(e->key);
		free(e->value);
	}
	htsp_uninit(&bctx->text_name2style);
}

#undef WARN_CNT

/* Error is handled on the push side */
void sch_bxl_error(sch_bxl_ctx_t *ctx, sch_bxl_STYPE tok, const char *s) { }
