/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - sch-rnd (executable)
 *  Copyright (C) 2020 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., 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 "config.h"
#include "operation.h"
#include "search.h"
#include "cnc_obj.h"

typedef struct {
	csch_rtree_box_t *query;
	csch_rtree_cb_t *found_obj;
	void *found_ctx;
	int find_grp_bbox; /* when 1 any overlap with the group's bbox is enough for a match, else require an object overlap */
} search_ctx_t;

static csch_rtree_dir_t search_obj(void *ctx_, void *obj_, const csch_rtree_box_t *box)
{
	search_ctx_t *ctx = ctx_;
/*	csch_chdr_t *obj = obj_;*/

	TODO("detailed check for intersection");
	return ctx->found_obj(ctx->found_ctx, obj_, box);
	return csch_RTREE_DIR_NOT_FOUND_CONT;
}

csch_rtree_dir_t csch_search(csch_sheet_t *sheet, csch_rtree_box_t *query, csch_rtree_cb_t *found_obj, void *ctx)
{
	int n;
	csch_rtree_dir_t res, state = 0;
	search_ctx_t sctx;

	sctx.query = query;
	sctx.found_obj = found_obj;
	sctx.found_ctx = ctx;

	for(n = CSCH_DSPLY_max-1; n >= 0; n--) {
		if (!csch_layer_vis[n])
			continue;
		sctx.find_grp_bbox = (n >= CSCH_DSPLY_SYMBOL);
		res = csch_rtree_search_obj(&sheet->dsply[n], query, search_obj, &sctx);
		state |= res;
		if (res & csch_RTREE_DIR_STOP)
			break;
	}

	return state;
}

csch_rtree_dir_t csch_search_obj_first_cb(void *ctx_, void *obj_, const csch_rtree_box_t *box)
{
	csch_chdr_t *obj = obj_;
	csch_search_obj_first_t *ctx = ctx_;
	long score = 1, locked;

	if (!csch_isc_with_box(obj, ctx->query))
		return csch_RTREE_DIR_NOT_FOUND_CONT;

	if ((ctx->mask != 0) && !csch_ctype_in_cmask(obj->type, ctx->mask))
		return csch_RTREE_DIR_NOT_FOUND_CONT;

	if (ctx->ignore_wirenet_grp && csch_obj_is_grp(obj)) {
		csch_cgrp_t *grp = (csch_cgrp_t *)obj;
		if (grp->role == CSCH_ROLE_WIRE_NET)
			return csch_RTREE_DIR_NOT_FOUND_CONT;
	}

	if ((ctx->ignore != NULL) && (ctx->ignore(ctx, obj)))
		return csch_RTREE_DIR_NOT_FOUND_CONT;

	switch(obj->type) {
		case CSCH_CTYPE_GRP:       score = 1000; break;
		case CSCH_CTYPE_GRP_REF:   score = 1000; break;
		case CSCH_CTYPE_POLY:      score = 2000; break;
		case CSCH_CTYPE_BITMAP:    score = 3000; break;
		case CSCH_CTYPE_TEXT:      score = 4000; break;
		case CSCH_CTYPE_LINE:      score = 5000*16; break; /* *16 means even a floater text won't win over a line, e.g. in a terminal */
		case CSCH_CTYPE_ARC:       score = 5000*16; break;

		case CSCH_CTYPE_CONN:
		case CSCH_CTYPE_PEN:
		case CSCH_CTYPE_invalid:
		case CSCH_CTYPE_max:
			return csch_RTREE_DIR_NOT_FOUND_CONT;
	}

	if (ctx->explicitly_locked_only) {
		locked = csch_cobj_is_explicit_locked(obj);
		if (!locked)
			return csch_RTREE_DIR_NOT_FOUND_CONT;
		obj = csch_cobj_first_explicit_locked(obj);
		if (obj == NULL)
			return csch_RTREE_DIR_NOT_FOUND_CONT;
	}
	else {
		locked = csch_cobj_is_locked(obj);
		if (!locked)
			score = score * 2;

		if (locked) {
			if (ctx->allow_term) {
				if (obj->parent->role == CSCH_ROLE_TERMINAL) {
					ctx->res = &obj->parent->hdr;
					ctx->score = score * 2; /* win over wirenet */
					return csch_RTREE_DIR_FOUND_CONT;
				}
			}
			
			/* clicked on something locked, parent wants to find the first unlocked parent */
			obj = csch_cobj_first_unlocked(obj);
			if (obj == NULL)
				return csch_RTREE_DIR_NOT_FOUND_CONT;
		}
	}

	if (obj->floater)
		score = score * 2;

	if (score > ctx->score) {
		ctx->res = obj;
		ctx->score = score;
	}
	return csch_RTREE_DIR_FOUND_CONT;
}

