/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  (this file is based on PCB, interactive printed circuit board design)
 *  Copyright (C) 1994,1995,1996 Thomas Nau
 *
 *  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/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 *
 */

/* Search object by location routines */

#ifndef PCB_SEARCH_H
#define PCB_SEARCH_H

#include <librnd/core/global_typedefs.h>
#include "layer.h"

int pcb_lines_intersect_at(rnd_coord_t ax1, rnd_coord_t ay1, rnd_coord_t ax2, rnd_coord_t ay2, rnd_coord_t bx1, rnd_coord_t by1, rnd_coord_t bx2, rnd_coord_t by2, rnd_coord_t *iscx, rnd_coord_t *iscy);
int pcb_lines_intersect(rnd_coord_t ax1, rnd_coord_t ay1, rnd_coord_t ax2, rnd_coord_t ay2, rnd_coord_t bx1, rnd_coord_t by1, rnd_coord_t bx2, rnd_coord_t by2);
rnd_bool pcb_arc_in_box(pcb_arc_t *arc, rnd_box_t *b);
rnd_bool pcb_gfx_in_box(pcb_gfx_t *gfx, rnd_box_t *b);

#define PCB_SLOP 5

/* ---------------------------------------------------------------------------
 * some define to check for 'type' in box
 */
#define	PCB_POINT_IN_BOX(x,y,b) \
	(PCB_IS_BOX_NEGATIVE(b) ?  \
	((x) >= (b)->X2 && (x) <= (b)->X1 && (y) >= (b)->Y2 && (y) <= (b)->Y1) \
	: \
	((x) >= (b)->X1 && (x) <= (b)->X2 && (y) >= (b)->Y1 && (y) <= (b)->Y2))

#define	PCB_VIA_OR_PIN_IN_BOX(v,b) \
	( \
	PCB_POINT_IN_BOX((v)->X-(v)->Thickness/2,(v)->Y-(v)->Thickness/2,(b)) && \
	PCB_POINT_IN_BOX((v)->X+(v)->Thickness/2,(v)->Y+(v)->Thickness/2,(b)) \
	)

#define	PCB_LINE_IN_BOX(l,b) \
	(PCB_POINT_IN_BOX((l)->Point1.X,(l)->Point1.Y,(b)) &&	\
	PCB_POINT_IN_BOX((l)->Point2.X,(l)->Point2.Y,(b)))

#define	PCB_PAD_IN_BOX(p,b) PCB_LINE_IN_BOX((pcb_line_t *)(p),(b))

#define	PCB_BOX_IN_BOX(b1,b) \
	((b1)->X1 >= (b)->X1 && (b1)->X2 <= (b)->X2 &&	\
	((b1)->Y1 >= (b)->Y1 && (b1)->Y2 <= (b)->Y2))

#define	PCB_TEXT_IN_BOX(t,b) \
	(PCB_BOX_IN_BOX(&((t)->BoundingBox), (b)))

#define	PCB_POLYGON_IN_BOX(p,b) \
	(PCB_BOX_IN_BOX(&((p)->BoundingBox), (b)))

#define	PCB_SUBC_IN_BOX(s,b) \
	(PCB_BOX_IN_BOX(&((s)->BoundingBox), (b)))

#define	PCB_ELEMENT_IN_BOX(e,b) \
	(PCB_BOX_IN_BOX(&((e)->BoundingBox), (b)))

/* the bounding box is much larger than the minimum, use it to decide it
   it is worth doing the expensive precise calculations */
#define PCB_ARC_IN_BOX(a,b) \
	((PCB_BOX_TOUCHES_BOX(&((a)->BoundingBox), (b))) && (pcb_arc_in_box(a,b)))

#define PCB_GFX_IN_BOX(g,b) \
	((PCB_BOX_TOUCHES_BOX(&((g)->BoundingBox), (b))) && (pcb_gfx_in_box(g,b)))

