/*
 *                            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
 */

/* Simulation setup, sim modification edit dialog, #included */

#define MAX_TDF_PARAMS 8

typedef struct sim_mod_dlg_ctx_s {
	RND_DAD_DECL_NOINIT(dlg)
	lht_node_t *nmod; /* this works only because the dialog is modal and the node won't be deleted during operation */

	int wtype;
	int wadd_name, wadd_name_box;
	int wadd_device, wadd_pos, wadd_neg;
	int wadd_what, wadd_value;
	int wadd_ac_value_box, wadd_ac_value;
	int wadd_tdf_box, wadd_tdf;
	int wadd_tdf_parkey[MAX_TDF_PARAMS], wadd_tdf_parval[MAX_TDF_PARAMS];
	int womit_type, womit_name;
	int wattr_key, wattr_val;
	int wdisc_comp, wdisc_port;
	int wtemp_temp;

	int wbox_add, wbox_omit, wbox_edit_attr, wbox_disconn, wbox_temp;
} sim_mod_dlg_ctx_t;

static void mod_uninit(sim_mod_dlg_ctx_t *ctx)
{

}


static void mod_update(sim_mod_dlg_ctx_t *ctx)
{
	int *show = NULL, has_name = 0;
	rnd_hid_attr_val_t hv;

	/* arrange the right box to show, hide the rest */
	switch(ctx->dlg[ctx->wtype].val.lng) {
		case SCH_SIMOD_ADD:       show = &ctx->wbox_add; has_name = 1; break;
		case SCH_SIMOD_OMIT:      show = &ctx->wbox_omit; break;
		case SCH_SIMOD_EDIT_ATTR: show = &ctx->wbox_edit_attr; break;
		case SCH_SIMOD_DISCON:    show = &ctx->wbox_disconn; break;
		case SCH_SIMOD_TEMP:      show = &ctx->wbox_temp; break;
	}

	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wbox_add, (show != &ctx->wbox_add));
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wbox_omit, (show != &ctx->wbox_omit) && (show != &ctx->wbox_edit_attr));
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wbox_edit_attr, (show != &ctx->wbox_edit_attr));
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wbox_disconn, (show != &ctx->wbox_disconn));
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wbox_temp, (show != &ctx->wbox_temp));

	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_name_box, !has_name);


	/* update type-specific fields and labels */
	switch(ctx->dlg[ctx->wtype].val.lng) {
		case SCH_SIMOD_ADD:
			{
				int hide_ac = 1, hide_tdf = 1;
				static const char *what_strs[] = {
					/* V */ "voltage, dc value",
					/* I */ "current, dc value",
					/* R */ "resistance [ohm]",
					/* C */ "capacitance [farad]",
					/* L */ "inductace [henry]"
				};
				int val = ctx->dlg[ctx->wadd_device].val.lng;

				if ((val >= 0) && (val < sizeof(what_strs)/sizeof(what_strs[0]))) {
					hv.str = what_strs[val];
					hide_ac = !sch_sim_device_has_ac[val];
					hide_tdf = !sch_sim_device_has_tdf[val];
				}
				else
					hv.str = "(?)";
				rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wadd_what, &hv);
				rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_ac_value_box, hide_ac);
				rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_tdf_box, hide_tdf);
			}
			break;
	}
}

static void fillin_str(sim_mod_dlg_ctx_t *ctx, const char *confpath, int widx)
{
	rnd_hid_attr_val_t hv;

	hv.str = get_path_str(ctx->nmod, confpath, "", NULL);
	rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, widx, &hv);
}

static void mod_conf2dlg_tdf(sim_mod_dlg_ctx_t *ctx)
{
	int n;
	rnd_hid_attr_val_t hv;
	sch_sim_mod_tdf_t tdf = ctx->dlg[ctx->wadd_tdf].val.lng;
	const sch_sim_mod_tdf_param_t tp_none = {0}, *tp = &tp_none;
	lht_node_t *ntdfp = lht_dom_hash_get(ctx->nmod, "tdf_params");
	gds_t tmp = {0};

	if ((ntdfp != NULL) && (ntdfp->type != LHT_HASH))
		ntdfp = NULL;

	if ((tdf >= 0) && (tdf < SCH_SIMTDF_max))
		tp = sch_sim_mod_tdf_params[tdf];

	for(n = 0; (n < MAX_TDF_PARAMS) && (tp->name != NULL); n++, tp++) {
		tmp.used = 0;
		gds_append_str(&tmp, tp->name);
		gds_append_str(&tmp, " (");
		gds_append_str(&tmp, tp->desc);
		gds_append(&tmp, ')');
		hv.str = tmp.array;
		rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wadd_tdf_parkey[n], &hv);

		if (ntdfp != NULL)
			hv.str = get_path_str(ntdfp, tp->name, "", NULL);
		else
			hv.str = "";
		rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wadd_tdf_parval[n], &hv);

		rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_tdf_parkey[n], 0);
		rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_tdf_parval[n], 0);
	}

	for(; n < MAX_TDF_PARAMS; n++) {
		rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_tdf_parkey[n], 1);
		rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wadd_tdf_parval[n], 1);
	}

	gds_uninit(&tmp);
}