csch_chdr_t *csch_search_first_mask(csch_sheet_t *sheet, csch_rtree_box_t *query, csch_cmask_t mask)
{
	csch_search_obj_first_t ctx = {0};
	ctx.query = query;
	ctx.mask = mask;
	csch_search(sheet, query, csch_search_obj_first_cb, &ctx);
	return ctx.res;
}

csch_chdr_t *csch_search_first_gui_filter(csch_sheet_t *sheet, csch_rtree_box_t *query, csch_search_obj_ignore_cb_t ignore)
{
	csch_search_obj_first_t ctx = {0};
	ctx.query = query;
	ctx.ignore_wirenet_grp = 1;
	ctx.ignore = ignore;
	csch_search(sheet, query, csch_search_obj_first_cb, &ctx);
	return ctx.res;
}

csch_chdr_t *csch_search_first_gui(csch_sheet_t *sheet, csch_rtree_box_t *query)
{
	return csch_search_first_gui_filter(sheet, query, NULL);
}


csch_chdr_t *csch_search_first_gui_inspect_filter(csch_sheet_t *sheet, csch_rtree_box_t *query, csch_search_obj_ignore_cb_t ignore)
{
	csch_search_obj_first_t ctx = {0};
	ctx.query = query;
	ctx.allow_term = 1;
	ctx.ignore_wirenet_grp = 1;
	ctx.ignore = ignore;
	csch_search(sheet, query, csch_search_obj_first_cb, &ctx);
	return ctx.res;
}

csch_chdr_t *csch_search_first_gui_inspect(csch_sheet_t *sheet, csch_rtree_box_t *query)
{
	return csch_search_first_gui_inspect_filter(sheet, query, NULL);
}


csch_chdr_t *csch_search_first_locked(csch_sheet_t *sheet, csch_rtree_box_t *query)
{
	csch_search_obj_first_t ctx = {0};
	ctx.query = query;
	ctx.explicitly_locked_only = 1;
	csch_search(sheet, query, csch_search_obj_first_cb, &ctx);
	return ctx.res;
}

csch_chdr_t *csch_search_first_locked_gui(csch_sheet_t *sheet, csch_rtree_box_t *query)
{
	csch_chdr_t *res = csch_search_first_locked(sheet, query);
	if (res != NULL)
		return res;

	return res;
}


long csch_search_all_selected(csch_sheet_t *sheet, csch_cgrp_t *src, vtp0_t *dst, int recursive)
{
	htip_entry_t *e;
	long cnt = 0;

	if (src == NULL)
		src = &sheet->direct;

	for(e = htip_first(&src->id2obj); e != NULL; e = htip_next(&src->id2obj, e)) {
		csch_chdr_t *obj = e->value;
		if (obj->selected) {
			vtp0_append(dst, obj);
			cnt++;
		}
		if (csch_obj_is_grp(obj) && (recursive || !csch_grp_is_atomic(sheet, (csch_cgrp_t *)obj)))
			cnt += csch_search_all_selected(sheet, (csch_cgrp_t *)obj, dst, recursive);
	}

	return cnt;
}
