/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - standard device mapper
 *  Copyright (C) 2022 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
 */

/*** devmap library handling ***/

static char *devmap_lib_lookup(ldch_ctx_t *ctx, const char *load_name, ldch_low_parser_t *low, ldch_high_parser_t *high, void *low_call_ctx, void *high_call_ctx)
{
/*	csch_hook_call_ctx_t *cctx = high_call_ctx;     for cctx->project */
/*	fgw_obj_t *obj = ctx->user_data;*/
	csch_lib_t *le;

	/* if full path is known */
	if (strchr(load_name, '/') != NULL)
		return rnd_strdup(load_name);

	TODO("we shouldn't search master, only sheet's devmap libs, but the"
	     "abstract model doesn't have sheets");
	le = csch_lib_search_master(devmaster, load_name, CSCH_SLIB_STATIC);
	if (le == NULL) {
		char *load_name2 = rnd_concat(load_name, ".devmap", NULL);
		le = csch_lib_search_master(devmaster, load_name2, CSCH_SLIB_STATIC);
		free(load_name2);
	}
/*	rnd_trace("devmap lookup: %s -> %p\n", load_name, le);*/

	return le == NULL ? NULL : rnd_strdup(le->realpath);
}

/* effectively a test-parse */
csch_lib_type_t devmap_lht_file_type(rnd_design_t *hl, const char *fn)
{
	FILE *f;
	int n;
	csch_lib_type_t res = CSCH_SLIB_invalid;

	f = rnd_fopen(hl, fn, "r");
	if (f == NULL)
		return res;

	for(n = 0; n < 16; n++) {
		char *s, line[1024];
		s = fgets(line, sizeof(line), f);
		if (s == NULL) break;
		if (strstr(s, "ha:std_devmap.v") == 0) {
			res = CSCH_SLIB_STATIC;
			break;
		}
	}
	fclose(f);
	return res;
}


static char *devmap_lht_realpath(rnd_design_t *hl, char *root)
{
	/* accept only non-prefixed paths for now */
	if (strchr(root, '@') != NULL)
		return NULL;

	return csch_lib_fs_realpath(hl, root);
}

static int devmap_lht_map(rnd_design_t *hl, csch_lib_t *root_dir)
{
	gds_t tmp = {0};
	gds_append_str(&tmp, root_dir->realpath);
	csch_lib_fs_map(hl, &be_devmap_lht, root_dir, &tmp, devmap_lht_file_type);
	gds_uninit(&tmp);
	return 0;
}



/* Load (and cache-parse) a devmap from the external libs */
static csch_attribs_t *devmap_get_extlib(devmap_ctx_t *ctx, const char *devmap_name, csch_hook_call_ctx_t *cctx, int save_in_local)
{
	ldch_data_t *data;
	devmap_t *devmap;
	csch_attribs_t *res;
	long n;

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

	devmap = (devmap_t *)&data->payload;
	res = &devmap->comp_attribs;

	if (save_in_local) {
/*		rnd_trace("devmap: inserting %s in local libs:\n", devmap_name);*/
		for(n = 0; n < ctx->ssyms.used; n++) {
			csch_cgrp_t *sym = ctx->ssyms.array[n];
/*			rnd_trace("         %s\n", sym->hdr.sheet->hidlib.fullpath);*/
			rnd_message(RND_MSG_INFO, "devmap: inserting %s in local lib of %s\n", devmap_name, sym->hdr.sheet->hidlib.fullpath);
			devmap_set_in_loclib(sym->hdr.sheet, devmap_name, res);
		}
	}

	return res;
}

/* Look up and return component attributes for the component's devmap name:
   - first look in the local lib; if present, load from there
   - if not, look in the library and import into the local lib
   - return NULL if both fail
*/
static csch_attribs_t *devmap_get_from_any_lib(devmap_ctx_t *ctx, csch_acomp_t *comp, csch_attrib_t *adm, void *ucallctx)
{
	csch_attribs_t *res = NULL, *loc_dm;
	csch_cgrp_t *res_sym = NULL;
	const char *devmap_name;
	long n;

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

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

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

	/* figure which symbol(s) have this devmap */
	ctx->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, "devmap");
/*rnd_trace("devmap: comp: %s devmap: %s sym's: '%s'\n", comp->name, devmap_name, cdmn);*/
		if ((cdmn == NULL) || (strcmp(cdmn, devmap_name) != 0))
			continue;

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

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

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

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

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

/*	rnd_trace("devmap: %s is not in our local libs\n", devmap_name);*/
	res = devmap_get_extlib(ctx, devmap_name, ucallctx, 1);
	if (res == NULL) {
		rnd_message(RND_MSG_ERROR, "Devmap %s not found in the library for component %s\n", devmap_name, comp->name);
		return NULL;
	}

	return res;
}

