/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - sch-rnd (executable)
 *  Copyright (C) 2021,2023 Tibor 'Igor2' Palinkas
 *
 *  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., 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 <librnd/hid/hid.h>
#include <librnd/core/globalconst.h>
#include <libcschem/config.h>
#include <libcschem/concrete.h>
#include <libcschem/operation.h>
#include <libcschem/cnc_any_obj.h>
#include <libcschem/cnc_obj.h>
#include <libcschem/rtree.h>


#include "conf_core.h"
#include "draw.h"
#include "search.h"

#include "select.h"

static void sel_chg_notify(csch_sheet_t *sheet)
{
	rnd_event(&sheet->hidlib, CSCH_EVENT_SELECTION_CHANGED, NULL);
}

void sch_rnd_unselect_all(csch_sheet_t *sheet)
{
	int n;

	csch_cobj_redraw_freeze(sheet);

	for(n = 0; n < CSCH_DSPLY_max; n++) {
		csch_chdr_t *o;
		csch_rtree_it_t it;
		for(o = csch_rtree_all_first(&it, &sheet->dsply[n]); o != NULL; o = csch_rtree_all_next(&it))
			csch_chdr_unselect_wp(o);
	}

	sel_chg_notify(sheet);
	csch_cobj_redraw_unfreeze(sheet);
}

void sch_rnd_select_add_obj(csch_sheet_t *sheet, csch_chdr_t *obj, int meta, int no_par)
{
	csch_cgrp_t *par = obj->parent;
	int par_atomic = 0, par_is_grp;

	par_is_grp = csch_obj_is_grp(&par->hdr);

	if (!meta) { /* some parents are atomic by default, even if not requested via meta */
		par_atomic = csch_grp_is_atomic(sheet, par);
		if (par_atomic && no_par)
			return;
	}

	if (conf_core.editor.lock_floaters && obj->floater)
		return;
	if (conf_core.editor.only_floaters && !obj->floater)
		return;

	if (par_is_grp && (meta || par_atomic) && !obj->floater) {
		if ((par != &sheet->direct) && (par != &sheet->indirect))
			sch_rnd_select_add_obj(sheet, &par->hdr, 0, 0);
	}
	else
		csch_cobj_select(sheet, obj);
}

void sch_rnd_select_click(csch_sheet_t *sheet, csch_coord_t x, csch_coord_t y)
{
	csch_chdr_t *obj;
	
	if (rnd_gui->shift_is_pressed(rnd_gui))
		obj = sch_rnd_search_obj_at(sheet, x, y, sch_rnd_slop); /* for the shift+click unselect */
	else
		obj = sch_rnd_search_obj_at_unsel(sheet, x, y, sch_rnd_slop);

	if (obj == NULL) { /* clicked away or clicked on selected */
		/* check for selected wire for second click */
		obj = sch_rnd_search_obj_at(sheet, x, y, sch_rnd_slop);
		if ((obj != NULL) && (obj->parent->role == CSCH_ROLE_WIRE_NET)) {
			sch_rnd_select_add_obj(sheet, obj, 1, 0);
			return;
		}

		if (!rnd_gui->shift_is_pressed(rnd_gui))
			sch_rnd_unselect_all(sheet);
		return;
	}

	if (csch_chdr_is_selected(obj)) {
		if (rnd_gui->shift_is_pressed(rnd_gui))
			csch_chdr_unselect_wp(obj);
		else
			sch_rnd_select_add_obj(sheet, obj, 1, 0);
	}
	else {
		if (!rnd_gui->shift_is_pressed(rnd_gui))
			sch_rnd_unselect_all(sheet);
		sch_rnd_select_add_obj(sheet, obj, 0, 0);
	}
	sel_chg_notify(sheet);
}


