/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - high level sim, GUI
 *  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
 */

#include <ctype.h>
#include <math.h>
#include <genlist/gendlist.h>
#include <genvector/vtd0.h>

#include <librnd/core/compat_fs.h>
#include <librnd/hid/hid_dad.h>
#include <librnd/hid/hid_dad_tree.h>
#include <liblihata/dom.h>
#include <liblihata/tree.h>

#include <libcschem/engine.h>
#include <plugins/sim/sim_conf.h>
#include <plugins/sim/actions.h>
#include <plugins/sim/util.h>
#include <plugins/sch_dialogs/dlg_stance.h>

#include "sim_gui_conf.h"

#define MAX_ANALS 16

/* Y height of the plot preview widget */
#define PLOT_PRV_Y sch_sim_gui_conf.plugins.sim_gui.plot_height_px

typedef struct sim_setup_dlg_ctx_s {
	RND_DAD_DECL_NOINIT(dlg)
	csch_project_t *prj;
	char *name;
	int wview;

	/* TAB: test bench & mods cfg */
	int wtest_bench, womit_no_test_bench, wmods_tree;

	/* TAB: output config */
	int wanals_tree;

	/* TAB: run & output cfg */
	struct {
		int wbox, wname, wreadout, wplot, wplot_ctrl, wplot_reset, wplot_yzoom, wtext;
		plot_preview_t pprv;
		int x_scale_type; /* see sch_siman_x_axis_log[] */
		vtd0_t xval;      /* all x values converted to double */
		
		unsigned has_data:1; /* whether pprv is loaded with data */
	} out[MAX_ANALS];

	FILE *fc; /* plot cache file */
	char *fc_name;
	unsigned subsequent_plot:1;  /* set to 1 after the first plot */

	vts0_t view_names;
	gds_t readout_tmp;
	int readout_idx;

	gdl_elem_t link;
} sim_setup_dlg_ctx_t;

static gdl_list_t dlgs;

static void plot_reset(sim_setup_dlg_ctx_t *ctx)
{
	int n;
	for(n = 0; n < MAX_ANALS; n++) {
		if (ctx->out[n].has_data) {
			plot_data_uninit(&ctx->out[n].pprv.pdata);
			ctx->out[n].has_data = 0;
		}
		ctx->out[n].xval.used = 0;
	}

	if (ctx->fc_name != NULL) {
		rnd_tempfile_unlink(ctx->fc_name);
		ctx->fc_name = NULL;
	}
	if (ctx->fc != NULL) {
		fclose(ctx->fc);
		ctx->fc = NULL;
	}
}

static void sim_setup_dlg_close_cb(void *caller_data, rnd_hid_attr_ev_t ev)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	int n;

	for(n = 0; n < ctx->view_names.used; n++)
		free(ctx->view_names.array[n]);
	vts0_uninit(&ctx->view_names);

	if (ctx->link.parent != NULL)
		gdl_remove(&dlgs, ctx, link);
	free(ctx->name);

	plot_reset(ctx);
	for(n = 0; n < MAX_ANALS; n++)
		vtd0_uninit(&ctx->out[n].xval);

	gds_uninit(&ctx->readout_tmp);

	free(ctx);
}

static lht_node_t *get_path(lht_node_t *parent, const char *path)
{
	lht_err_t err;
	return lht_tree_path_(parent->doc, parent, path, 1, 1, &err);
}

static const char *get_path_str(lht_node_t *parent, const char *path, const char *defval, lht_node_t **nout)
{
	const char *s;
	lht_err_t err;
	lht_node_t *nd = lht_tree_path_(parent->doc, parent, path, 1, 1, &err);

	if ((nd == NULL) || (nd->type != LHT_TEXT)) {
		if (nout != NULL)
			*nout = NULL;
		return defval;
	}

	s = nd->data.text.value;
	while(isspace(*s)) s++;
	if (*s == '\n') {
		if (nout != NULL)
			*nout = NULL;
		return defval;
	}

	if (nout != NULL)
		*nout = nd;
	return s;
}

static void sch2dlg_test_bench_mods(sim_setup_dlg_ctx_t *ctx, lht_node_t *nsetup, gds_t *tmp)
{
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wmods_tree];
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	char *cursor_path = NULL, *cell[4];
	rnd_hid_attr_val_t hv;
	lht_node_t *ntb, *nmods;
	const char *whole_circ = "<whole circuit>";
	const char *ov;
	int omit_no_test_bench;
	int sim_whole_circ = 0;

	ov = get_path_str(nsetup, "omit_no_test_bench", whole_circ, &ntb);
	omit_no_test_bench = rnd_istrue(ov);

	hv.str = get_path_str(nsetup, "test_bench", whole_circ, &ntb);
	/* special case: explicity set to empty */
	if (*hv.str == '\0') {
		omit_no_test_bench = 0;
		hv.str = whole_circ;
		sim_whole_circ = 1;
	}

	rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wtest_bench, &hv);

	hv.lng = omit_no_test_bench;
	rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->womit_no_test_bench, &hv);

	/* disable checkbox if whole circ */
	hv.lng = !sim_whole_circ;
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->womit_no_test_bench, hv.lng);

	/*** update the mods list ***/
	nmods = get_path(nsetup, "mods");

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

	/* remove existing items and fill up with the new */
	rnd_dad_tree_clear(tree);

	/* fill in the table */
	cell[3] = NULL;
	if ((nmods != NULL) && (nmods->type == LHT_LIST)) {
		lht_node_t *nmod;
		long idx;
		for(idx = 0, nmod = nmods->data.list.first; nmod != NULL; idx++, nmod = nmod->next) {
			cell[0] = rnd_strdup_printf("%ld", idx);
			cell[1] = rnd_strdup(nmod->name);
			if (nmod->type == LHT_HASH) {
				tmp->used = 0;
				sch_sim_append_print_mod(tmp, nmod, "  ");
				cell[2] = tmp->array;
				tmp->used = tmp->alloced = 0;
				tmp->array = NULL;
			}
			else
				cell[2] = rnd_strdup("<invalid node type>");
			rnd_dad_tree_append(attr, NULL, cell);
		}
	}

	/* restore cursor (will work only in non-modal upon project update from elsewhere) */
	if (cursor_path != NULL) {
		rnd_hid_attr_val_t hv;
		hv.str = cursor_path;
		rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wmods_tree, &hv);
		free(cursor_path);
	}
}

