/*
 *                            COPYRIGHT
 *
 *  camv-rnd - electronics-related CAM viewer
 *  Copyright (C) 2019,2020 Tibor 'Igor2' Palinkas
 *  (copied from pcb-rnd by the author)
 *
 *  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/camv-rnd
 *    lead developer: http://repo.hu/projects/camv-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include "config.h"

#include <librnd/config.h>

#include <string.h>
#include <ctype.h>

#include "event.h"
#include <librnd/core/actions.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_export.h>
#include <librnd/core/hidlib.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/compat_fs.h>
#include <librnd/core/globalconst.h>
#include <librnd/core/conf_hid.h>
#include <librnd/plugins/lib_hid_common/zoompan.h>

#include <librnd/plugins/lib_hid_common/dlg_export.h>

#include "data.h"
#include "plug_io_act.h"

#include "layersel.h"
#include "status.h"
#include "draw.h"

static const char *layersel_cookie = "camv_gui/layersel";
static const char *status_cookie = "camv_gui/status";
static const char *status_cookie2 = "camv_gui/status2";
static const char *camv_gui_cookie = "camv_gui";

#define NOGUI() \
do { \
	if ((rnd_gui == NULL) || (!rnd_gui->gui)) { \
		RND_ACT_IRES(1); \
		return 0; \
	} \
	RND_ACT_IRES(0); \
} while(0)

const char camv_acts_Popup[] = "Popup(MenuName, [obj-type])";
const char camv_acth_Popup[] = "Bring up the popup menu specified by MenuName, optionally modified with the object type under the cursor.\n";
fgw_error_t camv_act_Popup(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	char name[256], name2[256];
	const char *tn = NULL, *a0, *a1 = NULL;
	int r = 1;
	enum {
		CTX_NONE,
		CTX_OBJ_TYPE
	} ctx_sens = CTX_NONE;

	NOGUI();

	if (argc != 2 && argc != 3)
		RND_ACT_FAIL(Popup);

	RND_ACT_CONVARG(1, FGW_STR, Popup, a0 = argv[1].val.str);
	RND_ACT_MAY_CONVARG(2, FGW_STR, Popup, a1 = argv[2].val.str);

	*name = '\0';
	*name2 = '\0';

	if (argc == 3) {
		if (strcmp(a1, "obj-type") == 0) ctx_sens = CTX_OBJ_TYPE;
	}

	if (strlen(a0) < sizeof(name) - 32) {
		switch(ctx_sens) {
			case CTX_OBJ_TYPE:
				{
					rnd_coord_t x, y;
/*					pcb_objtype_t type;
					void *o1, *o2, *o3;*/
					rnd_hid_get_coords("context sensitive popup: select object", &x, &y, 0);
#if 0
					type = pcb_search_screen(x, y, PCB_OBJ_PSTK | PCB_OBJ_SUBC_PART, &o1, &o2, &o3);
					if (type == 0)
						type = pcb_search_screen(x, y, PCB_OBJ_CLASS_REAL, &o1, &o2, &o3);

					if (type == 0)
						tn = "none";
					else
						tn = pcb_obj_type_name(type);
#endif
					sprintf(name, "/popups/%s-%s", a0, tn);
					sprintf(name2, "/popups/%s-misc", a0);
				}
				break;
			case CTX_NONE:
				sprintf(name, "/popups/%s", a0);
				break;
				
		}
	}

	if (*name != '\0')
		r = rnd_gui->open_popup(rnd_gui, name);
	if ((r != 0) && (*name2 != '\0'))
		r = rnd_gui->open_popup(rnd_gui, name2);

	RND_ACT_IRES(r);
	return 0;
}

static char *dup_cwd(void)
{
	char tmp[RND_PATH_MAX + 1];
	return rnd_strdup(rnd_get_wd(tmp));
}

static const char camv_acts_Load[] = "Load()\n" "Load(Project|Layer)";
static const char camv_acth_Load[] = "Load a camv project or a layer from a user-selected file.";
static fgw_error_t camv_act_Load(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	static char *last_project = NULL, *last_layer = NULL;
	const char *function = "Layer";
	char *name = NULL;
	int multisel = 0;

	if (last_layer == NULL)     last_layer = dup_cwd();
	if (last_project == NULL)   last_project = dup_cwd();

	/* Called with both function and file name -> no gui */
	if (argc > 2)
		return RND_ACT_CALL_C(RND_ACT_DESIGN, camv_act_LoadFrom, res, argc, argv);

	RND_ACT_MAY_CONVARG(1, FGW_STR, Load, function = argv[1].val.str);

	if (rnd_strcasecmp(function, "Layer") == 0) {
		name = rnd_hid_fileselect(rnd_gui, "Load layer", "Import a layer from file", last_layer, NULL, NULL, "layer", RND_HID_FSD_READ | RND_HID_FSD_MULTI, NULL);
		multisel = 1;
	}
	else if (rnd_strcasecmp(function, "Project") == 0) {
		name = rnd_hid_fileselect(rnd_gui, "Load a project file", "load project (all layers) from file", last_project, ".lht", NULL, "project", RND_HID_FSD_READ, NULL);
		multisel = 0;
	}
	else {
		rnd_message(RND_MSG_ERROR, "Invalid subcommand for Load(): '%s'\n", function);
		RND_ACT_IRES(1);
		return 0;
	}

	if (name != NULL) {
		if (multisel) {
			char *n;
			for(n = name; *n != '\0'; n += strlen(n)+1) {
				if (rnd_conf.rc.verbose)
					fprintf(stderr, "Load:  Calling LoadFrom(%s, %s)\n", function, n);
				rnd_actionl("LoadFrom", function, n, NULL);
			}
		}
		else {
			if (rnd_conf.rc.verbose)
				fprintf(stderr, "Load:  Calling LoadFrom(%s, %s)\n", function, name);
			rnd_actionl("LoadFrom", function, name, NULL);
		}
		free(name);
	}

	RND_ACT_IRES(0);
	return 0;
}

