/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - GUI - view dialog
 *  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., 31 Milk Street, # 960789 Boston, MA 02196 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
 */

/* pen selection dialog */

#include <libcschem/config.h>

#include <stdlib.h>

#include <libcschem/libcschem.h>
#include <libcschem/project.h>

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

#include <sch-rnd/project.h>

#include "dlg_view.h"

/* double click timeout, in seconds */
#define DOUBLE_CLICK_TIME 0.5

typedef struct viewdlg_ctx_s {
	RND_DAD_DECL_NOINIT(dlg)
	int wviewlist, wenglist;
	csch_project_t *prj;
	long init_sel, last_sel;
	unsigned active:1;
	double last_click_time;
} viewdlg_ctx_t;

static viewdlg_ctx_t vctx;

static void viewdlg_close_cb(void *caller_data, rnd_hid_attr_ev_t ev)
{
	viewdlg_ctx_t *ctx = caller_data;

	ctx->active = 0;
}

static void view_view2dlg(viewdlg_ctx_t *ctx)
{
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wenglist];
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	csch_view_t *view = NULL;
	char *cell[3], *cursor_path = NULL;
	long n;


	r = rnd_dad_tree_get_selected(&ctx->dlg[ctx->wviewlist]);
	if (r != NULL)
		view = csch_view_get(ctx->prj, r->cell[0]);


	/* 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 (view != NULL) {
		cell[2] = NULL;
		for(n = 0; n < view->engines.used; n++) {
			rnd_hid_row_t *r;
			csch_view_eng_t *eng = view->engines.array[n];
			cell[0] = rnd_strdup(eng->obj->name);
			cell[1] = rnd_strdup_printf("%d, %d, %d", eng->eprio + CSCH_PRI_PLUGIN_HIGH, eng->eprio + CSCH_PRI_PLUGIN_NORMAL, eng->eprio + CSCH_PRI_PLUGIN_LOW);
			r = rnd_dad_tree_append(attr, NULL, cell);
			r->user_data2.lng = n;
		}
	}

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

static void view_prj2dlg(viewdlg_ctx_t *ctx)
{
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wviewlist];
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	rnd_hid_attr_val_t hv;
	char *cell[2], *cursor_path = NULL;
	long n;

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

	cell[1] = NULL;
	for(n = 0; n < ctx->prj->views.used; n++) {
		csch_view_t *view = ctx->prj->views.array[n];
		cell[0] = rnd_strdup(view->fgw_ctx.name);
		r = rnd_dad_tree_append(attr, NULL, cell);
		r->user_data2.lng = n;
		if ((cursor_path == NULL) && (n == ctx->prj->curr)) {
			hv.str = cell[0];
			rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wviewlist, &hv);
			ctx->last_sel = n;
		}
	}

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

	view_view2dlg(ctx);
}

static void timed_close_cb(rnd_hidval_t user_data)
{
	void *hid_ctx = user_data.ptr;
	static rnd_dad_retovr_t retovr = {0};
	rnd_hid_dad_close(hid_ctx, &retovr, 0);
}

static void view_select_cb(rnd_hid_attribute_t *attrib, void *hid_ctx, rnd_hid_row_t *row)
{
	rnd_hid_tree_t *tree = attrib->wdata;
	viewdlg_ctx_t *ctx = tree->user_ctx;
	double now = rnd_dtime();
	int dblclk = (now - ctx->last_click_time) < DOUBLE_CLICK_TIME;

	ctx->last_click_time = now;
	view_view2dlg(tree->user_ctx);

	if (dblclk && (row->user_data2.lng == ctx->last_sel)) {
		/* second click - needs to be timed, can't close from tree callback */
		rnd_hidval_t user_data;
		user_data.ptr = hid_ctx;
		rnd_gui->add_timer(rnd_gui, timed_close_cb, 1, user_data);
		return;
	}
	ctx->last_sel = row == NULL ? -1 : row->user_data2.lng;
}

static void activate_selected(viewdlg_ctx_t *ctx)
{
	if (ctx->last_sel >= 0)
		csch_view_activate(ctx->prj, ctx->last_sel);
}