static void dlg2sch_test_bench_mods(sim_setup_dlg_ctx_t *ctx, lht_node_t *nsetup)
{

}

static void sch2dlg_anals_plot(sim_setup_dlg_ctx_t *ctx, lht_node_t *nsetup, gds_t *tmp)
{
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wanals_tree];
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	char *cursor_path = NULL, *cell[4];
	lht_node_t *noutput;

	noutput = get_path(nsetup, "output");

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

	/* remove existing items and fill up with the new */
	rnd_dad_tree_clear(tree);

	/* fill in the table */
	cell[3] = NULL;
	if ((noutput != NULL) && (noutput->type == LHT_LIST)) {
		lht_node_t *nout;
		for(nout = noutput->data.list.first; nout != NULL; nout = nout->next) {
			cell[0] = rnd_strdup(nout->name);
			cell[1] = rnd_strdup(get_path_str(nout, "analysis/type", "N/A", NULL));
			cell[2] = rnd_strdup(get_path_str(nout, "presentation/type", "N/A", NULL));
			rnd_dad_tree_append(attr, NULL, cell);
		}
	}


	/* restore cursor (will work only in non-modal upon project update from elsewhere) */
	if (cursor_path != NULL) {
		rnd_hid_attr_val_t hv;
		hv.str = cursor_path;
		rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wanals_tree, &hv);
		free(cursor_path);
	}
}

static void dlg2sch_anals_plot(sim_setup_dlg_ctx_t *ctx, lht_node_t *nsetup)
{
	/* nothing to do here, the list in the conf is updated by the GUI code */
}

static void sch_sim_setup_sch2dlg(sim_setup_dlg_ctx_t *ctx)
{
	lht_node_t *nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 0);
	gds_t tmp = {0};

	if (nsetup == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't update sim setup dialog: setup node not found in config\n");
		return;
	}

	sch2dlg_test_bench_mods(ctx, nsetup, &tmp);
	sch2dlg_anals_plot(ctx, nsetup, &tmp);
/*	sch2dlg_run_out(ctx, nsetup, &tmp); - done once after run */

	gds_uninit(&tmp);
}

static void sch_sim_setup_dlg2sch(sim_setup_dlg_ctx_t *ctx, int tab1, int tab2, int tab3)
{
	lht_node_t *nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 0);

	if (nsetup == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't update the project file from the setup dialog:\nsetup node not found in config\n");
		return;
	}

	if (tab1) dlg2sch_test_bench_mods(ctx, nsetup);
	if (tab2) dlg2sch_anals_plot(ctx, nsetup);
/*	if (tab3) dlg2sch_run_out(ctx, nsetup); - done once after run */
}

static void setup1_save_conf_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr_btn)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	sch_sim_setup_dlg2sch(ctx, 1, 0, 0);
}

static void setup_test_bench_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr_btn)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	const char *initval;
	char *val = NULL;
	sch_stance_edit_res_t r;
	lht_node_t *nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 0);

	if (nsetup == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't find simulation setup '%s'\n", ctx->name);
		return;
	}

	initval = get_path_str(nsetup, "test_bench", "", NULL);
	r = sch_stance_edit_dlg("test_bench", initval, &val);
	if ((r == STE_CANCEL) || (val == NULL))
		return;

	if (r & STE_REMEMBER) csch_stance_add_to_values("test_bench", val);
	if (r & STE_SET) {
		lht_node_t *nd;
		const char *oldval;
		oldval = get_path_str(nsetup, "test_bench", "<whole circuit>", &nd);
		if (nd == NULL) {
			nd = lht_dom_node_alloc(LHT_TEXT, "test_bench");
			lht_dom_hash_put(nsetup, nd);
		}
		if (strcmp(val, oldval) != 0) {
			free(nd->data.text.value);
			nd->data.text.value = val; /* pass on ownership... */
			val = NULL; /* ... don't free here */
			sch_sim_flush_prj_file(ctx->prj);
			sch_sim_setup_sch2dlg(ctx);
		}
	}

	free(val);
}

static void setup_omit_no_test_bench_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr_btn)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	lht_node_t *nd;
	const char *soldval;
	int newval, oldval;
	lht_node_t *nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 0);

	newval = ctx->dlg[ctx->womit_no_test_bench].val.lng;

	soldval = get_path_str(nsetup, "omit_no_test_bench", "0", &nd);
	if (nd == NULL) {
		nd = lht_dom_node_alloc(LHT_TEXT, "omit_no_test_bench");
		lht_dom_hash_put(nsetup, nd);
	}
	oldval = rnd_istrue(soldval);

	if (oldval != newval) {
		free(nd->data.text.value);
		nd->data.text.value = rnd_strdup(newval ? "1" : "0");
		sch_sim_flush_prj_file(ctx->prj);
		sch_sim_setup_sch2dlg(ctx);
	}
}


#include "sim_mod_dlg.c"

/* Return the mod node corresponding to the the given tree table row, counting
   both rows and conf nodes */
