/*  libgrbs - geometric rubber band sketch model
    Copyright (C) 2021  Tibor 'Igor2' Palinkas
    (Supported by NLnet NGI0 PET Fund in 2021)

    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/libgrbs
      lead developer: http://repo.hu/projects/pcb-rnd/contact.html
*/

static void extend_sentinel(grbs_arc_t *sentinel, double end)
{
	if (grbs_angle_in_arc(sentinel->sa, sentinel->da, end, 0))
		return;

	/* need to extend sentinel range to include the newly attached angle */
	if (sentinel->new_adir > 0) {
		if (sentinel->sa > end)
			end += 2 * GRBS_PI;
		sentinel->da = end - sentinel->sa;
	}
	else {
		if (sentinel->sa < end)
			end -= 2 * GRBS_PI;
		sentinel->da = end - sentinel->sa;
	}
}

static int grbs_force_attach_line_to_pt(grbs_t *grbs, grbs_line_t *line, grbs_point_t *pt, double outer_copr, double clr, int segi)
{
	grbs_arc_t *new, *prev = line->a1, *next = line->a2, *sentinel = NULL, *under;
	grbs_2net_t *tn = grbs_arc_parent_2net(prev);
	g2d_offs_t o;
	g2d_vect_t vpt, vcr;
	g2d_cline_t cline;
	double dx, dy, sa, r, da;

	if (line->immutable)
		return -1;

	/* cheap: insert a zero-length arc keeping the path with two colinear lines;
	   then realize() will bump it as needed */
	vpt.x = pt->x; vpt.y = pt->y;
	cline.p1.x = line->x1; cline.p1.y = line->y1;
	cline.p2.x = line->x2; cline.p2.y = line->y2;
	o = g2d_project_pt_cline(vpt, &cline);
	if ((o < 0.0) || (o > 1.0))
		return -1;
	vcr = g2d_cline_offs(&cline, o);

	dx = vcr.x - vpt.x;
	dy = vcr.y - vpt.y;
	sa = atan2(dy, dx);
	if (sa < 0)
		sa += GRBS_PI*2;

	if (segi < 0) {
		int found = 0;

		for(segi = 0; segi < GRBS_MAX_SEG; segi++) {
			sentinel = gdl_first(&pt->arcs[segi]);
			if (sentinel == NULL) continue;
			if (grbs_angle_in_arc(sentinel->new_in_use ? sentinel->new_sa : sentinel->sa, sentinel->new_in_use ? sentinel->new_da : sentinel->da, sa, 0)) {
				found = 1;
				break;
			}
		}

		if (!found)
			return -1;
	}

	if (g2d_side_cline_pt(&cline, vpt) < 0)
		da = 0.0000001;
	else
		da = -0.0000001;

	r = sqrt(dx*dx + dy*dy);
	new = grbs_arc_new(grbs, pt, segi, r, sa, da);
	new->parent_pt = pt;
	new->in_use = 1;
	new->wrong_r = 1;
	new->min_r = r;
	new->copper = tn->copper;
	new->clearance = tn->clearance;

	/* if we are attaching to the bottom existing orbit, we may need to extend the sentinel to include us */
	if ((sentinel != NULL) && (new->link_point.prev == sentinel)) {
		if (sentinel->new_in_use) {
			extend_sentinel(sentinel, sa);
			extend_sentinel(sentinel, sa+da);
		}
		else {
			/* there was no ->new around this point, the sentinel is only us */
			sentinel->sa = sa;
			sentinel->da = da;
		}
	}

	grbs_line_del(grbs, line);
	gdl_insert_after(&tn->arcs, prev, new, link_2net);

	grbs_line_create(grbs, new);
	grbs_line_create(grbs, next);

	under = new->link_point.prev;
	assert(under != NULL);
	if (under->new_in_use)
		bump_seg_radii(grbs, new, 0, under->new_r + tn->copper, tn->clearance, 0, 0, 0);
	/* do not update original ->r - realize_() will bump radii as needed */

	if (grbs->auto_created_arc != NULL)
		grbs->auto_created_arc(grbs, tn, new);

	return 0;
}


int grbs_force_detach(grbs_t *grbs, grbs_arc_t *arc, int draw_line)
{
	grbs_arc_t *prev = arc->link_2net.prev, *next = arc->link_2net.next;
	int segi = arc->segi;
	grbs_point_t *pt = arc->parent_pt;

	if ((prev == NULL) || (next == NULL))
		return -1;

	if (arc->new_in_use || (arc->link_point.prev == NULL)) {
		grbs_2net_t *tn = grbs_arc_parent_2net(arc);

		grbs_line_del(grbs, arc->sline);
		grbs_line_del(grbs, arc->eline);
		gdl_remove(&tn->arcs, arc, link_2net);
		arc->in_use = 0; /* do not remove sentinel or something that's being routed above */
	}
	else
		grbs_del_arc(grbs, arc);

	grbs_clean_unused_sentinel_seg(grbs, pt, segi, 1);

	if (draw_line)
		grbs_line_create(grbs, next);
	return 0;
}