static void mod_dlg2conf_tdf(sim_mod_dlg_ctx_t *ctx, int del_all)
{
	sch_sim_mod_tdf_t tdf = ctx->dlg[ctx->wadd_tdf].val.lng;
	lht_node_t *nd, *ntdfp = lht_dom_hash_get(ctx->nmod, "tdf_params");
	const sch_sim_mod_tdf_param_t *tp;
	int n;

	if (ntdfp != NULL)
		lht_tree_del(ntdfp);

	if (del_all)
		return;

	ntdfp = lht_dom_node_alloc(LHT_HASH, "tdf_params");
	lht_dom_hash_put(ctx->nmod, ntdfp);

	if ((tdf < 0) || (tdf >= SCH_SIMTDF_max))
		return; /* leave ntdfp empty */

	if ((tdf < 0) || (tdf >= SCH_SIMTDF_max))
		return; /* invalid */

	tp = sch_sim_mod_tdf_params[tdf];

	for(n = 0; (n < MAX_TDF_PARAMS); n++, tp++) {
		const char *key = tp->name;
		const char *val;

		if (key == NULL)
			break;

		val = ctx->dlg[ctx->wadd_tdf_parval[n]].val.str;
		if (val != NULL) {
			nd = lht_dom_node_alloc(LHT_TEXT, key);
			nd->data.text.value = rnd_strdup(val);
			lht_dom_hash_put(ntdfp, nd);
		}
	}
}


static void mod_conf2dlg(sim_mod_dlg_ctx_t *ctx)
{
	rnd_hid_attr_val_t hv;

	hv.lng = sch_sim_str2mod_type(ctx->nmod->name);
	rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wtype, &hv);

	switch(ctx->dlg[ctx->wtype].val.lng) {
		case SCH_SIMOD_ADD:

			hv.lng = sch_sim_str2mod_device(get_path_str(ctx->nmod, "device", NULL, NULL));
			rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wadd_device, &hv);

			fillin_str(ctx, "pos", ctx->wadd_pos);
			fillin_str(ctx, "neg", ctx->wadd_neg);
			fillin_str(ctx, "value", ctx->wadd_value);
			fillin_str(ctx, "name", ctx->wadd_name);
			fillin_str(ctx, "ac_value", ctx->wadd_ac_value);

			hv.lng = sch_sim_str2mod_tdf(get_path_str(ctx->nmod, "tdf", NULL, NULL));
			rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wadd_tdf, &hv);

			mod_conf2dlg_tdf(ctx);
			break;

		case SCH_SIMOD_EDIT_ATTR:
			fillin_str(ctx, "key", ctx->wattr_key);
			fillin_str(ctx, "value", ctx->wattr_val);
			/* fall thru for reusing widgets */

		case SCH_SIMOD_OMIT:
			hv.lng = sch_sim_str2mod_target_type(get_path_str(ctx->nmod, "type", NULL, NULL));
			rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->womit_type, &hv);

			fillin_str(ctx, "name", ctx->womit_name);

			break;
		case SCH_SIMOD_DISCON:
			fillin_str(ctx, "comp", ctx->wdisc_comp);
			fillin_str(ctx, "port", ctx->wdisc_port);
			break;

		case SCH_SIMOD_TEMP:
			fillin_str(ctx, "temp", ctx->wtemp_temp);
			break;
	}

	mod_update(ctx);
}