/* == the same but accept if any part of the object touches the box == */
#define PCB_POINT_IN_CIRCLE(x, y, cx, cy, r) \
	(rnd_distance2(x, y, cx, cy) <= (double)(r) * (double)(r))

#define PCB_CIRCLE_TOUCHES_BOX(cx, cy, r, b) \
	(    PCB_POINT_IN_BOX((cx)-(r),(cy),(b)) || PCB_POINT_IN_BOX((cx)+(r),(cy),(b)) || PCB_POINT_IN_BOX((cx),(cy)-(r),(b)) || PCB_POINT_IN_BOX((cx),(cy)+(r),(b)) \
	  || PCB_POINT_IN_CIRCLE((b)->X1, (b)->Y1, (cx), (cy), (r)) || PCB_POINT_IN_CIRCLE((b)->X2, (b)->Y1, (cx), (cy), (r)) || PCB_POINT_IN_CIRCLE((b)->X1, (b)->Y2, (cx), (cy), (r)) || PCB_POINT_IN_CIRCLE((b)->X2, (b)->Y2, (cx), (cy), (r)))

#define	PCB_VIA_OR_PIN_TOUCHES_BOX(v,b) \
	PCB_CIRCLE_TOUCHES_BOX((v)->X,(v)->Y,((v)->Thickness + (v)->DrillingHole/2),(b))

#define	PCB_LINE_TOUCHES_BOX(l,b)	\
	(    pcb_lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X1, (b)->Y1, (b)->X2, (b)->Y1) \
	  || pcb_lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X1, (b)->Y1, (b)->X1, (b)->Y2) \
	  || pcb_lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X2, (b)->Y2, (b)->X1, (b)->Y2) \
	  || pcb_lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X2, (b)->Y2, (b)->X2, (b)->Y1) \
	  || PCB_LINE_IN_BOX((l), (b)))

#define	PCB_XYLINE_ISECTS_BOX(x1,y1,x2,y2,b) \
	(    pcb_lines_intersect(x1,y1,x2,y2, (b)->X1, (b)->Y1, (b)->X2, (b)->Y1) \
	  || pcb_lines_intersect(x1,y1,x2,y2, (b)->X1, (b)->Y1, (b)->X1, (b)->Y2) \
	  || pcb_lines_intersect(x1,y1,x2,y2, (b)->X2, (b)->Y2, (b)->X1, (b)->Y2) \
	  || pcb_lines_intersect(x1,y1,x2,y2, (b)->X2, (b)->Y2, (b)->X2, (b)->Y1) \
	)

#define	PCB_PAD_TOUCHES_BOX(p,b)	PCB_LINE_TOUCHES_BOX((pcb_line_t *)(p),(b))

/* a corner of either box is within the other, or edges cross */
#define	PCB_BOX_TOUCHES_BOX(b1,b2) \
	(   PCB_POINT_IN_BOX((b1)->X1,(b1)->Y1,b2) || PCB_POINT_IN_BOX((b1)->X1,(b1)->Y2,b2) || PCB_POINT_IN_BOX((b1)->X2,(b1)->Y1,b2) || PCB_POINT_IN_BOX((b1)->X2,(b1)->Y2,b2)  \
	 || PCB_POINT_IN_BOX((b2)->X1,(b2)->Y1,b1) || PCB_POINT_IN_BOX((b2)->X1,(b2)->Y2,b1) || PCB_POINT_IN_BOX((b2)->X2,(b2)->Y1,b1) || PCB_POINT_IN_BOX((b2)->X2,(b2)->Y2,b1)  \
	 || pcb_lines_intersect((b1)->X1,(b1)->Y1, (b1)->X2,(b1)->Y1, (b2)->X1,(b2)->Y1, (b2)->X1,(b2)->Y2) \
	 || pcb_lines_intersect((b2)->X1,(b2)->Y1, (b2)->X2,(b2)->Y1, (b1)->X1,(b1)->Y1, (b1)->X1,(b1)->Y2))