lht_node_t *setup_mod_get_node(sim_setup_dlg_ctx_t *ctx, rnd_hid_row_t *row)
{
	lht_node_t *nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 0);
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wmods_tree];
	rnd_hid_tree_t *tree = tattr->wdata;
	rnd_hid_row_t *r;
	lht_node_t *nmods, *n;

	if (nsetup == NULL)
		return NULL;

	nmods = get_path(nsetup, "mods");
	if (nmods == NULL)
		return NULL;

	for(n = nmods->data.list.first, r = gdl_first(&tree->rows); n != NULL; n = n->next, r = gdl_next(&tree->rows, r))
		if (r == row)
			return n;

	return NULL;
}

static void setup_mod_edit_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wmods_tree];
	rnd_hid_row_t *row = rnd_dad_tree_get_selected(tattr);
	lht_node_t *nmod;

	if (row == NULL) {
		rnd_message(RND_MSG_ERROR, "Select a modification from the list first!\n");
		return;
	}

	nmod = setup_mod_get_node(ctx, row);
	if (nmod == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to find the conf node for the selected row\n");
		return;
	}

	dlg_mod_edit(nmod);
	sch_sim_flush_prj_file(ctx->prj);

	TODO("this should be automatic via conf change callback");
	sch_sim_setup_sch2dlg(ctx);
}

static void setup_mod_add_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	lht_node_t *nnew, *nmods, *nsetup;

	nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 1);
	if ((nsetup == NULL) || (nsetup->type != LHT_HASH)) {
		rnd_message(RND_MSG_ERROR, "Failed to get or create the setup node");
		return;
	}

	nmods = get_path(nsetup, "mods");
	if (nmods == NULL) {
		nmods = lht_dom_node_alloc(LHT_LIST, "mods");
		lht_dom_hash_put(nsetup, nmods);
	}
	
	if (nmods->type != LHT_LIST) {
		rnd_message(RND_MSG_ERROR, "Setup's mods is not a list");
		return;
	}

	nnew = lht_dom_node_alloc(LHT_HASH, "unknown");
	if (nnew == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to allocate hash node for the new mod");
		return;
	}
	lht_dom_list_append(nmods, nnew);

	dlg_mod_edit(nnew);
	sch_sim_flush_prj_file(ctx->prj);

	TODO("this should be automatic via conf change callback");
	sch_sim_setup_sch2dlg(ctx);
}

static void setup_mod_del_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wmods_tree];
	rnd_hid_row_t *row = rnd_dad_tree_get_selected(tattr);
	lht_node_t *nmod;

	if (row == NULL) {
		rnd_message(RND_MSG_ERROR, "Select a modification from the list first!\n");
		return;
	}

	nmod = setup_mod_get_node(ctx, row);
	if (nmod == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to find the conf node for the selected row\n");
		return;
	}

	lht_tree_del(nmod);
	sch_sim_flush_prj_file(ctx->prj);

	TODO("this should be automatic via conf change callback");
	sch_sim_setup_sch2dlg(ctx);
}

#include "sim_outcfg_dlg.c"

static void output_edit_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wanals_tree];
	rnd_hid_row_t *row = rnd_dad_tree_get_selected(tattr);

	if (row == NULL) {
		rnd_message(RND_MSG_ERROR, "Select an output from the list first!\n");
		return;
	}

	dlg_outcfg_edit(ctx->prj, ctx->name, row->cell[0]);

	TODO("this should be automatic via conf change callback");
	sch_sim_setup_sch2dlg(ctx);
}

static void output_add_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	rnd_design_t *dsg = ctx->prj->hdr.designs.array[0];
	char *an_name;
	lht_node_t *nnew, *nold, *noutput, *nsetup;

	nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 1);
	if ((nsetup == NULL) || (nsetup->type != LHT_HASH)) {
		rnd_message(RND_MSG_ERROR, "Failed to get or create the setup node");
		return;
	}

	noutput = get_path(nsetup, "output");
	if (noutput == NULL) {
		noutput = lht_dom_node_alloc(LHT_LIST, "output");
		lht_dom_hash_put(nsetup, noutput);
	}
	
	if (noutput->type != LHT_LIST) {
		rnd_message(RND_MSG_ERROR, "Setup's output is not a list");
		return;
	}

	an_name = rnd_hid_prompt_for(dsg, "Name for the new output", NULL, "Simulation output naming");
	if ((an_name == NULL) || (*an_name == '\0')) {
		free(an_name);
		return; /* cancel */
	}
	nold = sch_sim_get_output(ctx->prj, ctx->name, an_name, 0);
	if (nold != NULL) {
		rnd_message(RND_MSG_ERROR, "Setup's output of that name already exists\nPlease choose a different name\n");
		return;
	}


	nnew = lht_dom_node_alloc(LHT_HASH, an_name);
	if (nnew == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to allocate hash node for the new output");
		free(an_name);
		return;
	}
	lht_dom_list_append(noutput, nnew);

	dlg_outcfg_edit(ctx->prj, ctx->name, an_name);
	free(an_name);

	TODO("this should be automatic via conf change callback");
	sch_sim_setup_sch2dlg(ctx);
}

static void output_del_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wanals_tree];
	rnd_hid_row_t *row = rnd_dad_tree_get_selected(tattr);
	lht_node_t *nd;

	if (row == NULL) {
		rnd_message(RND_MSG_ERROR, "Select an output from the list first!\n");
		return;
	}

	nd = sch_sim_get_output(ctx->prj, ctx->name, row->cell[0], 0);
	if (nd == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to find that output\n");
		return;
	}

	lht_tree_del(nd);
	sch_sim_flush_prj_file(ctx->prj);

	TODO("this should be automatic via conf change callback");
	sch_sim_setup_sch2dlg(ctx);
}

