/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - GUI
 *  Copyright (C) 2021,2022 Tibor 'Igor2' Palinkas
 *  (originally copied from camv-rnd by the author)
 *
 *  (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
 */

#include <libcschem/config.h>

#include <librnd/core/actions.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_cfg_input.h>
#include <librnd/hid/hid_dad.h>
#include <librnd/core/event.h>
#include <librnd/core/hidlib.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/plugins/lib_hid_common/xpm.h>

#include <libcschem/project.h>

#include "sch-rnd/conf_core.h"
#include "sch-rnd/crosshair.h"
#include "sch-rnd/draw.h"
#include "sch-rnd/buffer.h"
#include "sch-rnd/multi.h"

typedef struct {
	int active;
	rnd_hid_dad_subdialog_t stsub, rdsub; /* st is for the bottom status line, rd is for the top readouts */
	int stsub_inited, rdsub_inited;
	int wst, wsttxt, wcomp_chk, wcomp_btn, wcomp_bar;
	int st_has_text;
	int wrdunit, wrd[3];
	int wviewbtn;
	gds_t buf; /* save on allocation */
	int lock;
	rnd_hidval_t acomp_timer;
	unsigned acomp_timer_active:1;
	unsigned acomp_timer_want:1;
} status_ctx_t;

static status_ctx_t status;

#include "autocomp.c"

static void status_st_sch2dlg(rnd_design_t *dsg)
{
	rnd_hid_cfg_keys_t *kst = rnd_gui->key_state;
	static rnd_hid_attr_val_t hv;
	char kbd[128];
	const char *cnt, *refr, *rubber;
	csch_coord_t grid = (dsg != NULL) ? dsg->grid : 0;

	if (!status.stsub_inited)
		return;

	status.buf.used = 0;

	cnt    = conf_core.editor.line_cont ? "C," : "";
	refr   = conf_core.editor.line_refraction ? "|_" : "_|";
	if (conf_core.editor.rubber_band_mode)
		rubber = conf_core.editor.rubber_band_ortho ? ",RL" : ",R";
	else
		rubber = " ";

	if (kst != NULL) {
		if (kst->seq_len_action > 0) {
			int len;
			memcpy(kbd, "(last: ", 7);
			len = rnd_hid_cfg_keys_seq(kst, kbd+7, sizeof(kbd)-9);
			memcpy(kbd+len+7, ")", 2);
		}
		else
			rnd_hid_cfg_keys_seq(kst, kbd, sizeof(kbd));
	}
	else
		*kbd = '\0';

	rnd_append_printf(&status.buf, "grid=%$rc line=%s%s%s kbd=%s",
		P2C(grid),
		cnt, refr, rubber,
		kbd);
	hv.str = status.buf.array;
	rnd_gui->attr_dlg_set_value(status.stsub.dlg_hid_ctx, status.wst, &hv);
}

static void status_rd_sch2dlg(void)
{
	static rnd_hid_attr_val_t hv;

	if ((status.lock) || (!status.rdsub_inited))
		return;

	/* coordinate readout (right side box) */
	status.buf.used = 0;
	hv.str = status.buf.array;

	rnd_append_printf(&status.buf, "%$rc", P2C(sch_rnd_crosshair_x));
	rnd_gui->attr_dlg_set_value(status.rdsub.dlg_hid_ctx, status.wrd[0], &hv);
	status.buf.used = 0;
	rnd_append_printf(&status.buf, "%$rc", P2C(sch_rnd_crosshair_y));
	rnd_gui->attr_dlg_set_value(status.rdsub.dlg_hid_ctx, status.wrd[1], &hv);

	if (status.rdsub.dlg[status.wcomp_chk].val.lng) {
		rnd_gui->attr_dlg_widget_hide(status.rdsub.dlg_hid_ctx, status.wcomp_btn, 1);
		rnd_gui->attr_dlg_widget_hide(status.rdsub.dlg_hid_ctx, status.wcomp_bar, 0);
	}
	else {
		rnd_gui->attr_dlg_widget_hide(status.rdsub.dlg_hid_ctx, status.wcomp_btn, 0);
		rnd_gui->attr_dlg_widget_hide(status.rdsub.dlg_hid_ctx, status.wcomp_bar, 1);
	}
}

void status_activate_view(rnd_design_t *hidlib)
{
	rnd_hid_attr_val_t hv;
	csch_sheet_t *sheet = (csch_sheet_t *)hidlib;
	csch_project_t *prj;
	csch_view_t *v;

	if (!status.active && (sheet != NULL))
		return;

	prj = (csch_project_t *)sheet->hidlib.project;
	if (prj->curr < prj->views.used) {
		v = prj->views.array[prj->curr];
		hv.str = v->fgw_ctx.name;
	}
	else
		hv.str = "N/A";
	rnd_gui->attr_dlg_set_value(status.rdsub.dlg_hid_ctx, status.wviewbtn, &hv);
}

static void status_docked_create_st(void)
{
	RND_DAD_BEGIN_VBOX(status.stsub.dlg);
		RND_DAD_COMPFLAG(status.stsub.dlg, RND_HATF_EXPFILL | RND_HATF_TIGHT);
		RND_DAD_LABEL(status.stsub.dlg, "");
			RND_DAD_COMPFLAG(status.stsub.dlg, RND_HATF_HIDE);
			status.wsttxt = RND_DAD_CURRENT(status.stsub.dlg);
		RND_DAD_LABEL(status.stsub.dlg, "<pending update>");
			status.wst = RND_DAD_CURRENT(status.stsub.dlg);
	RND_DAD_END(status.stsub.dlg);
}

/* append an expand-vbox to eat up excess space for center-align */
static void vpad(rnd_hid_dad_subdialog_t *sub)
{
	RND_DAD_BEGIN_VBOX(sub->dlg);
		RND_DAD_COMPFLAG(sub->dlg, RND_HATF_EXPFILL | RND_HATF_TIGHT);
	RND_DAD_END(sub->dlg);
}

static void autocomp_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	status_rd_sch2dlg();
	if (status.rdsub.dlg[status.wcomp_chk].val.lng)
		autocomp_restart_timer(rnd_multi_get_current());
	else
		autocomp_stop_timer(rnd_multi_get_current());
}