#define	PCB_TEXT_TOUCHES_BOX(t,b) \
	(PCB_BOX_TOUCHES_BOX(&((t)->BoundingBox), (b)))

#define	PCB_POLYGON_TOUCHES_BOX(p,b) \
	(pcb_poly_is_rect_in_p((b)->X2, (b)->Y2, (b)->X1, (b)->Y1, (p)))

#define	PCB_SUBC_TOUCHES_BOX(s,b) \
	(PCB_BOX_TOUCHES_BOX(&((s)->BoundingBox), (b)))

#define	PCB_ELEMENT_TOUCHES_BOX(e,b) \
	(PCB_BOX_TOUCHES_BOX(&((e)->BoundingBox), (b)))

#define PCB_ARC_TOUCHES_BOX(a,b) \
	(pcb_is_arc_in_rectangle((b)->X2, (b)->Y2, (b)->X1, (b)->Y1, (a)))

#define PCB_GFX_TOUCHES_BOX(a,b) \
	(pcb_is_gfx_in_rectangle((b), (a)))


/* == the combination of *_IN_* and *_TOUCHES_*: use IN for positive boxes == */
#define PCB_IS_BOX_NEGATIVE(b) (((b)->X2 < (b)->X1) || ((b)->Y2 < (b)->Y1))

#define	PCB_BOX_NEAR_BOX(b1,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_BOX_TOUCHES_BOX(b1,b) : PCB_BOX_IN_BOX(b1,b))

#define	PCB_VIA_OR_PIN_NEAR_BOX(v,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_VIA_OR_PIN_TOUCHES_BOX(v,b) : PCB_VIA_OR_PIN_IN_BOX(v,b))

#define	PCB_LINE_NEAR_BOX(l,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_LINE_TOUCHES_BOX(l,b) : PCB_LINE_IN_BOX(l,b))

#define	PCB_PAD_NEAR_BOX(p,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_PAD_TOUCHES_BOX(p,b) : PCB_PAD_IN_BOX(p,b))

#define	PCB_TEXT_NEAR_BOX(t,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_TEXT_TOUCHES_BOX(t,b) : PCB_TEXT_IN_BOX(t,b))

#define	PCB_POLYGON_NEAR_BOX(p,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_POLYGON_TOUCHES_BOX(p,b) : PCB_POLYGON_IN_BOX(p,b))

#define	PCB_SUBC_NEAR_BOX(s,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_SUBC_TOUCHES_BOX(s,b) : PCB_SUBC_IN_BOX(s,b))

#define	PCB_ELEMENT_NEAR_BOX(e,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_ELEMENT_TOUCHES_BOX(e,b) : PCB_ELEMENT_IN_BOX(e,b))

#define PCB_ARC_NEAR_BOX(a,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_ARC_TOUCHES_BOX(a,b) : PCB_ARC_IN_BOX(a,b))

#define PCB_GFX_NEAR_BOX(a,b) \
	(PCB_IS_BOX_NEGATIVE(b) ? PCB_GFX_TOUCHES_BOX(a,b) : PCB_GFX_IN_BOX(a,b))

