/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - 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
 */

/* stance configuration dialog */

#include <libcschem/config.h>

#include <genht/htpp.h>
#include <genht/hash.h>
#include <genvector/vtl0.h>

#include <librnd/core/event.h>
#include <librnd/core/conf.h>
#include <librnd/core/conf_hid.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 <librnd/core/rnd_printf.h>

#include <libcschem/project.h>
#include <libcschem/event.h>
#include <libcschem/actions_csch.h>
#include <libcschem/search.h>

#include <sch-rnd/project.h>

/* This doesn't make a plugin dep as only inlines and macros and structs are used */
#include <sch-rnd/conf_core.h>
#define RND_TIMED_CHG_TIMEOUT conf_core.editor.edit_time
#include <librnd/plugins/lib_hid_common/timed_chg.h>

#include "dlg_stance.h"

typedef struct stance_dlg_ctx_s {
	RND_DAD_DECL_NOINIT(dlg)

	csch_project_t *prj;

	int wtree, wpend;
	rnd_timed_chg_t timed_update;
} stance_dlg_ctx_t;

static htpp_t prj2dlg;

static void stance_dlg_close_cb(void *caller_data, rnd_hid_attr_ev_t ev)
{
	stance_dlg_ctx_t *ctx = caller_data;

	rnd_timed_chg_cancel(&ctx->timed_update);
	htpp_pop(&prj2dlg, ctx->prj);
	free(ctx);
}

static void stance_prj2dlg(stance_dlg_ctx_t *ctx)
{
	static const char *paths[] = {"stance/model", "stance/sub_major", "stance/sub_minor", "stance/test_bench", NULL};
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wtree];
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	const char **s;
	char *cursor_path = NULL, *cell[3];

	/* remember cursor */
	r = rnd_dad_tree_get_selected(attr);
	if (r != NULL)
		cursor_path = rnd_strdup(r->path);

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

	/* add all items */
	cell[2] = NULL;
	for(s = paths; *s != NULL; s++) {
		rnd_conf_native_t *nat = rnd_conf_get_field(*s);
		cell[0] = rnd_strdup(*s + 7);
		cell[1] = (nat->val.string[0] == NULL) ? NULL : rnd_strdup(nat->val.string[0]);
		rnd_dad_tree_append(attr, NULL, cell);
	}

	/* restore cursor */
	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->wtree, &hv);
		free(cursor_path);
	}
}

typedef struct stance_val_ctx_s {
	RND_DAD_DECL_NOINIT(dlg)
	int wenum, wstr;
	vts0_t values;
} stance_val_ctx_t;

static void select_from_enum_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	stance_val_ctx_t *vctx = caller_data;
	rnd_hid_attr_val_t hv;

	if ((attr->val.lng >= 0) && (attr->val.lng < vctx->values.used)) {
		hv.str = vctx->values.array[attr->val.lng];
		rnd_gui->attr_dlg_set_value(vctx->dlg_hid_ctx, vctx->wstr, &hv);
	}
}

static void val_enter_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	stance_val_ctx_t *vctx = caller_data;
	rnd_hid_dad_close(hid_ctx, vctx->dlg_ret_override, 1);
}