static void btn_compile_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	rnd_actionva(rnd_multi_get_current(), "CompileProject", NULL);
	rnd_gui->invalidate_all(rnd_gui);
	TODO("fetch return value and print error or succes (info) - rather do it in the action: there are other places this is called from as well");
}


static void btn_viewchg_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	rnd_actionva(rnd_gui->get_dad_design(hid_ctx), "ViewDialog", NULL);
}

static void btn_support_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	rnd_actionva(rnd_gui->get_dad_design(hid_ctx), "irc", NULL);
}

static void status_docked_create_rd(rnd_design_t *hidlib)
{
	int n;
	const char **support_icon = rnd_dlg_xpm_by_name("online_help");

	RND_DAD_BEGIN_HBOX(status.rdsub.dlg);
		RND_DAD_COMPFLAG(status.rdsub.dlg, RND_HATF_TIGHT);

		/* compile block */
		RND_DAD_BEGIN_VBOX(status.rdsub.dlg);
			RND_DAD_COMPFLAG(status.rdsub.dlg, RND_HATF_FRAME | RND_HATF_TIGHT);
			RND_DAD_BEGIN_HBOX(status.rdsub.dlg);
				RND_DAD_BOOL(status.rdsub.dlg);
					RND_DAD_HELP(status.rdsub.dlg, "Enable timed auto-compile:\nautomatically compile current view after a short delay after\nchanges to the sheet");
					status.wcomp_chk = RND_DAD_CURRENT(status.rdsub.dlg);
					RND_DAD_CHANGE_CB(status.rdsub.dlg, autocomp_cb);
				RND_DAD_BUTTON(status.rdsub.dlg, "Compile");
					RND_DAD_HELP(status.rdsub.dlg, "Recompile the abstract model\nusing all sheets of the current project");
					RND_DAD_CHANGE_CB(status.rdsub.dlg, btn_compile_cb);
					status.wcomp_btn = RND_DAD_CURRENT(status.rdsub.dlg);
				RND_DAD_PROGRESS(status.rdsub.dlg);
					RND_DAD_COMPFLAG(status.rdsub.dlg, RND_HATF_PRG_SMALL);
					status.wcomp_bar = RND_DAD_CURRENT(status.rdsub.dlg);

			RND_DAD_END(status.rdsub.dlg);
			RND_DAD_BEGIN_HBOX(status.rdsub.dlg);
				RND_DAD_COMPFLAG(status.rdsub.dlg, RND_HATF_EXPFILL);
				RND_DAD_BUTTON(status.rdsub.dlg, "<unknown>");
					RND_DAD_COMPFLAG(status.rdsub.dlg, RND_HATF_EXPFILL);
					RND_DAD_HELP(status.rdsub.dlg, "Which of the project configured views is used for compilation\nand in turn for rendering display/ attributes");
					RND_DAD_CHANGE_CB(status.rdsub.dlg, btn_viewchg_cb);
					status.wviewbtn = RND_DAD_CURRENT(status.rdsub.dlg);
			RND_DAD_END(status.rdsub.dlg);
		RND_DAD_END(status.rdsub.dlg);

		RND_DAD_PICBUTTON(status.rdsub.dlg, support_icon);
			RND_DAD_CHANGE_CB(status.rdsub.dlg, btn_support_cb);
		RND_DAD_BEGIN_VBOX(status.rdsub.dlg);
			RND_DAD_COMPFLAG(status.rdsub.dlg, RND_HATF_EXPFILL | RND_HATF_FRAME | RND_HATF_TIGHT);
			vpad(&status.rdsub);
			for(n = 0; n < 3; n++) {
				RND_DAD_LABEL(status.rdsub.dlg, "<pending>");
					status.wrd[n] = RND_DAD_CURRENT(status.rdsub.dlg);
			}
		RND_DAD_END(status.rdsub.dlg);
	RND_DAD_END(status.rdsub.dlg);
}

