/*
 *                            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
 */

/*** local lib support ***/

static void anymap_local_ins(anymap_ctx_t *actx, htsp_t *map, csch_lib_t *root_dir, csch_cgrp_t *grp)
{
	csch_lib_t *newent;

	htsp_set(map, grp->loclib_name, grp);

	newent = csch_lib_alloc_append(actx->be, root_dir, rnd_strdup(grp->loclib_name), CSCH_SLIB_STATIC);
	newent->backend_data.lng[0] = grp->hdr.oid;
}

static void anymap_sheet_uninit(csch_lib_t *root_dir)
{
	csch_cgrp_t *symlib = root_dir->backend_data.ptr[1];

	if (symlib == NULL) return;

	htsp_free(root_dir->backend_data.ptr[0]);

	root_dir->backend_data.ptr[0] = NULL;
	root_dir->backend_data.ptr[1] = NULL;
}

void anymap_sheet_init_(anymap_ctx_t *actx, rnd_design_t *hl, csch_lib_t *root_dir, const csch_cgrp_t *maproot)
{
	if (root_dir->backend_data.ptr[0] == NULL) {
		htip_entry_t *e;
		htsp_t *map;

		map = root_dir->backend_data.ptr[0] = htsp_alloc(strhash, strkeyeq);
		root_dir->backend_data.ptr[1] = (void *)maproot;

		if (root_dir->backend == NULL) {
			static csch_lib_backend_t anymap_be_sheet_free;
			anymap_be_sheet_free.name = "anymap_be_sheet_free";
			anymap_be_sheet_free.free = anymap_sheet_uninit;
			root_dir->backend = &anymap_be_sheet_free;
		}

		for(e = htip_first(&maproot->id2obj); e != NULL; e = htip_next(&maproot->id2obj, e)) {
			csch_cgrp_t *grp = e->value;
			if (grp->hdr.type == CSCH_CTYPE_GRP)
				anymap_local_ins(actx, map, root_dir, grp);
		}
	}
}


void anymap_lht_free(csch_lib_t *src)
{
	csch_lib_t *root_dir = src->parent;
	if (root_dir != NULL) {
		htsp_t *map = root_dir->backend_data.ptr[0];
		if (map != NULL)
			htsp_pop(map, src->name);
	}
	else
		anymap_sheet_uninit(src); /* src is a root dir */
}


csch_attribs_t *anymap_get_from_loclib(anymap_ctx_t *actx, csch_sheet_t *sheet, const char *mapobj_name)
{
	csch_lib_t *root_dir;
	csch_cgrp_t *gd;
	htsp_t *map;

	actx->sheet_init(actx, sheet, &root_dir, 0);

	if ((root_dir == NULL) || (root_dir->backend_data.ptr[0] == NULL))
		return NULL;

	map = root_dir->backend_data.ptr[0];

	gd = htsp_get(map, mapobj_name);
/*rnd_trace("*** get: map=%p '%s' -> %p\n", map, mapobj_name, gd);*/
	if (gd == NULL)
		return NULL;

	return &gd->attr;
}

static void anymap_set_attr_in_loclib(anymap_ctx_t *ctx, csch_sheet_t *sheet, csch_cgrp_t *dst, csch_attribs_t *dma)
{
	csch_source_arg_t *src = csch_attrib_src_p(ctx->eng_src_name, "external lib");

	csch_attrib_apply(&dst->attr, dma, src, NULL);
	csch_sheet_set_changed(sheet, 1);
}

