/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - target plugin helper
 *  Copyright (C) 2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 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
 */

/* Maintain a cache of parsed comp/net/conn objects and compile abstract
   model from them */

#include <libcschem/config.h>

#include <stdio.h>
#include <stdarg.h>
#include <librnd/core/plugins.h>
#include <librnd/core/compat_misc.h>
#include <genht/hash.h>
#include <genht/ht_utils.h>

#include "lib_target.h"

static char dflt[] = "default";


const char *sch_trgt_copy_alt_attrib(csch_ahdr_t *obj, int prio, const char *src_desc, const char *dst_name, ...)
{
	const char *val = NULL, *key;
	va_list ap;

	va_start(ap, dst_name);
	for(;;) {
		key = va_arg(ap, const char *);
		if (key == NULL)
			break;
		val = csch_attrib_get_str(&obj->attr, key);
		if (val != NULL) {
			csch_source_arg_t *src;
			src = csch_attrib_src_pa(obj, key, src_desc, NULL);
			csch_attrib_set(&obj->attr, prio, dst_name, val, src, NULL);
			break;
		}
	}
	va_end(ap);
	return key;
}


void sch_trgt_ctx_init(sch_trgt_ctx_t *ctx, const rnd_conflist_t *whitelist, const rnd_conflist_t *blacklist, const char *flow_prefix, const char *sw_prefix)
{
	rnd_conf_listitem_t *item_li;
	const char *item_str;
	int idx;

	ctx->flow_prefix = flow_prefix;
	ctx->sw_prefix = sw_prefix;
	if (flow_prefix != NULL) ctx->flow_prefix_len = strlen(flow_prefix);
	if (sw_prefix != NULL) ctx->sw_prefix_len = strlen(sw_prefix);

	ctx->has_white = (whitelist != NULL) && (rnd_conflist_length(whitelist) > 0);
	ctx->has_black = (blacklist != NULL) && (rnd_conflist_length(blacklist) > 0);

	if (ctx->has_white) {
		htss_init(&ctx->white, strhash, strkeyeq);
		rnd_conf_loop_list_str(whitelist, item_li, item_str, idx) {
			char *sep = strchr(item_str, '>');
			if ((sep != NULL) && ((sep > item_str) || (sep[-1] == '-'))) {
				int seppos = sep - item_str - 1;
				char *key = rnd_strdup(item_str), *val;
				key[seppos] = '\0';
				val = key + seppos + 2;
				htss_set(&ctx->white, key, val);
			}
			else
				htss_set(&ctx->white, (char *)item_str, dflt);
		}
	}

	if (ctx->has_black) {
		htss_init(&ctx->black, strhash, strkeyeq);
		rnd_conf_loop_list_str(blacklist, item_li, item_str, idx) {
			htss_set(&ctx->white, (char *)item_str, dflt);
		}
	}
}

RND_INLINE int match_prefix_len(const char *key, const char *prefix, int prefix_len)
{
	if (memcmp(key, prefix, prefix_len) != 0) return 0;
	if (key[prefix_len] != ':') return 0;
	return 1;
}

void sch_trgt_export_attrs(sch_trgt_ctx_t *ctx, csch_ahdr_t *obj)
{
	htsp_entry_t *e;

	/* config says we shouldn't do anything */
	if (!ctx->has_white && !ctx->has_black && (ctx->flow_prefix == NULL) && (ctx->sw_prefix == NULL))
		return;

	for(e = htsp_first(&obj->attr); e != NULL; e = htsp_next(&obj->attr, e)) {
		const char *export_name = NULL;
		csch_attrib_t *a = e->value;

		if (ctx->has_white) {
			const char *wh = htss_get(&ctx->white, e->key);
			if (wh == dflt) export_name = e->key;
			else if (wh != NULL) export_name = wh;
		}

		if ((ctx->sw_prefix != NULL) && match_prefix_len(e->key, ctx->sw_prefix, ctx->sw_prefix_len))
			export_name = e->key + ctx->sw_prefix_len + 1;
		if ((ctx->flow_prefix != NULL) && match_prefix_len(e->key, ctx->flow_prefix, ctx->flow_prefix_len))
			export_name = e->key + ctx->flow_prefix_len + 1;

		/* black list: remove export name even if it was set by some other plugin beforehand */
		if (ctx->has_black) {
			const char *wh = htss_get(&ctx->black, e->key);
			if (wh != dflt) {
				if (a->export_name != NULL) {
					free(a->export_name);
					a->export_name = NULL;
				}
				continue;
			}
		}

		if (export_name != NULL)
			csch_attrib_set_export_name(a, export_name);
	}
}

void sch_trgt_ctx_uninit(sch_trgt_ctx_t *ctx)
{
	if (ctx->has_white) {
		genht_uninit_deep(htss, &ctx->white, {
			/* key has been allocated only if there was a value as well */
			if (htent->value != dflt)
				free(htent->key);
		});
		ctx->has_white = 0;
	}

	if (ctx->has_black) {
		htss_uninit(&ctx->black);
		ctx->has_black = 0;
	}
}


/*** plugin ***/

int pplg_check_ver_lib_target(int ver_needed) { return 0; }

void pplg_uninit_lib_target(void)
{
}

int pplg_init_lib_target(void)
{
	RND_API_CHK_VER;

	return 0;
}