static int sim_dlg_activate(sim_setup_dlg_ctx_t *ctx, int compile_now)
{
	return sch_sim_activate(ctx->prj, ctx->name, ctx->view_names.array[ctx->dlg[ctx->wview].val.lng], compile_now);
}

static void activate_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	sim_dlg_activate(ctx, 1);
}

static void run2out_reset(sim_setup_dlg_ctx_t *ctx)
{
	int n;

	for(n = 0; n < MAX_ANALS; n++)
		rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[n].wbox, 1);

	plot_reset(ctx);
}

static void gen_lin_labels(plot_alabel_t **dst_arr, long *dst_len, double min, double max)
{
	double v = min, step = (max-min) / 10;
	int n, num_pts;
	plot_alabel_t *arr;

	if (step > 1000) {
		step = floor(step/1000.0) * 1000;
		v = floor(v/1000.0) * 1000;
	}
	else if (step > 100) {
		step = floor(step/100.0) * 100;
		v = floor(v/100.0) * 100;
	}
	else if (step > 10) {
		step = floor(step/10.0) * 10;
		v = floor(v/10.0) * 10;
	}
	else if (step > 1) {
		step = floor(step);
		v = floor(v);
	}
	else if (step > 0.1) {
		step = floor(step*10)/10;
		v = floor(v*10)/10;
	}
	else if (step > 0.01) {
		step = floor(step*100)/100;
		v = floor(v*100)/100;
	}
	else if (step > 0.001) {
		step = floor(step*1000)/1000;
		v = floor(v*1000)/1000;
	}

	num_pts = floor((max-min) / step)+1;

	/* don't include 0, that's going to be indicated differently */
	if (v == 0) {
		v += step;
		num_pts--;
	}

	*dst_len = num_pts;
	*dst_arr = arr = malloc(sizeof(plot_alabel_t) * num_pts);

/*	rnd_trace("lin_labels: %f %f: num_pts %ld step: %f start: %f\n", min, max, num_pts, step, v);*/

	for(n = 0; n < num_pts; n++, v += step) {
		arr[n].plot_val = arr[n].print_val = v;
		arr[n].text = NULL;
	}
}

static const char *x_axis_name(sim_setup_dlg_ctx_t *ctx, sch_sim_setup_t *ssu, lht_node_t *npres, int idx)
{
	lht_node_t *nout = npres->parent, *nanalysis = get_path(nout, "analysis"), *ntype;
	sch_sim_analysis_type_t antype;

	ctx->out[idx].x_scale_type = 0;

	if ((nanalysis == NULL) || (nanalysis->type != LHT_HASH))
		return "ERROR: analysis is not a hash";

	ntype = get_path(nanalysis, "type");
	if ((ntype == NULL) || (ntype->type != LHT_TEXT))
		return "ERROR: analysis/type is not a text";

	antype = sch_sim_str2analysis_type(ntype->data.text.value);
	if (antype < 0)
		return "ERROR: invalid analysis/type";

	if ((antype == SCH_SIMAN_PREVIOUS) && (idx > 0)) {
		ctx->out[idx].x_scale_type = ctx->out[idx-1].x_scale_type;
		return ctx->out[idx-1].pprv.pdata.x_axis_name;
	}

	ctx->out[idx].x_scale_type = sch_siman_x_axis_log[antype];
	return sch_siman_x_axis_name[antype];
}

static void plot_autozoom(sim_setup_dlg_ctx_t *ctx, int idx)
{
	long mx = (ctx->out[idx].pprv.maxx)/20;
	double my = (ctx->out[idx].pprv.maxy - ctx->out[idx].pprv.miny) / 20;
	plot_zoomto(&ctx->dlg[ctx->out[idx].wplot], &ctx->out[idx].pprv, 0-mx, ctx->out[idx].pprv.miny-my, ctx->out[idx].pprv.maxx+mx, ctx->out[idx].pprv.maxy+my);
}

