/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2024 Tibor 'Igor2' Palinkas
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.*
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; 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
 */

#include "config.h"

#include <genht/htsp.h>
#include <genht/hash.h>
#include <librnd/core/error.h>
#include <librnd/core/compat_misc.h>

#include "cnc_grp.h"

#include "extobj.h"

/* persistent copy of impl; both key and value are allocated; on unregister
   a zombie impl is kept with all fields (including name) set to NULL */
htsp_t extobjs_impls;

void csch_extobj_impl_reg(const csch_extobj_impl_t *impl)
{
	csch_extobj_impl_t *i = htsp_get(&extobjs_impls, impl->name);

	if (i != NULL) {
		/* reuse existing if it's a zombie */
		if (impl->name == i->name) {
			rnd_message(RND_MSG_ERROR, "Double registration of extobj %s (same)\n", impl->name);
			return;
		}
		if (impl->name != NULL) {
			rnd_message(RND_MSG_ERROR, "Double registration of extobj %s (different)\n", impl->name);
			return;
		}
		memcpy(i, impl, sizeof(csch_extobj_impl_t));
		return;
	}

	i = malloc(sizeof(csch_extobj_impl_t));
	memcpy(i, impl, sizeof(csch_extobj_impl_t));
	htsp_set(&extobjs_impls, rnd_strdup(impl->name), i);
}


void csch_extobj_impl_unreg(const csch_extobj_impl_t *impl)
{
	csch_extobj_impl_t *i = htsp_get(&extobjs_impls, impl->name);

	if (i == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't unregister extobj %s (not in hash)\n", impl->name);
		return;
	}
	if (impl->name != i->name) {
		rnd_message(RND_MSG_ERROR, "Can't unregister extobj %s (name ptr mismatch)\n", impl->name);
		return;
	}

	/* retain it as a zombie */
	memset(i, 0, sizeof(csch_extobj_impl_t));
}


const csch_extobj_impl_t *csch_extobj_lookup(const char *name)
{
	csch_extobj_impl_t *i;

	if ((name == NULL) || (*name == '\0'))
		return NULL;

	i = htsp_get(&extobjs_impls, name);
	if (i != NULL)
		return i;

	/* allocate zombie for persistent pointer; an extobj plugin loaded later
	   would fill in this struct on registration */
	i = calloc(sizeof(csch_extobj_impl_t), 1);
	htsp_set(&extobjs_impls, rnd_strdup(name), i);
	rnd_message(RND_MSG_ERROR, "Can't find extobj %s\n", name);
	return i;
}

csch_cgrp_t *csch_extobj_gfx_lookup(csch_cgrp_t *grp)
{
	htip_entry_t *e;

	for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) {
		csch_cgrp_t *gfx = (csch_cgrp_t *)e->value;
		if (csch_obj_is_grp(&gfx->hdr) && (gfx->role == CSCH_ROLE_EXTOBJ_GFX))
			return gfx;
	}

	return NULL;
}

csch_cgrp_t *csch_extobj_gfx_clear(csch_sheet_t *sheet, csch_cgrp_t *grp)
{
	csch_cgrp_t *gfx = csch_extobj_gfx_lookup(grp);

	if (gfx == NULL) { /* create new empty */
		csch_source_arg_t *src = csch_attrib_src_c(NULL, 0, 0, NULL);
		gfx = csch_cgrp_alloc(sheet, grp, csch_oid_new(sheet, grp));
		csch_attrib_set(&gfx->attr, 0, "role", "extobj-gfx", src, NULL);
		gfx->role = CSCH_ROLE_EXTOBJ_GFX;
		csch_sheet_set_changed(sheet, 1);
		return gfx;
	}

	csch_cgrp_clear(gfx);
	return gfx;
}

void csch_extobj_init(void)
{
	htsp_init(&extobjs_impls, strhash, strkeyeq);
}

void csch_extobj_uninit(void)
{
	htsp_entry_t *e;
	for(e = htsp_first(&extobjs_impls); e != NULL; e = htsp_next(&extobjs_impls, e)) {
		free(e->key);
		free(e->value);
	}
	htsp_uninit(&extobjs_impls);
}

void csch_extobj_conv_from(vtp0_t *objs, const csch_extobj_impl_t *eo)
{
	if (eo->conv_from == NULL)
		return;

	while(objs->used > 0) {
		long orig = objs->used;

		eo->conv_from(objs);
		if (objs->used == orig)
			break; /* failed to convert any, no need to retry */
	}
}

