/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - construct groups
 *  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
 */


#include <libcschem/config.h>

#include <libcschem/search.h>
#include <libcschem/util_grp.h>
#include <libcschem/cnc_obj.h>
#include <librnd/core/actions.h>
#include <librnd/core/plugins.h>

#include <sch-rnd/funchash_core.h>
#include <sch-rnd/draw.h>

static const char sch_construct_cookie[] = "construct plugin";

RND_INLINE int can_convert(csch_sheet_t *sheet, csch_chdr_t *obj, unsigned int *warn, int by_selection)
{
	if (obj->parent != &sheet->direct) {

		if (by_selection) { /* when going by selection, do not warn objects part of first-level selected group */
			csch_cgrp_t *g;
			for(g = obj->parent; g->hdr.parent != &sheet->direct; g = g->hdr.parent) ;
			if (g->hdr.selected)
				return 0;
		}

		if ((warn != NULL) && (!(*warn & 1))) {
			rnd_message(RND_MSG_WARNING, "Some objects are not copied into the group because they are already part of a group\n");
			*warn |= 1;
		}
		return 0; /* do not convert objects already part of a group */
	}
	return 1;
}

#include "breakup.c"
#include "conv_grp.c"
#include "conv_poly.c"


static const char csch_acts_Convert[] = "Convert(selected|buffer|object|list, poly[gon]|group|sym[bol]|term[inal], [oidlist])\n";
static const char csch_acth_Convert[] = "Convert objects into a polygon or a group.";
static fgw_error_t csch_act_Convert(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_cgrp_t *grp;
	vtp0_t objs = {0};
	int src, type;
	long n;

	RND_ACT_CONVARG(1, FGW_KEYWORD, Convert, src = fgw_keyword(&argv[1]));
	RND_ACT_CONVARG(2, FGW_KEYWORD, Convert, type = fgw_keyword(&argv[2]));

	csch_cobj_redraw_freeze(sheet);
	uundo_freeze_serial(&sheet->undo);
	switch(src) {
		case F_Selected:
			if (csch_search_all_selected(sheet, NULL, &objs, 1) == 0) {
				rnd_message(RND_MSG_ERROR, "no movable object selected\n");
				goto error;
			}
			break;

		case F_Object:

		case F_Buffer:
		case F_List:
			rnd_message(RND_MSG_ERROR, "not yet implemented, sorry\n");
			goto error;

		default:
			rnd_message(RND_MSG_ERROR, "Convert: wrong first arg\n");
			goto error;
	}

	switch(type) {
		case F_Poly:
		case F_Polygon:
			if (sch_rnd_conv_poly(sheet, &sheet->direct, &objs, 0) == NULL)
				goto error;
			break;

		case F_Group:
		case F_Sym:
		case F_Symbol:
		case F_Term:
		case F_Terminal:
			if ((grp = sch_rnd_conv_grp(sheet, &sheet->direct, &objs, P2C(sch_rnd_crosshair_x), P2C(sch_rnd_crosshair_y), 0)) == NULL)
				goto error;
			break;

		default:
			rnd_message(RND_MSG_ERROR, "Convert: wrong second arg\n");
			goto error;
	}

	/* side effects for advanced groups */
	switch(type) {
		case F_Sym:
		case F_Symbol:
			csch_cgrp_role_side_effects(sheet, grp, CSCH_ROLE_SYMBOL);
			break;
		case F_Term:
		case F_Terminal:
			csch_cgrp_role_side_effects(sheet, grp, CSCH_ROLE_TERMINAL);
			break;
		default: break;
	}

	for(n = 0; n < objs.used; n++)
		csch_op_remove(sheet, objs.array[n]);

	uundo_unfreeze_serial(&sheet->undo);
	uundo_inc_serial(&sheet->undo);
	csch_cobj_redraw_unfreeze(sheet);
	vtp0_uninit(&objs);
	RND_ACT_IRES(0);
	return 0;

	error:;
	uundo_unfreeze_serial(&sheet->undo);
	csch_cobj_redraw_unfreeze(sheet);
	vtp0_uninit(&objs);
	RND_ACT_IRES(-1);
	return 0;
}