static void run2out_plot(sim_setup_dlg_ctx_t *ctx, sch_sim_setup_t *ssu, lht_node_t *npres, int idx)
{
	sch_sim_exec_t *se = sch_sim_get_sim_exec(ctx->prj, -1);
	rnd_design_t *dsg = ctx->prj->hdr.designs.array[0];
	lht_node_t *nd, *nout = npres->parent, *nprops;
	void *stream;
	vts0_t cols = {0};
	long num_props, num_rows, xm;
	double min_y = 0, max_y = 0;
	rnd_hid_attr_val_t hv;

	if (se == NULL) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': no sim exec\n", ctx->name, nout->name);
		return;
	}

	nprops = get_path(npres, "props");
	if ((nprops == NULL) || (nprops->type != LHT_LIST)) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': wrong or missing props subtree\n", ctx->name, nout->name);
		return;
	}

	stream = se->result_open(ctx->prj, ssu, idx);
	if (stream == NULL) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': can't open stream\n", ctx->name, nout->name);
		return;
	}

	/* count properties and rows*/
	for(nd = nprops->data.list.first, num_props = 0; nd != NULL; nd = nd->next, num_props++) ;

	num_rows = 0;
	while(se->result_read(ssu, stream, NULL) == 0)
		num_rows++;

	/* draw traces */
	if (num_rows > 0) {
		long n, xi;
		plot_trace_t *tr;
		plot_trdata_t *td[MAX_ANALS];
		plot_pos_t pw[MAX_ANALS];
		int pw_err[MAX_ANALS], xmark_skip;
		struct {
			double buff[256];
		} tmp[MAX_ANALS];

		plot_data_init(&ctx->out[idx].pprv.pdata, num_props);
		ctx->out[idx].has_data = 0;

		ctx->fc_name = rnd_tempfile_name_new("plot_cache");
		ctx->fc = rnd_fopen(dsg, ctx->fc_name, "w+");

		/* as a side effect this also fills in ctx->out[idx].x_scale_type */
		ctx->out[idx].pprv.pdata.x_axis_name = rnd_strdup(x_axis_name(ctx, ssu, npres, idx));
		ctx->out[idx].pprv.pdata.y_axis_name = rnd_strdup("value");
		ctx->out[idx].pprv.pdata.trace_name = malloc(sizeof(char *) * num_props);

		switch(ctx->out[idx].x_scale_type) {
			case 8:
				xmark_skip = 10;
				ctx->out[idx].pprv.type_x = PLAXTY_OCTAVE;
				break;
			case 10:
				xmark_skip = 10;
				ctx->out[idx].pprv.type_x = PLAXTY_DECADE;
				break;
			case 0:
			case -1:
			default:
				xmark_skip = 10;
				break;
		}

		ctx->out[idx].pprv.pdata.num_x_labels = xm = (num_rows/xmark_skip)+1;
		ctx->out[idx].pprv.pdata.x_labels = malloc(sizeof(plot_alabel_t) * xm);
		xm = xi = 0;

		for(n = 0, nd = nprops->data.list.first; n < num_props; n++, nd = nd->next) {
			tr = &ctx->out[idx].pprv.pdata.trace[n];
			plot_trace_init(tr, ctx->fc);
			td[n] = plot_trdata_alloc(tr, 0, num_rows);

			pw_err[n] = plot_write_init(&pw[n], tr, td[n], PLOT_MAIN, 0, 0, tmp[n].buff, sizeof(tmp[n].buff)/sizeof(tmp[n].buff[0]));

			ctx->out[idx].pprv.pdata.trace_name[n] = rnd_strdup(nd->data.text.value);
		}

		se->result_rewind(ssu, stream);
		while(se->result_read(ssu, stream, &cols) == 0) {
			if (cols.used != num_props + 1) {
				rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': broken output (number of cols)\n", ctx->name, nout->name);
				break;
			}
			
			/* read and store traces */
			for(n = 0; n < num_props; n++) {
				char *end;
				double val = strtod(cols.array[n], &end);
				if (*end != '\0') {
					rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': wrong numeric value '%s'\n", ctx->name, nout->name, cols.array[n]);
					val = 0;
				}
				if (pw_err[n] == 0) {
					plot_write(&pw[n], val);
					if (val > max_y) max_y = val;
					if (val < min_y) min_y = val;
				}
			}

			
			{
				char *end;
				double val = strtod(cols.array[num_props], &end);
				if ((xi > 0) && (xi % xmark_skip) == 0) {
					/* read and store every 10th x marker in the plot */
					assert(xm < ctx->out[idx].pprv.pdata.num_x_labels);
					ctx->out[idx].pprv.pdata.x_labels[xm].plot_val = xi;
					ctx->out[idx].pprv.pdata.x_labels[xm].print_val = val;
					xm++;
				}
				vtd0_append(&ctx->out[idx].xval, val);
			}
			xi++;
		}

		for(n = 0; n < num_props; n++)
			plot_flush(&pw[n]);
	}

	ctx->out[idx].pprv.pdata.num_x_labels = xm; /* off-by-one: xm may be one less */

	if (!ctx->subsequent_plot) {
		/* set only on first plot, preserve for updates so user choice is not overridden */
		ctx->out[idx].pprv.zoom_y = (double)PLOT_PRV_Y / (max_y - min_y);
	}

	ctx->out[idx].pprv.miny = min_y;
	ctx->out[idx].pprv.maxy = max_y;
	ctx->out[idx].pprv.maxx = num_rows;

	if (!ctx->subsequent_plot) {
		/* set only on first plot, preserve for updates so user choice is not overridden */
		hv.dbl = ctx->out[idx].pprv.zoom_y;
		rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->out[idx].wplot_yzoom, &hv);
	}

	/* generate y labels */
	gen_lin_labels(&ctx->out[idx].pprv.pdata.y_labels, &ctx->out[idx].pprv.pdata.num_y_labels, min_y, max_y);

	ctx->out[idx].pprv.grid_color = &sch_sim_gui_conf.plugins.sim_gui.plot_grid_color;
	if (!ctx->subsequent_plot) {
		/* first plot: auto-zoom */
		plot_autozoom(ctx, idx);
	}

	se->result_close(ssu, stream);
	vts0_uninit(&cols);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wplot, 0);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wplot_ctrl, 0);
}

static void run2out_print(sim_setup_dlg_ctx_t *ctx, sch_sim_setup_t *ssu, lht_node_t *npres, int idx)
{
	sch_sim_exec_t *se = sch_sim_get_sim_exec(ctx->prj, -1);
	lht_node_t *nd, *nout = npres->parent, *nprops;
	rnd_hid_attribute_t *atxt = &ctx->dlg[ctx->out[idx].wtext];
	rnd_hid_text_t *txt = atxt->wdata;
	vts0_t cols = {0};
	gds_t tmp = {0};
	void *stream;
	int n;

	if (se == NULL) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': no sim exec\n", ctx->name, nout->name);
		return;
	}

	nprops = get_path(npres, "props");
	if ((nprops == NULL) || (nprops->type != LHT_LIST)) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': wrong or missing props subtree\n", ctx->name, nout->name);
		return;
	}

	/* expect only a single row of data */
	stream = se->result_open(ctx->prj, ssu, idx);
	if (stream == NULL) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s': can't open stream\n", ctx->name, nout->name);
		return;
	}
	se->result_read(ssu, stream, &cols);
	se->result_close(ssu, stream);


	for(nd = nprops->data.list.first, n = 0; nd != NULL; nd = nd->next, n++) {
		if (nd->type == LHT_TEXT) {
			gds_append_str(&tmp, nd->data.text.value);
			gds_append_str(&tmp, " = ");
			if (n < cols.used)
				gds_append_str(&tmp, cols.array[n]);
			gds_append(&tmp, '\n');
		}
	}

	txt->hid_set_text(atxt, ctx->dlg_hid_ctx, RND_HID_TEXT_REPLACE, tmp.array);

	vts0_uninit(&cols);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wtext, 0);
}


