/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - back annotation parser
 *  Copyright (C) 2022, 2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022 and Entrust in 2023)
 *
 *  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
 */

/* Parse the old pcb-rnd bap format designed for geda back in 2016; included
   from the common line parser */


#include "bap_sphash.h"

static char *concat_raw(char **str, int *len, int numstr)
{
	int n, totlen = 0;
	char *start, *end;

	for(n = 0; n < numstr; n++)
		totlen += len[n] + 1;

	end = start = malloc(totlen);

	for(n = 0; n < numstr; end += len[n]+1, n++)
		memcpy(end, str[n], len[n]+1);

	return start;
}

/* Shift the next token from "src" (current token's start); pointer to next
   token start is stored in dst; true length (no separators) of src stored in
   srclen. New token ends at any char at seps; whitepsace before the new
   token is removed */
#define shiftl(dst, srclen, src, seps, error_inst) \
do { \
	char *sep = strpbrk(src, seps); \
	if (sep == NULL) { error_inst; } \
	else { \
		*sep = '\0'; \
		srclen = sep - src; \
		sep++; \
		while(isspace(*sep)) sep++; \
		dst = sep; \
	} \
} while(0)
#define shiftlc(dst, srclen, src, sepc, error_inst) \
do { \
	char *sep = strchr(src, sepc); \
	if (sep == NULL) { error_inst; } \
	else { \
		*sep = '\0'; \
		srclen = sep - src; \
		sep++; \
		while(isspace(*sep)) sep++; \
		dst = sep; \
	} \
} while(0)

static int backann_parse_net_info(sch_rnd_backann_t *ctx, sch_rnd_ba_type_t type, char *raw, long lineno)
{
	char *a[4];
	sch_rnd_ba_t *ba;
	int len[4];

	a[0] = raw;

	shiftl(a[1], len[0], a[0], " \t", {
		rnd_message(RND_MSG_ERROR, "Missing second arg in %s:%ld\n", ctx->fn, lineno);
		goto error;
	});

/*	rnd_trace("NET INFO:\n");*/

	while(*a[1] != '\0') {

		shiftlc(a[2], len[1], a[1], '-', {
			rnd_message(RND_MSG_ERROR, "Missing comp name arg in %s:%ld\n", ctx->fn, lineno);
			goto error;
		});

		shiftl(a[3], len[2], a[2], " \t\r\n", {
			rnd_message(RND_MSG_ERROR, "Missing term name arg in %s:%ld\n", ctx->fn, lineno);
			goto error;
		});

		ba = vtba_alloc_append(&ctx->list, 1);
		ba->type = type;
		ba->raw = concat_raw(a, len, 3);
		ba->value.any4.a0 = ba->raw;
		ba->value.any4.a1 = ba->value.any4.a0 + len[0] + 1;
		ba->value.any4.a2 = ba->value.any4.a1 + len[1] + 1;
		ba->value.any4.a3 = NULL;

/*		rnd_trace("  '%s' '%s' '%s' sep='%s'\n", ba->value.any4.a0, ba->value.any4.a1, ba->value.any4.a2, a[3]);*/
		a[1] = a[3];
	}

	return 0;

	error:;
	return -1;
}


static int backann_parse_entry(sch_rnd_backann_t *ctx, sch_rnd_ba_type_t type, char *args, long lineno)
{
	int len[4];
	char *raw, *a[4];
	sch_rnd_ba_t *ba;

	raw = rnd_strdup(args);
	a[0] = raw;

	if ((type == SCH_RND_BAT_CONN_ADD) || (type == SCH_RND_BAT_CONN_DEL)) {
		shiftlc(a[1], len[0], a[0], '-', {
			rnd_message(RND_MSG_ERROR, "Missing term name %s:%ld\n", ctx->fn, lineno);
			goto error;
		});
	}
	else {
		shiftl(a[1], len[0], a[0], " \t", {
			rnd_message(RND_MSG_ERROR, "Missing second arg in %s:%ld\n", ctx->fn, lineno);
			goto error;
		});
	}

	shiftl(a[2], len[1], a[1], " \t", {
		rnd_message(RND_MSG_ERROR, "Missing third arg in %s:%ld\n", ctx->fn, lineno);
		goto error;
	});

	shiftl(a[3], len[2], a[2], "\r\n", {
		rnd_message(RND_MSG_ERROR, "Missing fourth arg termination in %s:%ld (line too long?)\n", ctx->fn, lineno);
		goto error;
	});

	(void)len[0];

	ba = vtba_alloc_append(&ctx->list, 1);
	ba->type = type;
	ba->raw = raw;
	ba->value.any4.a0 = a[0];
	ba->value.any4.a1 = a[1];
	ba->value.any4.a2 = a[2];
	ba->value.any4.a3 = a[3];
	return 0;

	error:;
	free(raw);
	return -1;
}

static int backann_parse_bap(sch_rnd_backann_t *ctx, char *line, int lineno)
{
	char *args = strpbrk(line, " \t\r\n");

	if (args == NULL) {
		rnd_message(RND_MSG_ERROR, "backann bap: Invalid command (no args) in %s:%ld\n", ctx->fn, lineno);
		return 1;
	}

	*args = '\0';
	args++;
	while(isspace(*args)) args++;

	switch(backann_bap_sphash(line)) {
		case backann_bap_net_info:      return backann_parse_net_info(ctx, SCH_RND_BAT_NETINFO, args, lineno);
		case backann_bap_change_attrib: return backann_parse_entry(ctx, SCH_RND_BAT_COMP_ATTR, args, lineno);
		case backann_bap_del_conn:      return backann_parse_entry(ctx, SCH_RND_BAT_CONN_DEL, args, lineno);
		case backann_bap_add_conn:      return backann_parse_entry(ctx, SCH_RND_BAT_CONN_ADD, args, lineno);
		default:
			rnd_message(RND_MSG_ERROR, "backann bap: Unknown command '%s' in %s:%ld\n", line, ctx->fn, lineno);
			return 1;
	}

	return 1;
}