static void status_rd_init()
{
	if (conf_core.editor.autocomp) {
		rnd_hid_attr_val_t hv;
		hv.lng = 1;
		rnd_gui->attr_dlg_set_value(status.rdsub.dlg_hid_ctx, status.wcomp_chk, &hv);
		autocomp_cb(NULL, NULL, NULL);
	}
}

void sch_status_gui_init_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	if ((RND_HAVE_GUI_ATTR_DLG) && (rnd_gui->get_menu_cfg != NULL)) {
		status_docked_create_st();
		if (rnd_hid_dock_enter(&status.stsub, RND_HID_DOCK_BOTTOM, "status") == 0) {
			status.stsub_inited = 1;
			status_st_sch2dlg(hidlib);
		}

		status_docked_create_rd(hidlib);
		if (rnd_hid_dock_enter(&status.rdsub, RND_HID_DOCK_TOP_RIGHT, "readout") == 0) {
			status.rdsub_inited = 1;
			status_rd_init();
			status_rd_sch2dlg();
		}
		status.active = 1;
		status_activate_view(hidlib);
	}
}

void sch_status_st_update_conf(rnd_conf_native_t *cfg, int arr_idx, void *user_data)
{
	status_st_sch2dlg(rnd_multi_get_current());
}

void sch_status_st_update_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	status_st_sch2dlg(rnd_multi_get_current());
}

void sch_status_rd_update_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	status_rd_sch2dlg();
}

void sch_status_rd_edit_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	if (status.active && status.rdsub.dlg[status.wcomp_chk].val.lng)
		autocomp_restart_timer(hidlib);
}

void sch_status_view_activated_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	status_activate_view(hidlib);
}

void sch_status_postload_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	if (status.active && status.rdsub.dlg[status.wcomp_chk].val.lng)
		autocomp_restart_timer(hidlib);
}

const char csch_acts_StatusSetText[] = "StatusSetText([text])\n";
const char csch_acth_StatusSetText[] = "Replace status printout with text temporarily; turn status printout back on if text is not provided.";
fgw_error_t csch_act_StatusSetText(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *text = NULL;

	if (argc > 2)
		RND_ACT_FAIL(StatusSetText);

	RND_ACT_MAY_CONVARG(1, FGW_STR, StatusSetText, text = argv[1].val.str);

	if (text != NULL) {
		rnd_hid_attr_val_t hv;
		hv.str = text;
		rnd_gui->attr_dlg_set_value(status.stsub.dlg_hid_ctx, status.wsttxt, &hv);
		rnd_gui->attr_dlg_widget_hide(status.stsub.dlg_hid_ctx, status.wsttxt, 0);
		rnd_gui->attr_dlg_widget_hide(status.stsub.dlg_hid_ctx, status.wst, 1);
		status.st_has_text = 1;
	}
	else {
		status.st_has_text = 0;
		rnd_gui->attr_dlg_widget_hide(status.stsub.dlg_hid_ctx, status.wst, 0);
		rnd_gui->attr_dlg_widget_hide(status.stsub.dlg_hid_ctx, status.wsttxt, 1);
	}

	RND_ACT_IRES(0);
	return 0;
}