static void run2out(sim_setup_dlg_ctx_t *ctx, sch_sim_setup_t *ssu, lht_node_t *nout, int idx)
{
	lht_node_t *ntype, *npres = get_path(nout, "presentation");
	rnd_hid_attr_val_t hv;

	if (idx >= MAX_ANALS)
		return;

	if ((npres == NULL) || (npres->type != LHT_HASH)) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s' has missing or invalid presentation subtree\n", ctx->name, nout->name);
		return;
	}

	ntype = get_path(npres, "type");
	if ((ntype == NULL) || (ntype->type != LHT_TEXT)) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s' invalid presentation type (empty or missing)\n", ctx->name, nout->name);
		return;
	}

	hv.str = nout->name;
	rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->out[idx].wname, &hv);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wbox, 0);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wplot, 1);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wplot_ctrl, 1);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->out[idx].wtext, 1);


	switch(sch_sim_str2presentation_type(ntype->data.text.value)) {
		case SCH_SIMPRES_PLOT:
			run2out_plot(ctx, ssu, npres, idx);
			break;

		case SCH_SIMPRES_PRINT:
			run2out_print(ctx, ssu, npres, idx);
			break;

		case SCH_SIMPRES_invalid:
			rnd_message(RND_MSG_ERROR, "sim_dlg_run(): simulation setup '%s' output '%s' invalid presentation type (%s)\n", ctx->name, nout->name, ntype->data.text.value);
			break;
	}
}

static void run_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sch_sim_setup_t *ssu;
	sim_setup_dlg_ctx_t *ctx = caller_data;
	lht_node_t *no, *noutput, *nsetup = sch_sim_get_setup(ctx->prj, ctx->name, 0);
	int idx;

	run2out_reset(ctx);

	if (nsetup == NULL) {
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): failed to find simulation setup '%s'\n", ctx->name);
		return;
	}

	sim_dlg_activate(ctx, 1);
	ssu = sch_sim_run_prepare(ctx->prj, ctx->name);
	if (ssu == NULL)
		return;

	if (sch_sim_exec(ctx->prj, ssu) != 0)
		rnd_message(RND_MSG_ERROR, "sim_dlg_run(): failed to execute sim setup '%s'\n", ctx->name);


	/* load output into the dialog box */
	noutput = get_path(nsetup, "output");
	if ((noutput != NULL) && (noutput->type == LHT_LIST)) {
		for(no = noutput->data.list.first, idx = 0; no != NULL; no = no->next, idx++)
			run2out(ctx, ssu, no, idx);
		ctx->subsequent_plot = 1;
	}

	sch_sim_free(ctx->prj, ssu);
}

static void reset_zoom_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	int idx;

	for(idx = 0; idx < MAX_ANALS; idx++) {
		if (attr == &ctx->dlg[ctx->out[idx].wplot_reset]) {
			plot_autozoom(ctx, idx);
			return;
		}
	}
}

static void yzoom_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	sim_setup_dlg_ctx_t *ctx = caller_data;
	int idx;

	for(idx = 0; idx < MAX_ANALS; idx++) {
		if (attr == &ctx->dlg[ctx->out[idx].wplot_yzoom]) {
			ctx->out[idx].pprv.zoom_y = ctx->dlg[ctx->out[idx].wplot_yzoom].val.dbl;
			plot_redraw(&ctx->dlg[ctx->out[idx].wplot]);
			return;
		}
	}
}

static void readout_plot_begin_cb(plot_preview_t *pprv, long xi)
{
	sim_setup_dlg_ctx_t *ctx = pprv->user_data;
	int n, out_idx = -1;

	ctx->readout_tmp.used = 0;

	for(n = 0; n < MAX_ANALS; n++) {
		if (&ctx->out[n].pprv == pprv) {
			out_idx = n;
			break;
		}
	}
	ctx->readout_idx = out_idx;

	if ((out_idx >= 0) && (xi >= 0) && (xi < ctx->out[out_idx].xval.used)) {
		double x = ctx->out[out_idx].xval.array[xi];
		rnd_append_printf(&ctx->readout_tmp, "  x=%f", x);
	}

}

static void readout_plot_end_cb(plot_preview_t *pprv, long xi)
{
	sim_setup_dlg_ctx_t *ctx = pprv->user_data;

	if (ctx->readout_idx >= 0) {
		rnd_hid_attr_val_t hv;
		hv.str = ctx->readout_tmp.array;
		rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->out[ctx->readout_idx].wreadout, &hv);
	}

	ctx->readout_tmp.used = 0;
}

static void readout_plot_cb(plot_preview_t *pprv, int trace_idx, long xi, double y)
{
	sim_setup_dlg_ctx_t *ctx = pprv->user_data;

	rnd_append_printf(&ctx->readout_tmp, " %s=%f", pprv->pdata.trace_name[trace_idx], y);
}