static void btn_new_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	viewdlg_ctx_t *ctx = &vctx;
	rnd_design_t *hl = rnd_gui->get_dad_design(hid_ctx);
	csch_sheet_t *sheet = (csch_sheet_t *)hl;
	char *name;

	if (ctx->prj->dummy) {
		if (sch_rnd_project_create_file_for_sheet_gui(sheet) != 0)
			return;
	}

	name = rnd_hid_prompt_for(hl, "Name of the new view", "", "Creating new view");
	if ((name == NULL) || (*name == '\0')) {
		free(name);
		return;
	}

	if (sch_rnd_project_append_view(sheet, name, 0) == 0) {
		sch_rnd_project_views_save(sheet);
		view_prj2dlg(ctx);
	}
}

static void btn_del_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	viewdlg_ctx_t *ctx = &vctx;
	rnd_design_t *hl = rnd_gui->get_dad_design(hid_ctx);
	csch_sheet_t *sheet = (csch_sheet_t *)hl;

	if (ctx->prj->dummy) {
		if (sch_rnd_project_create_file_for_sheet_gui(sheet) != 0)
			return;
	}

	if (sch_rnd_project_del_view(sheet, ctx->last_sel, 0) == 0) {
		sch_rnd_project_views_save(sheet);
		ctx->prj->curr = -1;
		ctx->last_sel = 0;
		view_prj2dlg(ctx);
		csch_view_activate(ctx->prj, ctx->last_sel);
	}
}

/* op: -1: insert before; +1: append after; 0: delete */
static void view_eng_edit(void *hid_ctx, void *caller_data, int op)
{
	viewdlg_ctx_t *ctx = &vctx;
	rnd_design_t *hl = rnd_gui->get_dad_design(hid_ctx);
	csch_sheet_t *sheet = (csch_sheet_t *)hl;
	rnd_hid_attribute_t *tattr = &ctx->dlg[ctx->wenglist];
	rnd_hid_row_t *r;
	csch_view_t *view = NULL;
	csch_view_eng_t *neng;

	r = rnd_dad_tree_get_selected(&ctx->dlg[ctx->wviewlist]);
	if (r != NULL)
		view = csch_view_get(ctx->prj, r->cell[0]);
	if (view == NULL)
		return;

	r = rnd_dad_tree_get_selected(tattr);
	if ((op == 0) && (r == NULL)) /* remove requires selection */
		return;

	if (ctx->prj->dummy) {
		if (sch_rnd_project_create_file_for_sheet_gui(sheet) != 0)
			return;
	}

	if (op != 0) {
		const char *options = NULL;
		char *name = rnd_hid_prompt_for(hl, "Name of the engine", "", "Adding engine to a view");
		if ((name == NULL) || (*name == '\0')) {
			free(name);
			return;
		}
		neng = csch_eng_alloc(view, name, name, options);
		if (neng == NULL) {
			rnd_message(RND_MSG_ERROR, "dlg_view internal error: failed to create engine binding '%s' for view\nThere is probably no engine by that name (typo? missnig plugin?)\n", name);
			free(name);
			return;
		}
		free(name);

	}

	if (op < 0) { /* insert before */
		long where = 0;
		if (r != NULL)
			where = r->user_data2.lng;
		vtp0_insert_len(&view->engines, where, (void **)&neng, 1);
	}
	else if (op > 0) { /* append after */
		long where = 0;
		if (r != NULL)
			where = r->user_data2.lng;
		where++;
		if (where > view->engines.used)
			vtp0_append(&view->engines, (void *)neng);
		else
			vtp0_insert_len(&view->engines, where, (void **)&neng, 1);
	}
	else { /* op == 0: delete */
		fgw_obj_t *obj;
		vtp0_remove(&view->engines, r->user_data2.lng, 1);
		obj = fgw_obj_lookup(&view->fgw_ctx, r->cell[0]);
		if (obj != NULL)
			fgw_obj_unreg(&view->fgw_ctx, obj);
	}

	csch_view_renum(view);

	if (sch_rnd_project_view_lib2lht(view) != 0)
		rnd_message(RND_MSG_ERROR, "dlg_view internal error: failed to save view changes in project lihata");

	csch_project_flush();
	view_view2dlg(ctx);
}

