/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - GUI - abstract model
 *  Copyright (C) 2022 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022)
 *
 *  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
 */

#include <libcschem/config.h>

#include <string.h>
#include <genht/hash.h>
#include <genlist/gendlist.h>

#include <librnd/core/event.h>
#include <librnd/core/actions.h>
#include <librnd/core/hidlib.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/hid/hid_dad.h>
#include <librnd/hid/hid_dad_tree.h>

#include <libcschem/actions_csch.h>

#include "abst_attr.h"

void aattr_dlg_ahist2dlg(abst_attrdlg_ctx_t *actx, csch_ahdr_t *aobj)
{
	rnd_hid_attribute_t *aattr = &(*actx->dlg)[actx->waattrs];
	rnd_hid_attribute_t *hattr = &(*actx->dlg)[actx->wahistory];
	rnd_hid_tree_t *htree = hattr->wdata;
	rnd_hid_row_t *r, *bestr = NULL;
	const char *aattr_name = NULL;
	long best = 65537;
	const char *export_name = NULL;
	rnd_hid_attr_val_t hv;

	/* get abstract attr selected */
	r = rnd_dad_tree_get_selected(aattr);
	if (r != NULL)
		aattr_name = r->cell[0];

	/* remove existing items */
	rnd_dad_tree_clear(htree);

	if (aattr_name != NULL) {
		if (aobj != NULL) {
			csch_attrib_t *a = csch_attrib_get(&aobj->attr, aattr_name);
			long n, v;
			char *cell[2];

			export_name = a->export_name; /* side effect: display export name later */

			cell[1] = NULL;
			for(n = 0; n < a->source.used; n++) {
				cell[0] = rnd_strdup(a->source.array[n]);
				r = rnd_dad_tree_append(hattr, NULL, cell);
				v = strtol(cell[0], NULL, 0);
				if (v < best) {
					best = v;
					bestr = r;
				}
			}
		}
	}

	if (bestr != NULL) {
		hv.str = bestr->path;
		rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->wahistory, &hv);
	}

	/* side effect: display export name */
	hv.str = (export_name == NULL) ? "" : export_name;
	rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->wexport_name, &hv);
}

static void aattr_dlg_sheet2dlg_abst_net(abst_attrdlg_ctx_t *actx, csch_anet_t *anet)
{
	rnd_hid_attribute_t *attr = &((*actx->dlg)[actx->wdetailtree]);
	rnd_hid_tree_t *tree = attr->wdata;
	char *cell[2];
	gds_t tmp = {0};
	long n;

	/* remove existing items */
	rnd_dad_tree_clear(tree);

	cell[1] = NULL;
	for(n = 0; n < anet->conns.used; n++) {
		csch_aport_t *p = anet->conns.array[n];

		if (p->hdr.type == CSCH_ATYPE_PORT) {
			csch_acomp_t *c = p->parent;

			if (c == NULL) {
				const char *remark = NULL;
				tmp.used = 0;

				if (p->referer != NULL) { /* hierarchic: this is a sheet port on a subsheet instance */
					gds_append(&tmp, '(');
					gds_append_str(&tmp, p->referer->name);
					gds_append(&tmp, '-');
					remark = ") subsheet port";
				}
				else {
					gds_append_str(&tmp, "?????");
					gds_append(&tmp, '-');
				}
				gds_append_str(&tmp, p->name);
				if (remark != NULL)
					gds_append_str(&tmp, remark);
				cell[0] = rnd_strdup(tmp.array);
			}
			else if (c->hdr.type == CSCH_ATYPE_COMP) {
				tmp.used = 0;
				if (c->hdr.omit)
					gds_append(&tmp, '(');
				gds_append_str(&tmp, c->name);
				gds_append(&tmp, '-');
				gds_append_str(&tmp, p->name);
				if (c->hdr.omit)
					gds_append_str(&tmp, ") omitted");
				cell[0] = rnd_strdup(tmp.array);
			}
			else
				cell[0] = rnd_strdup(p->name);

			rnd_dad_tree_append(attr, NULL, cell);
		}
	}

	gds_uninit(&tmp);
}