static const char csch_acts_Breakup[] = "Breakup(selected|buffer|object, group|poly[gon]|all, [sheet|grp-oid])\n";
static const char csch_acth_Breakup[] = "Breakup group(s) found in context named in first arg, place resulting objects in the context named in second arg if present or on sheet if not present";
static fgw_error_t csch_act_Breakup(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;
	const char *dsts = NULL;
	csch_cgrp_t *dst = NULL;
	vtp0_t srcs = {0};
	csch_cmask_t mask;
	int src, types;
	long n;

	RND_ACT_CONVARG(1, FGW_KEYWORD, Breakup, src = fgw_keyword(&argv[1]));
	RND_ACT_CONVARG(2, FGW_KEYWORD, Breakup, types = fgw_keyword(&argv[2]));
	RND_ACT_MAY_CONVARG(3, FGW_STR, Breakup, dsts = argv[3].val.cstr);

	uundo_freeze_serial(&sheet->undo);
	switch(types) {
		case F_Poly:
		case F_Polygon:
			mask = CSCH_CMASK_POLY;
			break;
		case F_Group:
			mask = CSCH_CMASK_ANY_GRP;
			break;
		case F_Any:
			mask = CSCH_CMASK_ANY;
			break;
		default:
		rnd_message(RND_MSG_ERROR, "Breakup: invalid second argument\n");
		goto error;
	}


	switch(src) {
		case F_Selected:
			if (csch_search_all_selected(sheet, NULL, &srcs, 1) == 0) {
				rnd_message(RND_MSG_ERROR, "not object selected\n");
				goto error;
			}
			break;

		case F_Object:

		case F_Buffer:
		case F_List:
			rnd_message(RND_MSG_ERROR, "not yet implemented, sorry\n");
			goto error;

		default:
			rnd_message(RND_MSG_ERROR, "Breakup: wrong first arg\n");
			goto error;
	}

	if ((dsts == NULL) || (strcmp(dsts, "sheet") == 0)) {
		dst = &sheet->direct;
	}
	else {
		rnd_message(RND_MSG_ERROR, "Breakup: oid in third argument not yet supported\n");
		goto error;
	}

	for(n = 0; n < srcs.used; n++) {
		csch_chdr_t *obj = srcs.array[n];

		if (!csch_ctype_in_cmask(obj->type, mask)) continue;

		if (csch_obj_is_grp(obj))
			sch_rnd_breakup_grp(sheet, dst, (csch_cgrp_t *)obj, 0);
		else if (obj->type == CSCH_CTYPE_POLY)
			sch_rnd_breakup_poly(sheet, dst, (csch_cpoly_t *)obj, 0);
		csch_op_remove(sheet, obj);
	}

	uundo_unfreeze_serial(&sheet->undo);
	uundo_inc_serial(&sheet->undo);
	vtp0_uninit(&srcs);
	RND_ACT_IRES(0);
	return 0;

	error:;
	uundo_unfreeze_serial(&sheet->undo);
	vtp0_uninit(&srcs);
	RND_ACT_IRES(-1);
	return 0;
}

static rnd_action_t sch_construct_action_list[] = {
	{"Convert", csch_act_Convert, csch_acth_Convert, csch_acts_Convert},
	{"Breakup", csch_act_Breakup, csch_acth_Breakup, csch_acts_Breakup}
};


int pplg_check_ver_construct(int ver_needed) { return 0; }

void pplg_uninit_construct(void)
{
	rnd_remove_actions_by_cookie(sch_construct_cookie);
}

int pplg_init_construct(void)
{
	RND_API_CHK_VER;

	RND_REGISTER_ACTIONS(sch_construct_action_list, sch_construct_cookie);
	return 0;
}

