/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2024 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust in 2023)
 *
 *  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
 */

/* included from abstract.c */

/* Search netname for v/netname in all hlev parents. Return NULL if not found. */
RND_INLINE csch_anet_t *net_search_subtree_up(csch_abstract_t *abs, csch_hlevel_t *hlev, const char *netname)
{
	for(hlev = hlev->parent; hlev != NULL; hlev = hlev->parent) {
		csch_anet_t *net = htsp_get(&hlev->nets, netname);
		if ((net != NULL) && (net->scope == CSCH_ASCOPE_SUBTREE_LOCAL))
			return net;
	}

	return NULL;
}

csch_anet_t *csch_anet_get_at(csch_abstract_t *abs, csch_hlevel_t *hlev, csch_ascope_t scope, const char *netname)
{
	csch_anet_t *net = NULL;
	csch_ascope_t prefix_scope;
	const char *orig_name = netname;

	prefix_scope = csch_split_name_scope(&netname);
	if (scope != CSCH_ASCOPE_unknown) {
		if ((prefix_scope != CSCH_ASCOPE_AUTO) && (prefix_scope != scope)) {
			rnd_message(RND_MSG_ERROR, "Failed to resolve net '%s': conflicting scopes\n", orig_name);
			return NULL;
		}
	}
	else
		scope = prefix_scope;


	switch(scope) {
		case CSCH_ASCOPE_AUTO:
			/* first search on sheet local and parents for v/ nets */
			if (hlev != NULL) {
				net = htsp_get(&hlev->nets, netname);
				if (net != NULL)
					return net;
				net = net_search_subtree_up(abs, hlev, netname);
				if (net != NULL)
					return net;
			}
			return htsp_get(&abs->nets, netname); /* fall back to global */

		case CSCH_ASCOPE_GLOBAL:           return htsp_get(&abs->nets, netname);
		case CSCH_ASCOPE_SHEET_LOCAL:      return htsp_get(&hlev->nets, netname);
		case CSCH_ASCOPE_SUBTREE_LOCAL:
			/* search in sheet only so that multuple v/foo are merged together;
			   sub-sheets are compiled later so they can find this net searching upward */
			return htsp_get(&hlev->nets, netname);

		case CSCH_ASCOPE_SUBTREE_AUTO: /* The ^/ prefix */
			if (hlev == NULL) {
				rnd_message(RND_MSG_ERROR, "Failed to resolve net '%s': scope requires hierarchy which is not available\n", orig_name);
				return NULL;
			}
			/* search on sheet local and parents for v/ nets */
			return net_search_subtree_up(abs, hlev, netname);

		case CSCH_ASCOPE_unknown:
			rnd_message(RND_MSG_ERROR, "Failed to resolve net '%s': unknown scope\n", orig_name);
			return NULL;
	}
	return NULL;
}

csch_anet_t *csch_anet_get(csch_abstract_t *abs, const char *netname)
{
	return csch_anet_get_at(abs, NULL, CSCH_ASCOPE_unknown, netname);
}


csch_anet_t *csch_anet_new(csch_abstract_t *abs, csch_hlevel_t *hlev, csch_ascope_t scope, const char *name_glob, const char *name_loc, int set_no_uname)
{
	csch_anet_t *net = calloc(sizeof(csch_anet_t), 1);
	int short_name;

	if (scope == CSCH_ASCOPE_SUBTREE_AUTO) {
		rnd_message(RND_MSG_ERROR, "Can not find subtree-local net v/%s\n", name_loc);
		return NULL;
	}


	/* create global nets with the short name; AUTO is taken GLOBAL: if we got
	   here no other binding worked for it */
	short_name = (scope == CSCH_ASCOPE_GLOBAL) || (scope == CSCH_ASCOPE_AUTO);

	csch_aobj_init(abs, &net->hdr, CSCH_ATYPE_NET);
	net->name = rnd_strdup(short_name ? name_loc : name_glob);
	net->name_loc = rnd_strdup(name_loc);
	net->no_uname = set_no_uname;
	net->hlev = hlev;
	net->hdepth = (hlev == NULL) ? 0 : hlev->hdepth;

	htsp_set(&abs->nets, net->name, net);
	if (hlev != NULL) {
		switch(scope) {
			case CSCH_ASCOPE_AUTO:       /* if we got here, we are adding it as global */
			case CSCH_ASCOPE_GLOBAL:
			case CSCH_ASCOPE_unknown:
				break; /* has no impact on local */

			case CSCH_ASCOPE_SUBTREE_LOCAL:
			case CSCH_ASCOPE_SHEET_LOCAL:
				htsp_set(&hlev->nets, net->name_loc, net);
				break;

			case CSCH_ASCOPE_SUBTREE_AUTO:
				return NULL; /* can't get here */
		}
	}
	net->scope = scope;
	return net;
}