static void mod_dlg2conf(sim_mod_dlg_ctx_t *ctx)
{
	const char *str;

	switch(ctx->dlg[ctx->wtype].val.lng) {
		case SCH_SIMOD_ADD:
			str = ctx->dlg[ctx->wadd_device].val.lng < 0 ? NULL : sch_simmod_dev_names[ctx->dlg[ctx->wadd_device].val.lng];
			sch_sim_update_text_node(ctx->nmod, "device", str);

			sch_sim_update_text_node(ctx->nmod, "pos", ctx->dlg[ctx->wadd_pos].val.str);
			sch_sim_update_text_node(ctx->nmod, "neg", ctx->dlg[ctx->wadd_neg].val.str);
			sch_sim_update_text_node(ctx->nmod, "value", ctx->dlg[ctx->wadd_value].val.str);
			sch_sim_update_text_node(ctx->nmod, "name", ctx->dlg[ctx->wadd_name].val.str);
			
			if ((ctx->dlg[ctx->wadd_device].val.lng >= 0) && sch_sim_device_has_ac[ctx->dlg[ctx->wadd_device].val.lng])
				sch_sim_update_text_node(ctx->nmod, "ac_value", ctx->dlg[ctx->wadd_ac_value].val.str);
			else
				sch_sim_update_text_node(ctx->nmod, "ac_value", NULL);

			if ((ctx->dlg[ctx->wadd_device].val.lng >= 0) && sch_sim_device_has_tdf[ctx->dlg[ctx->wadd_device].val.lng])
				str = ctx->dlg[ctx->wadd_tdf].val.lng < 0 ? NULL : sch_simmod_tdf_names[ctx->dlg[ctx->wadd_tdf].val.lng];
			else
				str = NULL;
			sch_sim_update_text_node(ctx->nmod, "tdf", str);

			mod_dlg2conf_tdf(ctx, ((ctx->dlg[ctx->wadd_device].val.lng < 0) || !sch_sim_device_has_tdf[ctx->dlg[ctx->wadd_device].val.lng]));
			break;

		case SCH_SIMOD_EDIT_ATTR:
			sch_sim_update_text_node(ctx->nmod, "key", ctx->dlg[ctx->wattr_key].val.str);
			sch_sim_update_text_node(ctx->nmod, "value", ctx->dlg[ctx->wattr_val].val.str);
		/* fall thru for reusing widgets */

		case SCH_SIMOD_OMIT:
			str = ctx->dlg[ctx->womit_type].val.lng < 0 ? NULL : sch_simmod_target_type_names[ctx->dlg[ctx->womit_type].val.lng];
			sch_sim_update_text_node(ctx->nmod, "type", str);

			sch_sim_update_text_node(ctx->nmod, "name", ctx->dlg[ctx->womit_name].val.str);
			break;
		case SCH_SIMOD_DISCON:
			sch_sim_update_text_node(ctx->nmod, "comp", ctx->dlg[ctx->wdisc_comp].val.str);
			sch_sim_update_text_node(ctx->nmod, "port", ctx->dlg[ctx->wdisc_port].val.str);
			break;
		case SCH_SIMOD_TEMP:
			sch_sim_update_text_node(ctx->nmod, "temp", ctx->dlg[ctx->wtemp_temp].val.str);
			break;
	}
}

static void mod_regen_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr_btn)
{
	sim_mod_dlg_ctx_t *ctx = caller_data;
	mod_update(ctx);
}

static void mod_type_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr_btn)
{
	const char *str;
	sim_mod_dlg_ctx_t *ctx = caller_data;
	/* when type is switched there would be some type-specific fields in the
	   config left over from the previous type; rather clean the hash */

	sch_sim_lht_dom_hash_clean(ctx->nmod);

	str = ctx->dlg[ctx->wtype].val.lng < 0 ? NULL : sch_simmod_type_names[ctx->dlg[ctx->wtype].val.lng];

	/* we can do this because our parent is not a hash */
	free(ctx->nmod->name);
	ctx->nmod->name = rnd_strdup(str);

	mod_update(ctx);
}

static void mod_tdf_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr_btn)
{
	sim_mod_dlg_ctx_t *ctx = caller_data;
	mod_update(ctx);
	mod_conf2dlg_tdf(ctx);
}