void anymap_set_in_loclib(anymap_ctx_t *ctx, csch_sheet_t *sheet, const char *mapobj_name, csch_attribs_t *dma)
{
	csch_lib_t *root_dir;
	csch_cgrp_t *maproot;
	htsp_t *map;
	csch_cgrp_t *gd;

	ctx->sheet_init(ctx, sheet, &root_dir, 1);

	map = root_dir->backend_data.ptr[0];
	maproot = root_dir->backend_data.ptr[1];

	if ((map == NULL) || (maproot == NULL)) {
		rnd_message(RND_MSG_ERROR, "Failed to create %s local lib root for sheet %s\nNot building a local lib, sheet is not portable.\n", ctx->name, sheet->hidlib.loadname);
		return;
	}

	gd = csch_cgrp_alloc(sheet, maproot, csch_oid_new(sheet, maproot));
	if (gd == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to create %s local lib entry for %s in sheet %s\nNot building a local lib, sheet is not portable.\n", ctx->name, mapobj_name, sheet->hidlib.loadname);
		return;
	}


	gd->loclib_name = rnd_strdup(mapobj_name);
	anymap_set_attr_in_loclib(ctx, sheet, gd, dma);

	anymap_local_ins(ctx, map, root_dir, gd);
	rnd_event(&sheet->hidlib, CSCH_EVENT_LIBRARY_CHANGED, NULL);

/*rnd_trace("*** set: map=%p '%s' -> %p\n", map, gd->loclib_name, gd);*/


}


/* Return a map object */
static void *anymap_refresh_from_ext_get(anymap_ctx_t *actx, csch_sheet_t *sheet, csch_lib_t *src, csch_cgrp_t **old_out)
{
	csch_lib_t *root_dir = src->parent;
	htsp_t *map;
	ldch_data_t *data;
	csch_hook_call_ctx_t cctx = {0};
	csch_cgrp_t *old;

	map = root_dir->backend_data.ptr[0];
	*old_out = old = htsp_get(map, src->name);
	if (old == NULL) {
		rnd_message(RND_MSG_ERROR, "%s loclib internal error: can't find %s '%s'\n", actx->name, actx->name, src->name);
		return NULL;
	}

	/* check if it is loadable from the external lib */
	data = ldch_load_(&actx->maps, src->name, actx->low_parser, actx->high_parser, NULL, &cctx);
	if (data == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't find %s '%s' in the external %s lib\n", actx->name, src->name, actx->name);
		return NULL;
	}

	return &data->payload;
}

int anymap_loc_refresh_from_ext(anymap_ctx_t *actx, csch_sheet_t *sheet, csch_lib_t *src)
{
	csch_cgrp_t *old;
	anymap_obj_t *mapobj = (anymap_obj_t *)anymap_refresh_from_ext_get(actx, sheet, src, &old);

	if (mapobj == NULL)
		return -1;

	anymap_set_attr_in_loclib(actx, sheet, old, &mapobj->comp_attribs);
	return 0;
}


static long anymap_loc_list_recurse(csch_cgrp_t *grp, const char *mapobj_name, vtp0_t *res, const char *attr_key)
{
	long sum = 0;
	htip_entry_t *e;
	const char *devmapa;

	devmapa = csch_attrib_get_str(&grp->attr, attr_key);
	if ((devmapa != NULL) && (strcmp(devmapa, mapobj_name) == 0)) {
		vtp0_append(res, grp);
		sum++;
	}

	for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) {
		csch_cgrp_t *child = e->value;
		if (csch_obj_is_grp(&child->hdr))
			sum += anymap_loc_list_recurse(child, mapobj_name, res, attr_key);
	}
	return sum;
}


int anymap_loc_list(csch_sheet_t *sheet, csch_lib_t *src, const char *attr_key)
{
	long cnt;
	vtp0_t arr = {0};

	cnt = anymap_loc_list_recurse(&sheet->direct, src->name, &arr, attr_key);
	rnd_message(RND_MSG_INFO, "Found %ld references to %s\n", cnt, src->name);

	if (cnt > 0) {
		fgw_arg_t args[4], ares;

		args[1].type = FGW_STR; args[1].val.str = "objarr";
		fgw_ptr_reg(&rnd_fgw, &args[2], CSCH_PTR_DOMAIN_COBJ_ARR, FGW_PTR | FGW_STRUCT, &arr);
		rnd_actionv_bin(&sheet->hidlib, "TreeDialog", &ares, 3, args);
		fgw_ptr_unreg(&rnd_fgw, &args[2], CSCH_PTR_DOMAIN_COBJ_ARR);
		vtp0_uninit(&arr);
	}

	return 0;
}