sch_stance_edit_res_t sch_stance_edit_dlg(const char *stance_name, const char *initval, char **val_out)
{
	rnd_hid_dad_buttons_t clbtn[] = {{"Cancel", 0}, {"OK", 1}, {NULL, 0}};
	rnd_conf_native_t *nat;
	gds_t tmp = {0};
	const char *currval;
	int res, enum_val = -1;
	rnd_hid_attr_val_t hv;
	stance_val_ctx_t vctx = {0};

	*val_out = NULL;

	/* get current value */
	gds_append_str(&tmp, "stance/");
	gds_append_str(&tmp, stance_name);
	if (initval == NULL) {
		nat = rnd_conf_get_field(tmp.array);
		if (nat == NULL) {
			rnd_message(RND_MSG_ERROR, "internal error: invalid stance name: '%s' (conf path: '%s')\n", stance_name, tmp.array);
			return STE_CANCEL;
		}
		currval = nat->val.string[0];
	}
	else
		currval = initval;

	/* get possible values */
	gds_append_str(&tmp, "_values");
	nat = rnd_conf_get_field(tmp.array);
	if ((nat != NULL) && (nat->type == RND_CFN_LIST)) {
		rnd_conf_listitem_t *item_li;
		const char *item_str;
		int idx;

		rnd_conf_loop_list_str(nat->val.list, item_li, item_str, idx) {
			vts0_append(&vctx.values, (char *)item_str);
			if (strcmp(item_str, currval) == 0)
				enum_val = idx;
		}
	}

	/* label */
	tmp.used = 0;
	gds_append_str(&tmp, "Value of ");
	gds_append_str(&tmp, stance_name);
	gds_append(&tmp, ':');


	RND_DAD_BEGIN_VBOX(vctx.dlg);
		RND_DAD_LABEL(vctx.dlg, tmp.array);
		if (vctx.values.array !=NULL) {
			RND_DAD_ENUM(vctx.dlg, vctx.values.array);
			vctx.wenum = RND_DAD_CURRENT(vctx.dlg);
			RND_DAD_CHANGE_CB(vctx.dlg, select_from_enum_cb);
		}
		RND_DAD_STRING(vctx.dlg);
			vctx.wstr = RND_DAD_CURRENT(vctx.dlg);
			RND_DAD_ENTER_CB(vctx.dlg, val_enter_cb);
		RND_DAD_BUTTON_CLOSES(vctx.dlg, clbtn);
	RND_DAD_END(vctx.dlg);

	RND_DAD_NEW("StanceEditDialog", vctx.dlg, "Edit stance value", &vctx, 1, NULL); /* type=local/modal */

	hv.str = currval;
	rnd_gui->attr_dlg_set_value(vctx.dlg_hid_ctx, vctx.wstr, &hv);

	hv.lng = enum_val;
	rnd_gui->attr_dlg_set_value(vctx.dlg_hid_ctx, vctx.wenum, &hv);

	res = RND_DAD_RUN(vctx.dlg);
	if (res == 1)
		*val_out = rnd_strdup(vctx.dlg[vctx.wstr].val.str);
	RND_DAD_FREE(vctx.dlg);

	gds_uninit(&tmp);
	vts0_uninit(&vctx.values);

	if (res == 0)
		return STE_CANCEL;

	if ((*val_out == NULL) || (**val_out == '\0'))
		return STE_SET; /* do not save empty in the list of values */

	return STE_SET | STE_REMEMBER;
}

static void stance_edit_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	stance_dlg_ctx_t *ctx = caller_data;
	csch_sheet_t *sheet = (csch_sheet_t *)ctx->prj->hdr.designs.array[0];
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wtree];
	rnd_hid_row_t *row = rnd_dad_tree_get_selected(tattr);
	char *val = NULL;
	sch_stance_edit_res_t r;

	if (row == NULL)
		return;

	r = sch_stance_edit_dlg(row->cell[0], NULL, &val);
	if ((r == STE_CANCEL) || (val == NULL)) {
		free(val);
		return;
	}

	if (sch_rnd_project_create_file_for_sheet_gui(sheet) == 0) {
		if (r & STE_REMEMBER) csch_stance_add_to_values(row->cell[0], val);
		if (r & STE_SET) csch_stance_set(row->cell[0], val);
	}
	else
		rnd_message(RND_MSG_ERROR, "Failed to save stance change in project file\n");

	free(val);
}

static void timed_update_cb(void *uctx)
{
	stance_dlg_ctx_t *ctx = uctx;
	stance_prj2dlg(ctx);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wpend, 1);
}