void aattr_dlg_sheet2dlg_abstract(abst_attrdlg_ctx_t *actx, csch_ahdr_t *a)
{
	rnd_hid_attr_val_t hv;
	rnd_hid_attribute_t *attr = &((*actx->dlg)[actx->waattrs]);
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	char *cursor_path = NULL;
	char name_tmp[256];

	actx->last_ahdr = a;

	/* remember cursor */
	r = rnd_dad_tree_get_selected(attr);
	if (r != NULL)
		cursor_path = rnd_strdup(r->cell[0]);

	/* remove existing items */
	rnd_dad_tree_clear(tree);

	if (a != NULL) {
		char *cell[3];
		vtp0_t tmp = {0};
		long n;

		/* add all items recursively */
		csch_attrib_sort(&tmp, &a->attr);

		for(n = 0; n < tmp.used; n++) {
			csch_attrib_t *a = tmp.array[n];
			cell[0] = rnd_strdup(a->key);
			if (a->val != NULL)
				cell[1] = rnd_strdup(a->val);
			else
				cell[1] = rnd_strdup("<array>");
			cell[2] = NULL;
			r = rnd_dad_tree_append(attr, NULL, cell);
		}
		vtp0_uninit(&tmp);
		hv.str = name_tmp;
		switch(a->type) {
			case CSCH_ATYPE_NET:     rnd_snprintf(name_tmp, sizeof(name_tmp), "%s (net)", ((csch_anet_t *)a)->name); break;
			case CSCH_ATYPE_PORT:    rnd_snprintf(name_tmp, sizeof(name_tmp), "%s (port)", ((csch_aport_t *)a)->name); break;
			case CSCH_ATYPE_COMP:    rnd_snprintf(name_tmp, sizeof(name_tmp), "%s (component)", ((csch_acomp_t *)a)->name); break;
			case CSCH_ATYPE_BUSNET:
			case CSCH_ATYPE_BUSCHAN:
			case CSCH_ATYPE_BUSPORT:
			case CSCH_ATYPE_HUB:     hv.str = "other"; break; /* these are not yet supported */
			case CSCH_ATYPE_INVALID: hv.str = "<invalid type>"; break;
		}
	}
	else {
		hv.str = "<none>";
	}
	rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->waname, &hv);

	/* restore cursor */
	if (cursor_path != NULL) {
		hv.str = cursor_path;
		rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->waattrs, &hv);
		free(cursor_path);
		aattr_dlg_ahist2dlg(actx, a);
	}

	if (a == NULL)
		goto hide_all;

	switch(a->type) {
		case CSCH_ATYPE_NET:
			hv.str = "Ports connected:";
			rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->wdetaillab, &hv);
			rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetaillab, 0);
			rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetailtree, 0);
			aattr_dlg_sheet2dlg_abst_net(actx, (csch_anet_t *)a);
			break;
		case CSCH_ATYPE_PORT:
			{
				gds_t tmp = {0};
				csch_aport_t *p = (csch_aport_t *)a;

				gds_append_str(&tmp, "Connected to:\n");
				if (p->conn.net != NULL) {
					gds_append_str(&tmp, "net ");
					gds_append_str(&tmp, p->conn.net->name);
				}
				else
					gds_append_str(&tmp, "<nothing>");

				hv.str = tmp.array;
				rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->wdetaillab, &hv);
				rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetaillab, 0);
				gds_uninit(&tmp);
				goto hide_all_but_lab;
			}
		default:
			hide_all:;
			rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetaillab, 1);
			hide_all_but_lab:;
			rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetailtree, 1);
			break;
	}
}

void aattr_sources(abst_attrdlg_ctx_t *actx)
{
	if (actx->last_ahdr != NULL) {
		fgw_arg_t args[4], ares;
		rnd_design_t *hl;
		csch_sheet_t *sheet = actx->prj->hdr.designs.array[0];

		hl = &sheet->hidlib;
		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, &actx->last_ahdr->srcs);
		rnd_actionv_bin(hl, "TreeDialog", &ares, 3, args);
		fgw_ptr_unreg(&rnd_fgw, &args[2], CSCH_PTR_DOMAIN_COBJ_ARR);
	}
}

void aattr_attr_src(abst_attrdlg_ctx_t *actx)
{
	csch_sheet_t *sheet = actx->prj->hdr.designs.array[0];
	rnd_design_t *hl= &sheet->hidlib;
	fgw_arg_t args[4], ares;
	rnd_hid_attribute_t *hattr = &(*actx->dlg)[actx->wahistory];
	rnd_hid_row_t *r = rnd_dad_tree_get_selected(hattr);
	csch_chdr_t *cobj;
	csch_ahdr_t *aobj;
	char *attr;
	const char *desc;

	if (r == NULL)
		return;
	rnd_trace("History button on: %s\n", r->cell[0]);

	if (csch_attrib_src_parse(sheet, r->cell[0], NULL, NULL, &cobj, &aobj, &attr, &desc) == 0) {
		if (cobj != NULL) {
			gds_t tmp = {0};
			csch_oidpath_t oidp = {0};

			gds_append_str(&tmp, "object:");
			csch_oidpath_from_obj(&oidp, cobj);
			csch_oidpath_to_str_append(&tmp, &oidp);
			csch_oidpath_free(&oidp);

			args[1].type = FGW_STR | FGW_DYN; args[1].val.str = tmp.array;
			args[2].type = FGW_STR; args[2].val.cstr = attr;
			rnd_actionv_bin(hl, "AttributeDialog", &ares, 3, args);
			fgw_arg_free(&rnd_fgw, &ares);
		}
		else if (aobj != NULL) {
			args[1].type = FGW_LONG; args[1].val.nat_long = aobj->aid;
			args[2].type = FGW_STR; args[2].val.cstr = attr;
			rnd_actionv_bin(hl, "AbstractDialog", &ares, 3, args);
			fgw_arg_free(&rnd_fgw, &ares);
		}
	}
	free(attr);
}

void aattr_dlg_select_attr(abst_attrdlg_ctx_t *actx, const char *attr_name)
{
	rnd_hid_attr_val_t hv;
	hv.str = attr_name;
	rnd_gui->attr_dlg_set_value(*actx->dlg_hid_ctx, actx->waattrs, &hv);
	aattr_dlg_ahist2dlg(actx, actx->last_ahdr);
}

void aattr_dlg_init(abst_attrdlg_ctx_t *actx)
{
	rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetailtree, 1);
	rnd_gui->attr_dlg_widget_hide(*actx->dlg_hid_ctx, actx->wdetaillab, 1);
}