rnd_bool pcb_is_point_on_line(rnd_coord_t X, rnd_coord_t Y, rnd_coord_t Radius, pcb_line_t *Line);
rnd_bool pcb_is_point_on_thinline( rnd_coord_t X, rnd_coord_t Y, rnd_coord_t X1, rnd_coord_t Y1, rnd_coord_t X2,rnd_coord_t Y2 );
rnd_bool pcb_is_point_on_line_end(rnd_coord_t X, rnd_coord_t Y, pcb_rat_t *Line);
rnd_bool pcb_is_point_on_arc(rnd_coord_t X, rnd_coord_t Y, rnd_coord_t Radius, pcb_arc_t *Arc);
rnd_bool pcb_is_point_in_gfx(rnd_coord_t X, rnd_coord_t Y, rnd_coord_t Radius, pcb_gfx_t *gfx);
rnd_bool pcb_is_point_on_obj(rnd_coord_t X, rnd_coord_t Y, rnd_coord_t Radius, pcb_any_obj_t *obj);
rnd_bool pcb_is_line_in_rectangle(rnd_coord_t X1, rnd_coord_t Y1, rnd_coord_t X2, rnd_coord_t Y2, pcb_line_t *Line);
rnd_bool pcb_is_line_in_quadrangle(rnd_point_t p[4], pcb_line_t *Line);
rnd_bool pcb_is_arc_in_rectangle(rnd_coord_t X1, rnd_coord_t Y1, rnd_coord_t X2, rnd_coord_t Y2, pcb_arc_t *Arc);
rnd_bool pcb_is_gfx_in_rectangle(const rnd_box_t *b, const pcb_gfx_t *gfx);
rnd_bool pcb_is_point_in_line(rnd_coord_t X, rnd_coord_t Y, rnd_coord_t Radius, pcb_any_line_t *Pad);
rnd_bool pcb_is_point_in_box(rnd_coord_t X, rnd_coord_t Y, rnd_box_t *box, rnd_coord_t Radius);

/* returns whether l is inside the quadrangle p */
int pcb_geo_pt_in_quadrangle(rnd_point_t p[4], rnd_point_t *l);

/* Return the distance^2 between a (finite) line-center and a point; of
   offs_out is set to the offset of the point projected onto the infinite line,
   0.0 being lx1;ly1, 1.0 being lx2;ly2. prj*_out os the projected points' x;y
   coord. Any of the *_out arguments may be NULL. */
double pcb_geo_point_line_dist2(rnd_coord_t px, rnd_coord_t py, rnd_coord_t lx1, rnd_coord_t ly1, rnd_coord_t lx2, rnd_coord_t ly2, double *offs_out, double *prjx_out, double *prjy_out);


/* Return the distance^2 between a (finite) line-center and a point */
double pcb_point_line_dist2(rnd_coord_t X, rnd_coord_t Y, pcb_line_t *Line);

/* Return the first line object that has its centerline crossing the point;
   if ang is not NULL, only return lines that are pointing in the right
   angle (also accept 180 degree rotation)
   if no_subc_part is true, ignore lines that are part of subcircuits;
   if no_term is true, ignore lines that are terminals */
pcb_line_t *pcb_line_center_cross_point(pcb_layer_t *layer, rnd_coord_t x, rnd_coord_t y, rnd_angle_t *ang, rnd_bool no_subc_part, rnd_bool no_term);

int pcb_search_screen(rnd_coord_t X, rnd_coord_t Y, int Type, void **Result1, void **Result2, void **Result3);

/* If disabled and there's no GUI: return VOID immediatelly; else search
   screen at X,Y for any Type; if 0 or 1 found, return immediately. If
   there are more hits, present the object selector dialog (modal) */
int pcb_search_screen_selector(rnd_coord_t X, rnd_coord_t Y, int Type, void **Result1, void **Result2, void **Result3);

int pcb_search_screen_maybe_selector(rnd_coord_t X, rnd_coord_t Y, int Type, void **Result1, void **Result2, void **Result3);


int pcb_search_grid_slop(rnd_coord_t X, rnd_coord_t Y, int Type, void **Result1, void **Result2, void **Result3);
int pcb_search_obj_by_location(unsigned long Type, void **Result1, void **Result2, void **Result3, rnd_coord_t X, rnd_coord_t Y, rnd_coord_t Radius);
int pcb_search_obj_by_id(pcb_data_t *Base, void **Result1, void **Result2, void **Result3, int ID, int type);
int pcb_search_obj_by_id_(pcb_data_t *Base, void **Result1, void **Result2, void **Result3, int ID, int type); /* no-hace version */

/* Same as pcb_search_obj_by_id, but search in buffers too */
int pcb_search_obj_by_id_buf2(pcb_data_t *Base, void **Result1, void **Result2, void **Result3, int ID, int type);

#endif