static void sch_rnd_stance_dlg(csch_project_t *prj)
{
	stance_dlg_ctx_t *ctx;
	rnd_hid_dad_buttons_t clbtn[] = {{"Close", 0}, {NULL, 0}};
	const char *hdr[] = {"name", "value", NULL};

	ctx = htpp_get(&prj2dlg, prj);
	if (ctx != NULL) {
		TODO("raise?");
		return;
	}

	ctx = calloc(sizeof(stance_dlg_ctx_t), 1);
	ctx->prj = prj;
	rnd_timed_chg_init(&ctx->timed_update, timed_update_cb, ctx);
	htpp_set(&prj2dlg, prj, ctx);

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

		RND_DAD_BEGIN_VBOX(ctx->dlg); /* left */
			RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
			RND_DAD_TREE(ctx->dlg, 2, 0, hdr); /* top left: tree */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL | RND_HATF_SCROLL);
				ctx->wtree = RND_DAD_CURRENT(ctx->dlg);

			RND_DAD_BEGIN_HBOX(ctx->dlg);
				RND_DAD_BUTTON(ctx->dlg, "Edit...");
					RND_DAD_CHANGE_CB(ctx->dlg, stance_edit_cb);
					RND_DAD_HELP(ctx->dlg, "Change the value of the selected stance.");
				RND_DAD_BEGIN_VBOX(ctx->dlg); /* spring */
					RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
					RND_DAD_LABEL(ctx->dlg, "pending change...");
						ctx->wpend = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_END(ctx->dlg);
				RND_DAD_BUTTON_CLOSES(ctx->dlg, clbtn);
			RND_DAD_END(ctx->dlg);

		RND_DAD_END(ctx->dlg);

	RND_DAD_END(ctx->dlg);

	RND_DAD_DEFSIZE(ctx->dlg, 300, 400);
	RND_DAD_NEW("StanceDialog", ctx->dlg, "Project stances", ctx, 0, stance_dlg_close_cb); /* type=local */

	stance_prj2dlg(ctx);
	rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wpend, 1);
	
}



const char csch_acts_StanceDialog[] = "StanceDialog()";
const char csch_acth_StanceDialog[] = "Open the current project's stance configuration dialog.\n";
fgw_error_t csch_act_StanceDialog(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	rnd_design_t *hidlib = RND_ACT_DESIGN;
	csch_sheet_t *sheet = (csch_sheet_t *)hidlib;

	sch_rnd_stance_dlg((csch_project_t *)sheet->hidlib.project);
	return 0;
}


/*** Common ***/

/* trigger gui update if any stance conf changes */
static void stance_change_post(rnd_conf_native_t *cfg, int arr_idx, void *user_data)
{
	if ((cfg->hash_path[0] == 's') && (strncmp(cfg->hash_path, "stance/", 7) == 0)) {
		rnd_design_t *curr = rnd_multi_get_current();
		if (curr != NULL) {
			rnd_project_t *prj = curr->project;
			stance_dlg_ctx_t *ctx = htpp_get(&prj2dlg, prj);
			if (ctx != NULL) {
				rnd_gui->attr_dlg_widget_hide(ctx->dlg_hid_ctx, ctx->wpend, 0);
				rnd_timed_chg_schedule(&ctx->timed_update);
			}
			rnd_event(curr, CSCH_EVENT_PRJ_STANCE_CHANGED, NULL);
		}
	}
}

static rnd_conf_hid_id_t stance_hid_id;
static const char cookie[] = "stance gui";
void csch_dlg_stance_init(void)
{
	static rnd_conf_hid_callbacks_t cb = {0};

	htpp_init(&prj2dlg, ptrhash, ptrkeyeq);

	/* get a global conf change callback */
	cb.val_change_post = stance_change_post;
	stance_hid_id = rnd_conf_hid_reg(cookie, &cb);
}

void csch_dlg_stance_uninit(void)
{
	rnd_dad_retovr_t retovr = {0};
	htpp_entry_t *e;

	for(e = htpp_first(&prj2dlg); e != NULL; e = htpp_next(&prj2dlg, e)) {
		stance_dlg_ctx_t *ctx = e->value;
		rnd_hid_dad_close(ctx->dlg_hid_ctx, &retovr, 0);
	}
	htpp_uninit(&prj2dlg);

	rnd_conf_hid_unreg(cookie);
}