void sch_rnd_select_box(csch_sheet_t *sheet, csch_coord_t x1, csch_coord_t y1, csch_coord_t x2, csch_coord_t y2)
{
	int neg = 0;
	vtp0_t res = {0};
	csch_coord_t slop = sch_rnd_slop;
	csch_rtree_box_t rbox;
	long n;

	csch_cobj_redraw_freeze(sheet);

	if (!rnd_gui->shift_is_pressed(rnd_gui))
		sch_rnd_unselect_all(sheet);

	/* figure if selection is negative */
	if (!conf_core.editor.selection.disable_negative) {
		int negx = x2 < x1, negy = y1 < y2; /* y is inverted on screen */

		if (!conf_core.editor.selection.symmetric_negative)
			neg = negy;

		neg |= negx;
	}

	/* make the box canonical */
	if (x1 > x2) rnd_swap(csch_coord_t, x1, x2);
	if (y1 > y2) rnd_swap(csch_coord_t, y1, y2);

	rbox.x1 = x1 - slop; rbox.y1 = y1 - slop;
	rbox.x2 = x2 + slop; rbox.y2 = y2 + slop;
	sch_rnd_search_obj_box(sheet, &res, &rbox);

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

		if (neg) { /* select anything that intersects */
			if (csch_isc_with_box(o, &rbox))
				sch_rnd_select_add_obj(sheet, o, 0, 0);
		}
		else { /* select anything that is fully within */
			if ((o->bbox.x1 >= rbox.x1) && (o->bbox.y1 >= rbox.y1) && (o->bbox.x2 <= rbox.x2) && (o->bbox.y2 <= rbox.y2))
				sch_rnd_select_add_obj(sheet, o, 0, 1);
		}
	}

TODO("if not neg, do a search on group bboxes too - that's the way a positiv box would select any symbol");
	sel_chg_notify(sheet);

	vtp0_uninit(&res);

	csch_cobj_redraw_unfreeze(sheet);
}


void sch_rnd_select_invert_grp(csch_chdr_t *obj, int recursive)
{
	if (recursive) {
		if (csch_obj_is_grp(obj)) {
			csch_cgrp_t *grp = (csch_cgrp_t *)obj;
			htip_entry_t *e;

			for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e))
				sch_rnd_select_invert_grp(e->value, recursive);
		}
		else
			obj->selected = !obj->selected;
	}
	else {
		if (obj->selected)
			csch_cobj_unselect(obj->sheet, obj);
		else
			sch_rnd_select_add_obj(obj->sheet, obj, 0, 1);
	}
}

void sch_rnd_select_invert(csch_sheet_t *sheet)
{
	htip_entry_t *e;

	csch_cobj_redraw_freeze(sheet);

	for(e = htip_first(&sheet->direct.id2obj); e != NULL; e = htip_next(&sheet->direct.id2obj, e)) {
		csch_chdr_t *obj = e->value;

		if (csch_obj_is_grp(obj)) {
			csch_cgrp_t *grp = (csch_cgrp_t *)obj;
			if (!csch_grp_is_atomic(sheet, grp)) {
				sch_rnd_select_invert_grp(&grp->hdr, 1);
				continue;
			}
		}
		sch_rnd_select_invert_grp(obj, 0);
	}
	sel_chg_notify(sheet);
	csch_cobj_redraw_unfreeze(sheet);
}

static long grp_add_sel_bbox(csch_rtree_box_t *dst, csch_cgrp_t *grp)
{
	htip_entry_t *e;
	long cnt = 0;

	for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) {
		csch_chdr_t *obj = e->value;
		if (obj->selected) {
			cnt++;
			csch_bbox_bump(dst, x, grp->bbox_flt.x1);
			csch_bbox_bump(dst, x, grp->bbox_flt.x2);
			csch_bbox_bump(dst, y, grp->bbox_flt.y1);
			csch_bbox_bump(dst, y, grp->bbox_flt.y2);
		}
		if (csch_obj_is_grp(obj))
			cnt += grp_add_sel_bbox(dst, (csch_cgrp_t *)obj);
	}

	return cnt;
}

long sch_rnd_get_selection_bbox(csch_rtree_box_t *dst, csch_sheet_t *sheet)
{
	dst->x1 = dst->y1 = RND_MAX_COORD;
	dst->x2 = dst->y2 = -RND_MAX_COORD;

	return grp_add_sel_bbox(dst, &sheet->direct);
}

long sch_rnd_get_selection_bbox_gui(rnd_box_t *dst, csch_sheet_t *sheet)
{
	long res;
	csch_rtree_box_t cb;

	res = sch_rnd_get_selection_bbox(&cb, sheet);
	dst->X1 = C2P(cb.x1);
	dst->Y1 = C2P(cb.y1);
	dst->X2 = C2P(cb.x2);
	dst->Y2 = C2P(cb.y2);
	return res;
}