static void btn_eng_ins_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	view_eng_edit(hid_ctx, caller_data, -1);
}

static void btn_eng_app_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	view_eng_edit(hid_ctx, caller_data, +1);
}


static void btn_eng_del_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	view_eng_edit(hid_ctx, caller_data, 0);
}

static const int view_dlg(csch_project_t *prj)
{
	viewdlg_ctx_t *ctx = &vctx;
	const char *views_hdr[] = {"view"};
	const char *plugin_hdr[] = {"engine", "range (+10)"};
	rnd_hid_dad_buttons_t clbtn[] = {{"close", 0}, {NULL, 0}};

	assert(!ctx->active);

	ctx->active = 1;
	ctx->prj = prj;
	ctx->last_sel = -1;
	ctx->init_sel = prj->curr;

	RND_DAD_BEGIN_VBOX(ctx->dlg);
		RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
		RND_DAD_BEGIN_HPANE(ctx->dlg, "left-right");

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* left side */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
				RND_DAD_TREE(ctx->dlg, 1, 0, views_hdr); /* top left: tree */
					RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
					RND_DAD_TREE_SET_CB(ctx->dlg, selected_cb, view_select_cb);
					RND_DAD_TREE_SET_CB(ctx->dlg, ctx, ctx);
					ctx->wviewlist = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_BUTTON(ctx->dlg, "New");
						RND_DAD_HELP(ctx->dlg, "Append a new view at the end of the list\nand save project file");
						RND_DAD_CHANGE_CB(ctx->dlg, btn_new_cb);
					RND_DAD_BUTTON(ctx->dlg, "Del");
						RND_DAD_HELP(ctx->dlg, "Remove currently selected view\nand save project file");
						RND_DAD_CHANGE_CB(ctx->dlg, btn_del_cb);
				RND_DAD_END(ctx->dlg);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* right side */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);

				RND_DAD_TREE(ctx->dlg, 2, 0, plugin_hdr); /* top right: tree */
					RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
					ctx->wenglist = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_BUTTON(ctx->dlg, "Ins. before");
						RND_DAD_CHANGE_CB(ctx->dlg, btn_eng_ins_cb);
						RND_DAD_HELP(ctx->dlg, "Insert a new engine before the currently selected one\nand save project file");
					RND_DAD_BUTTON(ctx->dlg, "App. after");
						RND_DAD_CHANGE_CB(ctx->dlg, btn_eng_app_cb);
						RND_DAD_HELP(ctx->dlg, "Append a new engine after the currently selected one\nand save project file");
					RND_DAD_BUTTON(ctx->dlg, "Del");
						RND_DAD_CHANGE_CB(ctx->dlg, btn_eng_del_cb);
						RND_DAD_HELP(ctx->dlg, "Remove the currently selected engine\nand save project file");
				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, 300);
	RND_DAD_NEW("ViewDialog", ctx->dlg, "Change view", ctx, 1, viewdlg_close_cb); /* type=local/modal */

	view_prj2dlg(ctx);

	RND_DAD_RUN(ctx->dlg);

	activate_selected(ctx);
	if (ctx->init_sel != ctx->last_sel) /* compile if changed */
		rnd_actionva(rnd_gui->get_dad_design(ctx->dlg_hid_ctx), "CompileProject", NULL);

	RND_DAD_FREE(ctx->dlg);

	return 0;
}


const char csch_acts_ViewDialog[] = "ViewDialog()";
const char csch_acth_ViewDialog[] = "Bring up a modal dialog for selecting the current view or editing views of the current project.\n";
fgw_error_t csch_act_ViewDialog(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;
	csch_project_t *prj = (csch_project_t *)sheet->hidlib.project;

	RND_ACT_IRES(view_dlg(prj));

	return 0;
}