static int save_as(rnd_design_t *hl, int what, int as, const char *name)
{
	camv_design_t *camv = (camv_design_t *)hl;
	const char *prev_fn, *whats, *prev_loadfn;
	char *free_me = NULL;
	int need_fsd, save_res = -1;

	if (name == NULL) {
		prev_fn = camv->hidlib.fullpath;
		if (prev_fn == NULL)
			prev_fn = free_me = dup_cwd();
		prev_loadfn = camv->hidlib.loadname;
		if (prev_loadfn == NULL)
			prev_loadfn = prev_fn;
		need_fsd = 1;
	}
	else {
		prev_fn = name;
		need_fsd = 0;
	}

	/* invoke FSD if needed */
	switch(what) {
		case 'd': /* design */
			if (need_fsd)
				name = rnd_hid_fileselect(rnd_gui, "Save design", "Save all layers to a design file", prev_loadfn, ".tdx", NULL, "design", 0, NULL);
			whats = "design";
			break;
		default:
			rnd_message(RND_MSG_ERROR, "Invalid first argument for Save() or SaveAs()\n");
			goto error;
	}

	if (name != NULL) { /* NULL means cancel */
		if (rnd_conf.rc.verbose)
			fprintf(stderr, "Save:  Calling SaveTo(%s, %s)\n", whats, name);
		save_res = rnd_actionva(hl, "SaveTo", whats, name, NULL);
	}
	else if (rnd_conf.rc.verbose)
		fprintf(stderr, "Save: SaveTo(%s, ...) cancelled\n", whats);

	free(free_me);
	return save_res;

	error:;
	free(free_me);
	return 1;
}

static const char camv_acts_Save[] = "Save()\n" "Save(design)";
static const char camv_acth_Save[] = "Save all sheets of a design to file.";
static const char camv_acts_SaveAs[] = "SaveAs(design, [filename])";
static const char camv_acth_SaveAs[] = "Save all sheets of a design to file.";
static fgw_error_t camv_act_Save(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	rnd_design_t *hl = RND_ACT_DESIGN;
	const char *actname = argv[0].val.func->name;
	int as = (actname[4] == 'a') || (actname[4] == 'A');
	int what = 'd';
	const char *name = NULL;

	if (as) {
		RND_ACT_MAY_CONVARG(1, FGW_STR, SaveAs, what = tolower(argv[1].val.cstr[0]));
		RND_ACT_MAY_CONVARG(2, FGW_STR, SaveAs, name = argv[2].val.cstr);
	}
	else
		RND_ACT_MAY_CONVARG(1, FGW_STR, Save, what = tolower(argv[1].val.cstr[0]));

	RND_ACT_IRES(save_as(hl, what, as, name));
	return 0;
}

const char camv_acts_SwapSides[] = "SwapSides(-|v|h|r, [S])";
const char camv_acth_SwapSides[] = "Swaps the side of the board you're looking at.";
fgw_error_t camv_act_SwapSides(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	camv_design_t *camv = (camv_design_t *)RND_ACT_DESIGN;
	rnd_box_t vb;
	rnd_coord_t x, y;
	double xcent, ycent, xoffs, yoffs;

	RND_GUI_NOGUI();

	rnd_hid_get_coords("Click to center of flip", &x, &y, 0);

	x = camv->crosshair_x;
	y = camv->crosshair_y;

	rnd_gui->view_get(rnd_gui, &vb);
	xcent = (double)(vb.X1 + vb.X2)/2.0;
	ycent = (double)(vb.Y1 + vb.Y2)/2.0;
	xoffs = xcent - x;
	yoffs = ycent - y;

	camv_draw_inhibit_inc();
	if (argc > 1) {
		const char *a, *b = "";

		RND_ACT_CONVARG(1, FGW_STR, SwapSides, a = argv[1].val.str);
		RND_ACT_MAY_CONVARG(2, FGW_STR, SwapSides, b = argv[2].val.str);
		switch (a[0]) {
			case '-': break;
			case 'h': case 'H':
				rnd_conf_toggle_heditor_("view/flip_x", view.flip_x);
				xoffs = 0;
				break;
			case 'v': case 'V':
				if (!rnd_conf.editor.view.flip_y)
					yoffs = -yoffs;
				else
					yoffs = 0;
				rnd_conf_toggle_heditor_("view/flip_y", view.flip_y);
				break;
			case 'r': case 'R':
				xoffs = 0;
				if (!rnd_conf.editor.view.flip_y)
					yoffs = -yoffs;
				else
					yoffs = 0;

				rnd_conf_toggle_heditor_("view/flip_x", view.flip_x);
				rnd_conf_toggle_heditor_("view/flip_y", view.flip_y);
				break;

			default:
				camv_draw_inhibit_dec();
				RND_ACT_IRES(1);
				return 0;
		}

		switch (b[0]) {
			case 'S':
			case 's':
				camv_data_reverse_layers(camv, 0);
				break;
		}
	}

	camv_draw_inhibit_dec();

	rnd_gui->pan(rnd_gui, rnd_round(x + xoffs), rnd_round(y + yoffs), 0);
	rnd_gui->set_crosshair(rnd_gui, x, y, RND_SC_PAN_VIEWPORT);

	rnd_gui->invalidate_all(rnd_gui);

	RND_ACT_IRES(0);
	return 0;
}