static void list_sim_views(csch_project_t *prj, vts0_t *dst)
{
	long n, m;

	for(n = 0; n < prj->views.used; n++) {
		csch_view_t *v = prj->views.array[n];
		const char *view_name = v->fgw_ctx.name;
		for(m = 0; m < v->engines.used; m++) {
			csch_view_eng_t *ve = v->engines.array[m];
			if (fgw_func_lookup_in(ve->obj, "sim_exec_get") != 0) {
				vts0_append(dst, rnd_strdup(view_name));
				break; /* no need to check the rest of the engines in this view */
			}
		}
	}
}

static void sim_setup_dlg(csch_project_t *prj, const char *name)
{
	sim_setup_dlg_ctx_t *ctx;
	rnd_hid_dad_buttons_t clbtn[] = {{"Close", 0}, {NULL, 0}};
	char *title;
	int n;
	const char *tabs[] = {"test bench & mods cfg", "output config", "run & output", NULL};
	const char *test_bench_help = "test_bench setting is in attribute forge-if/test_bench\nsee also: File menu, project, project stances -> test_bench stance\nsee also: Select menu, change selected objects, testbench affiliation menu";
	const char *mods_tbl_hdr[] = {"#", "mod", "arguments", NULL};
	const char *anals_tbl_hdr[] = {"name", "analysis", "presentation", NULL};
	static rnd_box_t prvbb = {0, 0, 200*200, 200*200};

	/* skip if already open */
	for(ctx = gdl_first(&dlgs); ctx != NULL; ctx = gdl_next(&dlgs, ctx))
		if ((ctx->prj == prj) && (strcmp(ctx->name, name) == 0))
			return;

	ctx = calloc(sizeof(sim_setup_dlg_ctx_t), 1);
	ctx->prj = prj;
	ctx->name = rnd_strdup(name);
	gdl_append(&dlgs, ctx, link);
	list_sim_views(prj, &ctx->view_names);

	RND_DAD_BEGIN_VBOX(ctx->dlg);
		RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);

		RND_DAD_BEGIN_TABBED(ctx->dlg, tabs);
			RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* TAB: test bench & mods cfg */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_BUTTON(ctx->dlg, "        ");
						RND_DAD_HELP(ctx->dlg, test_bench_help);
						ctx->wtest_bench = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, setup_test_bench_cb);
					RND_DAD_LABEL(ctx->dlg, "Test bench to use");
						RND_DAD_HELP(ctx->dlg, test_bench_help);
				RND_DAD_END(ctx->dlg);
				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_BOOL(ctx->dlg);
						RND_DAD_HELP(ctx->dlg, test_bench_help);
						ctx->womit_no_test_bench = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, setup_omit_no_test_bench_cb);
					RND_DAD_LABEL(ctx->dlg, "Omit symbols that do not have test_bench setting");
						RND_DAD_HELP(ctx->dlg, test_bench_help);
				RND_DAD_END(ctx->dlg);

				RND_DAD_TREE(ctx->dlg, 3, 0, mods_tbl_hdr); /* mods */
					RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL | RND_HATF_SCROLL);
					ctx->wmods_tree = RND_DAD_CURRENT(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_BUTTON(ctx->dlg, "Edit...");
						RND_DAD_HELP(ctx->dlg, "Edit selected modification");
						RND_DAD_CHANGE_CB(ctx->dlg, setup_mod_edit_cb);
					RND_DAD_BUTTON(ctx->dlg, "Add...");
						RND_DAD_HELP(ctx->dlg, "Create a new modification");
						RND_DAD_CHANGE_CB(ctx->dlg, setup_mod_add_cb);
					RND_DAD_BUTTON(ctx->dlg, "Remove");
						RND_DAD_HELP(ctx->dlg, "Remove selected modification");
						RND_DAD_CHANGE_CB(ctx->dlg, setup_mod_del_cb);
				RND_DAD_END(ctx->dlg);

			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* TAB: output config */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
				RND_DAD_TREE(ctx->dlg, 3, 0, anals_tbl_hdr);
					ctx->wanals_tree = RND_DAD_CURRENT(ctx->dlg);
					RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL | RND_HATF_SCROLL);
				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_BUTTON(ctx->dlg, "Edit...");
						RND_DAD_CHANGE_CB(ctx->dlg, output_edit_cb);
						RND_DAD_HELP(ctx->dlg, "Edit selected output");
					RND_DAD_BUTTON(ctx->dlg, "Add...");
						RND_DAD_CHANGE_CB(ctx->dlg, output_add_cb);
						RND_DAD_HELP(ctx->dlg, "Create a new output");
					RND_DAD_BUTTON(ctx->dlg, "Remove");
						RND_DAD_CHANGE_CB(ctx->dlg, output_del_cb);
						RND_DAD_HELP(ctx->dlg, "Remove selected output");
				RND_DAD_END(ctx->dlg);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* TAB: run & output cfg */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL | RND_HATF_SCROLL);
				for(n = 0; n < MAX_ANALS; n++) {
					RND_DAD_BEGIN_VBOX(ctx->dlg);
						ctx->out[n].wbox = RND_DAD_CURRENT(ctx->dlg);
						if (n != 0)
							RND_DAD_LABEL(ctx->dlg, "\n\n");
						RND_DAD_BEGIN_HBOX(ctx->dlg);
							RND_DAD_LABEL(ctx->dlg, "<name of the analysis>");
								ctx->out[n].wname = RND_DAD_CURRENT(ctx->dlg);
							RND_DAD_LABEL(ctx->dlg, "");
								ctx->out[n].wreadout = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_END(ctx->dlg);
						RND_DAD_TEXT(ctx->dlg, NULL);
							ctx->out[n].wtext = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_PREVIEW(ctx->dlg, plot_preview_expose_cb, plot_mouse_cb, NULL, NULL, &prvbb, 200, PLOT_PRV_Y, &ctx->out[n].pprv);
							ctx->out[n].wplot = RND_DAD_CURRENT(ctx->dlg);
							ctx->out[n].pprv.readout_cb = readout_plot_cb;
							ctx->out[n].pprv.readout_begin_cb = readout_plot_begin_cb;
							ctx->out[n].pprv.readout_end_cb = readout_plot_end_cb;
							ctx->out[n].pprv.user_data = ctx;
							ctx->out[n].pprv.widget_idx = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_BEGIN_HBOX(ctx->dlg);
							ctx->out[n].wplot_ctrl = RND_DAD_CURRENT(ctx->dlg);
/*							RND_DAD_BUTTON(ctx->dlg, "view");*/
							RND_DAD_BUTTON(ctx->dlg, "reset zoom");
								ctx->out[n].wplot_reset = RND_DAD_CURRENT(ctx->dlg);
								RND_DAD_CHANGE_CB(ctx->dlg, reset_zoom_cb);
							RND_DAD_LABEL(ctx->dlg, "   y zoom:");
							RND_DAD_REAL(ctx->dlg);
								RND_DAD_HELP(ctx->dlg, "multiply values (y coords) to change aspect of the drawing");
								RND_DAD_MINMAX(ctx->dlg, 0.01, 10000);
								ctx->out[n].wplot_yzoom = RND_DAD_CURRENT(ctx->dlg);
								RND_DAD_CHANGE_CB(ctx->dlg, yzoom_cb);
						RND_DAD_END(ctx->dlg);
					RND_DAD_END(ctx->dlg);
				}
			RND_DAD_END(ctx->dlg);

		RND_DAD_END(ctx->dlg);


		/* bottom buttons */
		RND_DAD_BEGIN_HBOX(ctx->dlg);
			if (ctx->view_names.used > 0) {
				RND_DAD_LABEL(ctx->dlg, "view:");
				RND_DAD_ENUM(ctx->dlg, ctx->view_names.array);
					ctx->wview = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_BUTTON(ctx->dlg, "activate");
					RND_DAD_HELP(ctx->dlg, "activate the sim target and the test_bench stance associated with this simulation setup");
					RND_DAD_CHANGE_CB(ctx->dlg, activate_cb);
				RND_DAD_BUTTON(ctx->dlg, "run");
					RND_DAD_HELP(ctx->dlg, "activate this simulation setup, run the simulator and present the results in the \"run & output\" tab");
					RND_DAD_CHANGE_CB(ctx->dlg, run_cb);
			}
			else {
				RND_DAD_LABEL(ctx->dlg, "There's no sim capable view");
				RND_DAD_HELP(ctx->dlg, "There must be at least one view that has a target_sim_* target, else activating or running the simulation is not possible");
			}

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* spring */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BUTTON_CLOSES(ctx->dlg, clbtn);
		RND_DAD_END(ctx->dlg);

	RND_DAD_END(ctx->dlg);

	title = rnd_concat("Simulation setup \"", name, "\"", NULL);

	RND_DAD_DEFSIZE(ctx->dlg, 300, 500);
	RND_DAD_NEW("SimulationSetupDialog", ctx->dlg, title, ctx, 0, sim_setup_dlg_close_cb); /* type=local */

	for(n = 0; n < MAX_ANALS; n++)
		ctx->out[n].pprv.hid_ctx = ctx->dlg_hid_ctx;

	free(title);
	sch_sim_setup_sch2dlg(ctx);
	run2out_reset(ctx);
}

