/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2024 Tibor 'Igor2' Palinkas
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.*
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; 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
 */

#include "config.h"

#include <genht/htsp.h>
#include <genht/hash.h>
#include <librnd/core/error.h>
#include <librnd/core/compat_misc.h>
#include <librnd/hid/hid.h>
#include <librnd/core/actions.h>
#include <librnd/hid/hid_dad.h>

#include "libcschem/cnc_grp.h"
#include "libcschem/search.h"
#include "libcschem/extobj.h"

#include "funchash_core.h"
#include "crosshair.h"
#include "search.h"
#include "draw.h"

#include "extobj_act.h"

static const char extobj_cookie[] = "extobj";

static char *last_eo_name = NULL;

static csch_extobj_impl_t *extobj_pick_gui(void)
{
	csch_extobj_impl_t *res = NULL;
	int n, resi;
	htsp_entry_t *e;
	rnd_hid_dad_buttons_t clbtn[] = {{"Cancel", -1}, {NULL, 0}};
	RND_DAD_DECL(dlg);

	RND_DAD_BEGIN_VBOX(dlg);
		RND_DAD_COMPFLAG(dlg, RND_HATF_EXPFILL);
		RND_DAD_LABEL(dlg, "Choose extended object:");
		if (last_eo_name != NULL) {
			int idx = -1;
			csch_extobj_impl_t *eo;

			for(e = htsp_first(&extobjs_impls), n = 0; e != NULL; e = htsp_next(&extobjs_impls, e), n++) {
				eo = e->value;
				if (strcmp(eo->name, last_eo_name) == 0) {
					idx = n;
					break;
				}
			}

			if (idx >= 0) {
				RND_DAD_BEGIN_HBOX(dlg);
					RND_DAD_LABEL(dlg, "Last used:");
					RND_DAD_BUTTON_CLOSE(dlg, eo->name, idx);
				RND_DAD_END(dlg);
			}
		}
		RND_DAD_BEGIN_TABLE(dlg, 3);
			for(e = htsp_first(&extobjs_impls), n = 0; e != NULL; e = htsp_next(&extobjs_impls, e), n++) {
				csch_extobj_impl_t *eo = e->value;
				RND_DAD_BUTTON_CLOSE(dlg, eo->name, n);
			}
		RND_DAD_END(dlg);
		RND_DAD_BUTTON_CLOSES(dlg, clbtn);
	RND_DAD_END(dlg);

	RND_DAD_NEW("extobj_select", dlg, "Select extended object", NULL, rnd_true, NULL);
	resi = RND_DAD_RUN(dlg);
	RND_DAD_FREE(dlg);

	if (resi >= 0) {
		for(e = htsp_first(&extobjs_impls), n = 0; e != NULL; e = htsp_next(&extobjs_impls, e), n++)
			if (n == resi)
				res = e->value;
	}

	if (res != NULL) {
		free(last_eo_name);
		last_eo_name = rnd_strdup(res->name);
	}
	return res;
}


static int filter_for_extobj(csch_search_obj_first_t *ctx, csch_chdr_t *obj)
{
	if (obj->type == CSCH_CTYPE_GRP) {
		csch_cgrp_t *grp = (csch_cgrp_t *)obj;
		if (grp->extobj != NULL)
			return 0;
	}
	return 1; /* ignore */
}


