/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - lib: compile helpers
 *  Copyright (C) 2022 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022)
 *
 *  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., 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
 */

#include "config.h"

#include <librnd/core/error.h>
#include <librnd/core/misc_util.h>

#include "abstract.h"
#include "compile.h"
#include "libcschem.h"
#include "engine.h"
#include "actions_csch.h"

#include "util_compile.h"

csch_anet_t *csch_cmp_merge_nets(csch_anet_t *n1, csch_anet_t *n2)
{
	csch_anet_t *src, *dst;
	long n, i;

	if (n1 == n2)
		return n1;

	/* dst should be the one without user chosen name */
	if (!n1->no_uname && !n2->no_uname) {
		csch_anet_t *top, *bottom;

		if (n1->hdepth == n2->hdepth) {
			rnd_message(RND_MSG_ERROR, "csch_cmp_merge_nets(): different networks '%s' and '%s' can not be connected\n", n1->name, n2->name);
			return NULL;
		}
		if (CSCH_CFG(hier_net_allow_rename, 0) == 0) {
			rnd_message(RND_MSG_ERROR, "csch_cmp_merge_nets(): the same network is explicitly named differently between hierarchi levels: '%s' and '%s'\n", n1->name, n2->name);
			return NULL;
		}

		if (n1->hdepth < n2->hdepth) {
			top = n1;
			bottom = n2;
		}
		else {
			top = n2;
			bottom = n1;
		}
		if (CSCH_CFG(hier_net_rename_prefer_top, 1) != 0) {
			dst = top;
			src = bottom;
		}
		else {
			dst = bottom;
			src = top;
		}
	}
	else if (!n1->no_uname) {
		dst = n1;
		src = n2;
	}
	else {
		dst = n2;
		src = n1;
	}

	/* switch over the concrete->abstract pointers on src net so src concrete
	   objects know they belong to dst net now */
	for(n = 0; n < src->hdr.srcs.used; n++) {
		csch_cgrp_t *cgrp = src->hdr.srcs.array[n];
		long i;

		if (!csch_obj_is_grp(&cgrp->hdr))
			continue;

		for(i = 0; i < cgrp->aid.used; i++)
			if (cgrp->aid.array[i] == src->hdr.aid)
				cgrp->aid.array[i] = dst->hdr.aid;
	}

	for(n = 0; n < src->conns.used; n++) {
		csch_aport_t *ap = (csch_aport_t *)src->conns.array[n];
		if (ap->hdr.type == CSCH_ATYPE_PORT)
			ap->conn.net = NULL; /* disconnect from src */
		csch_compile_connect_net_to(&dst, &ap->hdr, 1);
	}
	src->conns.used = 0;

	/* copy/merge all attributes */
	csch_attrib_copy_all_undoable(NULL, NULL, &dst->hdr.attr, &src->hdr.attr, 0);

	/* copy all sources from src to dst */
	for(n = 0; n < src->hdr.srcs.used; n++)
		csch_compile_add_source(src->hdr.srcs.array[n], &dst->hdr);

	/* remove sources from the source net which should become empty */
	for(n = 0; n < src->hdr.srcs.used; n++) {
		csch_cgrp_t *obj = src->hdr.srcs.array[n];
		for(i = 0; i < obj->aid.used; i++) {
			if (obj->aid.array[i] == src->hdr.aid) {
				vtl0_remove(&obj->aid, i, 1);
				break;
			}
		}
	}
	src->hdr.srcs.used = 0;
	src->hdr.ghost = "csch_cmp_merge_nets()";

	return dst;
}

int csch_cmp_nameconn_port_net(csch_project_t *project, csch_acomp_t *comp, const char *portname, const char *netname, int require_conn, int view_id, csch_hier_path_t *hpath)
{
	csch_aport_t *port = csch_aport_get(comp->hdr.abst, comp, portname, 1);
	csch_anet_t *net; /* net from the argument */
	csch_ascope_t scope;
	char *name_glob, *name_loc;

	if (port == NULL) {
		rnd_message(RND_MSG_ERROR, "Component '%s' doesn't contain port named '%s'\n", comp->name, portname);
		return -1;
	}

	scope = csch_split_name_scope(&netname);

	/* Translate name, including hierarchical address prefixes */
	csch_eng_call_namemod(project, view_id, hpath, CSCH_ENGHK_ATTRIB_NET_NAME_TO_NET_NAME,
		&name_glob, &name_loc, netname, FGW_AOBJ, comp, FGW_INVALID);

	net = csch_anet_get_at(comp->hdr.abst, comp->hlev, scope, name_loc);
	if (net == NULL)
		net = csch_anet_new(comp->hdr.abst, comp->hlev, scope, name_glob, name_loc, 0);

	csch_eng_free_namemod(&name_glob, &name_loc);

	if (net == NULL) {
		rnd_message(RND_MSG_ERROR, "Netname '%s' does not exist\n", netname);
		return -1;
	}
	if (port->conn.net == NULL) {
TODO("TODO#rgc: drc/require_graphical_conn should be checked in drc");
/* TODO: cases to check:
   - place resistor, add conn array 4:foo; this should connect a 4th port of
     the resistor to network foo, with no error; csch_compile_connect_net_to()
     below does that properly; require_conn=0 case
   - place an unconnected gnd symbol; it has an 1:GND which would do the same,
     but with an extra drc attribute on the port this should rather throw
     an error; require_conn=1 case
*/
		csch_compile_connect_net_to(&net, &port->hdr, 1);
		if (require_conn) {
			csch_attrib_t *a = csch_attrib_get(&port->hdr.attr, "drc/require_graphical_conn");
			if ((a != NULL) && rnd_istrue(a->val)) {
				rnd_message(RND_MSG_ERROR, "Port '%s-%s' has a non-graphical connection to net %s, but no graphical connection\n", comp->name, portname, netname);
				return -1;
			}
			else
				return 0;
		}
		return 0;
	}

	return (csch_cmp_merge_nets(net, port->conn.net) != NULL) ? 0 : -1;
}