const char csch_acts_SimSetupDlg[] = "SimDlg(setup_name)";
const char csch_acth_SimSetupDlg[] = "Open the sim(ulation) setup/run dialog for the named setup in the current project\n";
fgw_error_t csch_act_SimSetupDlg(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	rnd_design_t *dsg = RND_ACT_DESIGN;
	const char *name;

	RND_ACT_CONVARG(1, FGW_STR, SimSetupDlg, name = argv[1].val.str);

	sim_setup_dlg((csch_project_t *)dsg->project, name);
	return 0;
}

static void sim_setup_dlg_prj_unload(rnd_project_t *prj)
{
	sim_setup_dlg_ctx_t *ctx, *next;

	/* close all dialogs referring to this project */
	for(ctx = gdl_first(&dlgs); ctx != NULL; ctx = next) {
		next = gdl_next(&dlgs, ctx);

		if (&ctx->prj->hdr == prj) {
			rnd_dad_retovr_t retovr = {0};
			gdl_remove(&dlgs, ctx, link);
			rnd_hid_dad_close(ctx->dlg_hid_ctx, &retovr, 0);
		}
	}
}

void sim_setup_dlg_setup_removed(const char *setup_name)
{
	sim_setup_dlg_ctx_t *ctx, *next;

	/* close all dialogs referring to this project */
	for(ctx = gdl_first(&dlgs); ctx != NULL; ctx = next) {
		next = gdl_next(&dlgs, ctx);

		if (strcmp(ctx->name, setup_name) == 0) {
			rnd_dad_retovr_t retovr = {0};
			gdl_remove(&dlgs, ctx, link);
			rnd_hid_dad_close(ctx->dlg_hid_ctx, &retovr, 0);
		}
	}
}

static void sim_setup_dlg_uninit(void)
{
	sim_setup_dlg_ctx_t *ctx;

	while((ctx = gdl_first(&dlgs)) != NULL) {
		rnd_dad_retovr_t retovr = {0};

		gdl_remove(&dlgs, ctx, link);
		rnd_hid_dad_close(ctx->dlg_hid_ctx, &retovr, 0);
	}
}


static void sim_setup_dlg_init(void)
{
}