int dlg_mod_edit(lht_node_t *nmod)
{
	sim_mod_dlg_ctx_t ctx_ = {0}, *ctx = &ctx_;
	rnd_hid_dad_buttons_t clbtn[] = {{"Close", 0}, {NULL, 0}};
	int n;

	if ((nmod == NULL) || (nmod->type != LHT_HASH))
		return -1;

	ctx->nmod = nmod;

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

		RND_DAD_ENUM(ctx->dlg, sch_simmod_type_names);
		ctx->wtype = RND_DAD_CURRENT(ctx->dlg);
		RND_DAD_CHANGE_CB(ctx->dlg, mod_type_cb);

		RND_DAD_BEGIN_VBOX(ctx->dlg); /* add */
			RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
			ctx->wbox_add = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_ENUM(ctx->dlg, sch_simmod_dev_names);
				ctx->wadd_device = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_CHANGE_CB(ctx->dlg, mod_regen_cb);

			RND_DAD_BEGIN_HBOX(ctx->dlg);
				ctx->wadd_name_box = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Name (optional)");
				RND_DAD_LABEL(ctx->dlg, ":");
				RND_DAD_STRING(ctx->dlg);
					ctx->wadd_name = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Connect positive to:");
				RND_DAD_STRING(ctx->dlg);
					ctx->wadd_pos = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);
			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Connect negative to:");
				RND_DAD_STRING(ctx->dlg);
					ctx->wadd_neg = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Value ");
				RND_DAD_LABEL(ctx->dlg, "(what)");
					ctx->wadd_what = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, ":");
				RND_DAD_STRING(ctx->dlg);
					ctx->wadd_value = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);


			RND_DAD_BEGIN_HBOX(ctx->dlg);
				ctx->wadd_ac_value_box = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "AC value:");
					RND_DAD_HELP(ctx->dlg, "set to AC magnitude (V or A) when this is the source for ac analysis");
				RND_DAD_STRING(ctx->dlg);
					ctx->wadd_ac_value = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_VBOX(ctx->dlg);
				ctx->wadd_tdf_box = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Time dependent value:");
					RND_DAD_ENUM(ctx->dlg, sch_simmod_tdf_names);
						ctx->wadd_tdf = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, mod_tdf_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_TABLE(ctx->dlg, 2);
					for(n = 0; n < MAX_TDF_PARAMS; n++) {
						RND_DAD_LABEL(ctx->dlg, "");
							ctx->wadd_tdf_parkey[n] = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_STRING(ctx->dlg);
							ctx->wadd_tdf_parval[n] = RND_DAD_CURRENT(ctx->dlg);
					}
				RND_DAD_END(ctx->dlg);
			RND_DAD_END(ctx->dlg);
		RND_DAD_END(ctx->dlg);

		RND_DAD_BEGIN_HBOX(ctx->dlg); /* omit */
			ctx->wbox_omit = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_ENUM(ctx->dlg, sch_simmod_target_type_names);
				ctx->womit_type = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_STRING(ctx->dlg);
				ctx->womit_name = RND_DAD_CURRENT(ctx->dlg);
		RND_DAD_END(ctx->dlg);

		RND_DAD_BEGIN_VBOX(ctx->dlg); /* edit attr */
			ctx->wbox_edit_attr = RND_DAD_CURRENT(ctx->dlg);
			/* NOTE: reuses target type from above */
			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Attribute key:");
				RND_DAD_STRING(ctx->dlg);
				ctx->wattr_key = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);
			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Attribute value:");
				RND_DAD_STRING(ctx->dlg);
				ctx->wattr_val = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);
		RND_DAD_END(ctx->dlg);

		RND_DAD_BEGIN_VBOX(ctx->dlg); /* disconn */
			ctx->wbox_disconn = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Component:");
				RND_DAD_STRING(ctx->dlg);
				ctx->wdisc_comp = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);
			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Port name:");
				RND_DAD_STRING(ctx->dlg);
				ctx->wdisc_port = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);
		RND_DAD_END(ctx->dlg);

		RND_DAD_BEGIN_VBOX(ctx->dlg); /* temp */
			ctx->wbox_temp = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_LABEL(ctx->dlg, "Temperature in Celsius:");
				RND_DAD_STRING(ctx->dlg);
				ctx->wtemp_temp = RND_DAD_CURRENT(ctx->dlg);
			RND_DAD_END(ctx->dlg);
		RND_DAD_END(ctx->dlg);

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

	RND_DAD_DEFSIZE(ctx->dlg, 300, 300);
	RND_DAD_NEW("SimModConfig", ctx->dlg, "Simulation mod(ification) config", ctx, 1, NULL); /* type=local/modal */

	mod_conf2dlg(ctx);
	RND_DAD_RUN(ctx->dlg);
	mod_dlg2conf(ctx);
	RND_DAD_FREE(ctx->dlg);

	mod_uninit(ctx);

	return -1;
}