static const char csch_acts_Extobj[] = "Extobj(Object|Selected, Update|Edit)\nExtobj(Object|Selected, ConvFrom, [extobj_name])\n";
static const char csch_acth_Extobj[] = "Standard extended object operations on a single object (under the crosshair) or selected objects.";
fgw_error_t csch_act_Extobj(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	int scope, op;
	int want_extobj = 1; /* search only for extended objects for objs.array[] */
	vtp0_t objs = {0};
	csch_chdr_t *cobj;
	htip_entry_t *e;
	long n, found = 0;

	RND_ACT_IRES(1);

	RND_ACT_CONVARG(1, FGW_KEYWORD, Extobj, scope = fgw_keyword(&argv[1]));
	RND_ACT_CONVARG(2, FGW_KEYWORD, Extobj, op = fgw_keyword(&argv[2]));

	if (op == F_ConvFrom)
		want_extobj = 0;

	switch(scope) {
		case F_Object:
			if (want_extobj) {
				csch_rtree_box_t qb;
				qb.x1 = P2C(sch_rnd_crosshair_x); qb.y1 = P2C(sch_rnd_crosshair_y);
				qb.x2 = qb.x1+1; qb.y2 = qb.y1 + 1;
				cobj = csch_search_first_gui_inspect_filter(sheet, &qb, filter_for_extobj);
			}
			else
				cobj = sch_rnd_search_obj_at(sheet, P2C(sch_rnd_crosshair_x), P2C(sch_rnd_crosshair_y), sch_rnd_slop);

			if (cobj != NULL)
				vtp0_append(&objs, cobj);
			break;

		case F_Selected:
			for(e = htip_first(&sheet->direct.id2obj); e != NULL; e = htip_next(&sheet->direct.id2obj, e)) {
				csch_chdr_t *cobj = e->value;
				if (want_extobj) {
					if (filter_for_extobj(NULL, cobj) == 0)
						vtp0_append(&objs, cobj);
				}
				else
					vtp0_append(&objs, cobj);
			}
			break;

		default:
			rnd_message(RND_MSG_ERROR, "Extobj(): invalid scope (first argument)\n");
			return 0; /* IRES is set to 1 */
	}

	if (objs.used == 0) {
		rnd_message(RND_MSG_ERROR, "Extobj(): no suitable object found in scope\n");
		return 0; /* IRES is set to 1 */
	}

	switch(op) {
		case F_Update:
			for(n = 0; n < objs.used; n++) {
				csch_cgrp_t *grp = (csch_cgrp_t *)objs.array[n];
				if ((grp->hdr.type == CSCH_CTYPE_GRP) && (grp->extobj != NULL)) {
					csch_extobj_update(grp);
					found++;
				}
			}
			if (found == 0) {
				rnd_message(RND_MSG_ERROR, "Extobj(): failed to find any extobj to update\n");
				break;
			}
			RND_ACT_IRES(0);
			return 0;

		case F_Edit:
			for(n = 0; n < objs.used; n++) {
				csch_cgrp_t *grp = (csch_cgrp_t *)objs.array[n];
				if ((grp->hdr.type == CSCH_CTYPE_GRP) && (grp->extobj != NULL)) {
					if (csch_extobj_gui_edit_dlg(grp))
						found++;
				}
			}
			if (found == 0) {
				rnd_message(RND_MSG_ERROR, "Extobj(): failed to find any extobj to invoke gui editor dialog for\n");
				break;
			}
			RND_ACT_IRES(0);
			return 0;

		case F_ConvFrom:
			{
				long orig = objs.used;
				const csch_extobj_impl_t *eo;
				const char *eoname = NULL;

				RND_ACT_MAY_CONVARG(3, FGW_STR, Extobj, eoname = argv[3].val.str);

				if ((eoname == NULL) || (*eoname == '\0')) {
					eo = extobj_pick_gui();
					if (eo == NULL)
						break; /* cancel */
				}
				else {
					eo = htsp_get(&extobjs_impls, eoname);
					if (eo == NULL) {
						rnd_message(RND_MSG_ERROR, "Extobj(): no such extobj implementation: %s\n", eoname);
						break;
					}
				}

				if (eo->conv_from == NULL) {
					rnd_message(RND_MSG_ERROR, "Extobj(): extended object %s does not implement ConvFrom\n", eoname);
					break;
				}

				csch_extobj_conv_from(&objs, eo);

				if (objs.used == orig) {
					rnd_message(RND_MSG_ERROR, "Extobj(): failed to convert any of the objects to extended object %s\n", eoname);
					break;
				}

				RND_ACT_IRES(0);
				return 0;
			}
			break;

		default:
			rnd_message(RND_MSG_ERROR, "Extobj(): invalid command (second argument)\n");
			break;
	}

	return 0; /* IRES is set to 1 */
}

static rnd_action_t csch_extobj_act_list[] = {
	{"Extobj", csch_act_Extobj, csch_acth_Extobj, csch_acts_Extobj}
};

void sch_rnd_extobj_act_init(void)
{
	RND_REGISTER_ACTIONS(csch_extobj_act_list, extobj_cookie);
}

void sch_rnd_extobj_act_uninit(void)
{
	rnd_remove_actions_by_cookie(extobj_cookie);
}