static rnd_action_t camv_gui_action_list[] = {
	{"PrintGUI", rnd_act_PrintDialog, rnd_acth_PrintDialog, rnd_acts_PrintDialog},
	{"Popup", camv_act_Popup, camv_acth_Popup, camv_acts_Popup},
	{"Load", camv_act_Load, camv_acth_Load, camv_acts_Load},
	{"Export", rnd_act_Export, rnd_acth_Export, rnd_acts_Export},
	{"Layer", camv_act_Layer, camv_acth_Layer, camv_acts_Layer},
	{"StatusSetText", camv_act_StatusSetText, camv_acth_StatusSetText, camv_acts_StatusSetText},
	{"Save", camv_act_Save, camv_acth_Save, camv_acts_Save},
	{"SaveAs", camv_act_Save, camv_acth_SaveAs, camv_acts_SaveAs},
	{"SwapSides", camv_act_SwapSides, camv_acth_SwapSides, camv_acts_SwapSides}
};

int pplg_check_ver_gui(int ver_needed) { return 0; }

void pplg_uninit_gui(void)
{
	rnd_actionl("rnd_toolbar_uninit", NULL);
	rnd_event_unbind_allcookie(status_cookie);
	rnd_event_unbind_allcookie(layersel_cookie);
	rnd_conf_hid_unreg(status_cookie);
	rnd_conf_hid_unreg(status_cookie2);
	rnd_remove_actions_by_cookie(camv_gui_cookie);
}

static rnd_conf_hid_id_t install_events(const char *cookie, const char *paths[], rnd_conf_hid_callbacks_t cb[], void (*update_cb)(rnd_conf_native_t*,int,void*))
{
	const char **rp;
	rnd_conf_native_t *nat;
	int n;
	rnd_conf_hid_id_t conf_id;

	conf_id = rnd_conf_hid_reg(cookie, NULL);
	for(rp = paths, n = 0; *rp != NULL; rp++, n++) {
		memset(&cb[n], 0, sizeof(cb[0]));
		cb[n].val_change_post = update_cb;
		nat = rnd_conf_get_field(*rp);
		if (nat != NULL)
			rnd_conf_hid_set_cb(nat, conf_id, &cb[n]);
	}

	return conf_id;
}

int pplg_init_gui(void)
{
	const char *stpaths[] = { "editor/grid_unit", "editor/grid", "editor/view/flip_x", "editor/view/flip_y", NULL };
	const char *rdpaths[] = { "editor/grid_unit", NULL };
	static rnd_conf_hid_callbacks_t stcb[sizeof(stpaths)/sizeof(stpaths[0])];
	static rnd_conf_hid_callbacks_t rdcb[sizeof(rdpaths)/sizeof(rdpaths[0])];

	rnd_event_bind(RND_EVENT_GUI_INIT, camv_layersel_gui_init_ev, NULL, layersel_cookie);
	rnd_event_bind(CAMV_EVENT_LAYERS_CHANGED, camv_layersel_layer_chg_ev, NULL, layersel_cookie);
	rnd_event_bind(CAMV_EVENT_LAYERVIS_CHANGED, camv_layersel_vis_chg_ev, NULL, layersel_cookie);
	rnd_event_bind(RND_EVENT_GUI_INIT, camv_status_gui_init_ev, NULL, status_cookie);
	rnd_event_bind(RND_EVENT_USER_INPUT_KEY, camv_status_st_update_ev, NULL, status_cookie);
	rnd_event_bind(RND_EVENT_CROSSHAIR_MOVE, camv_status_rd_update_ev, NULL, status_cookie);

	install_events(status_cookie, stpaths, stcb, camv_status_st_update_conf);
	install_events(status_cookie2, rdpaths, rdcb, camv_status_rd_update_conf);

	RND_REGISTER_ACTIONS(camv_gui_action_list, camv_gui_cookie);
	rnd_actionl("rnd_toolbar_init", NULL);
	return 0;
}
