/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - spice model libs
 *  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
 */

/*** local lib support ***/

static void spicelib_local_ins(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(&be_spicelib_mod, root_dir, rnd_strdup(grp->loclib_name), CSCH_SLIB_STATIC);
	newent->backend_data.lng[0] = grp->hdr.oid;
}

/* hash: a htsi_t, keyed with indirect spicelib groups are stored in the
   indirect/purpose=spicelib group's backend_data->ptr[0]; this is used
   for quick lookup of spicelib_name -> grp; key is grp->loclib_name (not
   alloced/free'd for the hash table). backend_data->ptr[0] points
   to the indirect/purpose=spicelib grp */
static void spicelib_sheet_init_(rnd_design_t *hl, csch_lib_t *root_dir, const csch_cgrp_t *spicelib_root)
{
	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 *)spicelib_root;

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

static int spicelib_mod_map_local(rnd_design_t *hl, csch_lib_t *root_dir, const csch_cgrp_t *indirect)
{
	csch_sheet_t *sheet = (csch_sheet_t *)hl;
	const csch_cgrp_t *root_grp;

	/* do not automatically create a local spicelib dir (we are at opening a sheet) */
	root_grp = csch_loclib_get_root(sheet, spicelibmaster, NULL, 0, NULL);
	if (root_grp != NULL)
		spicelib_sheet_init_(hl, root_dir, root_grp);

	return 0;
}

static void spicelib_sheet_init(csch_sheet_t *sheet, csch_lib_t **root_dir_out, int alloc)
{
	csch_lib_t *root_dir = NULL;
	int alloced;
	csch_cgrp_t *root_grp;
	csch_source_arg_t *src;

	src = csch_attrib_src_p("spicelib", NULL);
	if (csch_loclib_get_roots(&root_dir, &root_grp, spicelibmaster, sheet, src, alloc, &alloced) == 0)
		spicelib_sheet_init_(&sheet->hidlib, root_dir, root_grp);

	if (root_dir_out != NULL)
		*root_dir_out = root_dir;
}

static void spicelib_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;
}

static int spicelib_mod_load(csch_sheet_t *sheet, void *dst_, csch_lib_t *src, const char *params)
{
	/* real loading happens through spicelib_lib_lookup(); this is called from the
	   lib window (will be needed for the preview) */
	return -1;
}

static void spicelib_mod_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
		spicelib_sheet_uninit(src); /* src is a root dir */
}


static const char *spicelib_get_from_loclib(csch_sheet_t *sheet, const char *spicelib_name)
{
	csch_lib_t *root_dir;
	csch_cgrp_t *gd;
	htsp_t *map;

	spicelib_sheet_init(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, spicelib_name);
/*rnd_trace("*** get: map=%p '%s' -> %p\n", map, spicelib_name, gd);*/
	if (gd == NULL)
		return NULL;

	return csch_attrib_get_str(&gd->attr, "spice/model_card");
}

static void spicelib_set_attr_in_loclib(csch_sheet_t *sheet, csch_cgrp_t *dst, gds_t *dma)
{
	csch_source_arg_t *src = csch_attrib_src_p("spicelib", "external lib");

	csch_attrib_set(&dst->attr, CSCH_ATP_USER_DEFAULT, "spice/model_card", dma->array, src, NULL);
	csch_sheet_set_changed(sheet, 1);
}

void spicelib_set_in_loclib(csch_sheet_t *sheet, const char *spicelib_name, gds_t *dma)
{
	csch_lib_t *root_dir;
	csch_cgrp_t *spicelib_root;
	htsp_t *map;
	csch_cgrp_t *gd;

	spicelib_sheet_init(sheet, &root_dir, 1);

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

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

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


	gd->loclib_name = rnd_strdup(spicelib_name);
	spicelib_set_attr_in_loclib(sheet, gd, dma);

	spicelib_local_ins(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);*/


}


static int spicelib_loc_refresh_from_ext(csch_sheet_t *sheet, csch_lib_t *src)
{
	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;
	spicelib_t *spicelib;

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

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

	spicelib = (spicelib_t *)&data->payload;
	spicelib_set_attr_in_loclib(sheet, old, spicelib->text);
	return 0;
}


static long loc_list_recurse(csch_cgrp_t *grp, const char *spicelib_name, vtp0_t *res)
{
	long sum = 0;
	htip_entry_t *e;
	const char *spiceliba;

	spiceliba = csch_attrib_get_str(&grp->attr, "spicelib");
	if ((spiceliba != NULL) && (strcmp(spiceliba, spicelib_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 += loc_list_recurse(child, spicelib_name, res);
	}
	return sum;
}


static int spicelib_loc_list(csch_sheet_t *sheet, csch_lib_t *src)
{
	long cnt;
	vtp0_t arr = {0};

	cnt = loc_list_recurse(&sheet->direct, src->name, &arr);
	rnd_message(RND_MSG_INFO, "Found %ld references to spicelib %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;
}
