/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - any map helpers (e.g. devmap/funcmap)
 *  Copyright (C) 2022,2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust Fund 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., 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
 */

/*** devmap library handling ***/

static void anymap_save_in_local(anymap_ctx_t *ctx, const char *anyobj_name, csch_attribs_t *dma)
{
	long n;

/*		rnd_trace("anymap: inserting %s in local libs:\n", anyobj_name);*/
	for(n = 0; n < ctx->ssyms.used; n++) {
		csch_cgrp_t *sym = ctx->ssyms.array[n];
		csch_attribs_t *existing;
/*			rnd_trace("         %s\n", sym->hdr.sheet->hidlib.fullpath);*/
		existing = anymap_get_from_loclib(ctx, sym->hdr.sheet, anyobj_name);
		if (existing == NULL) {
			rnd_message(RND_MSG_INFO, "%s: inserting %s in local lib of %s\n", ctx->name, anyobj_name, sym->hdr.sheet->hidlib.fullpath);
			anymap_set_in_loclib(ctx, sym->hdr.sheet, anyobj_name, dma);
		}
	}
}

/* Load (and cache-parse) a mapobj from the external libs */
csch_attribs_t *anymap_get_extlib(anymap_ctx_t *ctx, const char *mapobj_name, csch_hook_call_ctx_t *cctx, int save_in_local)
{
	ldch_data_t *data;
	anymap_obj_t *mapobj;
	csch_attribs_t *res;

	data = ldch_load_(&ctx->maps, mapobj_name, ctx->low_parser, ctx->high_parser, NULL, cctx);
	if (data == NULL)
		return NULL;

	mapobj = (anymap_obj_t *)&data->payload;
	res = &mapobj->comp_attribs;

	if (save_in_local)
		anymap_save_in_local(ctx, mapobj_name, res);

	return res;
}

/* INTERNAL CALL
   Look up and return component attributes for the component's mapobj name:
   look in the local lib; if present, load from there
   - if not, look in the library and import into the local lib
   - if that fails, set *get_name_from_ext to the mapobj name that should be
     imported from the external lib by the caller
   - return NULL if both fail
*/
static csch_attribs_t *anymap_get_from_int_lib(anymap_ctx_t *actx, csch_acomp_t *comp, csch_attrib_t *adm, void *ucallctx, const char **get_name_from_ext)
{
	csch_attribs_t *res = NULL, *loc_dm;
	csch_cgrp_t *res_sym = NULL;
	const char *mapobj_name;
	long n;

	*get_name_from_ext = 0;

	if ((adm == NULL) || (adm->deleted)) return NULL;
	mapobj_name = adm->val;

	if (mapobj_name == NULL) {
		rnd_message(RND_MSG_ERROR, "Devmap attribute should be a string in component %s\n", comp->name);
		return NULL;
	}

	if (*mapobj_name == '\0') /* empty value is okay, it means no mapobj */
		return NULL;

	/* figure which symbol(s) have this mapobj */
	actx->ssyms.used = 0;
	for(n = 0; n < comp->hdr.srcs.used; n++) {
		csch_cgrp_t *sym = comp->hdr.srcs.array[n];
		const char *cdmn;
		
		if (sym == NULL)
			continue;

		cdmn = csch_attrib_get_str(&sym->attr, actx->attr_key);
/*rnd_trace("anymap: comp: %s devmap: %s sym's: '%s'\n", comp->name, mapobj_name, cdmn);*/
		if ((cdmn == NULL) || (strcmp(cdmn, mapobj_name) != 0))
			continue;

		vtp0_append(&actx->ssyms, sym);
	}

	if (actx->ssyms.used == 0) {
		rnd_message(RND_MSG_ERROR, "%s internal error in component %s: no source symbol has the right %s attribute\n", actx->name, comp->name, actx->name);
		return NULL;
	}

	/* if there are more than one, compare the relevant ones and remember the first */
	for(n = 0; n < actx->ssyms.used; n++) {
		csch_cgrp_t *sym = actx->ssyms.array[n];

		loc_dm = anymap_get_from_loclib(actx, sym->hdr.sheet, mapobj_name);
		if (res != NULL) {
			if ((loc_dm != NULL) && (!csch_attrib_eq(res, loc_dm))) {
				rnd_message(RND_MSG_ERROR, "%s local lib sync error sheet %s and %s has different copy of %s %s\nPlease sync your local libs before compiling!\n", actx->name, sym->hdr.sheet->hidlib.fullpath, res_sym->hdr.sheet->hidlib.fullpath, actx->name, mapobj_name);
				return NULL;
			}
		}
		else {
			res = loc_dm;
			if (res != NULL)
				res_sym = sym;
		}
	}

	if (res != NULL) {
/*		rnd_trace("anymap: served %s from local lib of sheet %s\n", mapobj_name, res_sym->hdr.sheet->hidlib.fullpath);*/
		return res;
	}

	*get_name_from_ext = mapobj_name;
	return res;
}


csch_attribs_t *anymap_get_from_any_lib(anymap_ctx_t *actx, csch_acomp_t *comp, csch_attrib_t *adm, void *ucallctx)
{
	const char *get_from_ext;
	csch_attribs_t *res = anymap_get_from_int_lib(actx, comp, adm, ucallctx, &get_from_ext);

	if (get_from_ext != NULL) {
/*	rnd_trace("anymap: %s is not in our local libs\n", get_from_ext);*/
		res = anymap_get_extlib(actx, get_from_ext, ucallctx, 1);
		if (res == NULL) {
			rnd_message(RND_MSG_ERROR, "%s %s not found in the library for component %s\n(Hint: after creating the %s in the lib: file menu, maintenance, re-scan devmap)\n", actx->name, get_from_ext, comp->name, actx->name);
			return NULL;
		}
	}
	return res;
}

