/*
    libcdtr - Constrained Delaunay Triangulation using incremental algorithm

    Copyright (C) 2018,2021 Wojciech Krutnik

    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

    Constrained Delaunay Triangulation using incremental algorithm, as described in:
      Yizhi Lu and Wayne Wei-Ming Dai, "A numerical stable algorithm for constructing
      constrained Delaunay triangulation and application to multichip module layout,"
      China., 1991 International Conference on Circuits and Systems, Shenzhen, 1991,
      pp. 644-647 vol.2.
 */

#include <assert.h>
#include <stdio.h>
#include <math.h>

#include "cdt.h"

#define DEBUG

#define LEFTPOINT(p1, p2) ((p1)->pos.x < (p2)->pos.x || ((p1)->pos.x == (p2)->pos.x && (p1)->pos.y > (p2)->pos.y))

/* If cdt_precision is defined, round any new point coordinate using it to
   avoid "points too close" floating point errors */
#ifdef cdt_precision
#	define CROUND(c) (round((c) * (cdt_precision))/(cdt_precision))
#else
#	define CROUND(c) (c)
#endif

static point_t *new_point(cdt_t *cdt, pos_t pos)
{
	point_t *p = *vtpoint_alloc_append(&cdt->points, 1);
	p->pos = pos;
	return p;
}

static edge_t *new_edge(cdt_t *cdt, point_t *p1, point_t *p2, int constrain)
{
	edge_t *e;
	assert(p1->pos.x != p2->pos.x || p1->pos.y != p2->pos.y);
	e = *vtedge_alloc_append(&cdt->edges, 1);
	/* always orient the edge to the right (or down when x1==x2; requires epsilon check?) */
	if (LEFTPOINT(p1, p2)) {
		e->endp[0] = p1;
		e->endp[1] = p2;
	}
	else {
		e->endp[0] = p2;
		e->endp[1] = p1;
	}
	e->is_constrained = constrain;
	p1->adj_edges = edgelist_prepend(p1->adj_edges, &e);
	p2->adj_edges = edgelist_prepend(p2->adj_edges, &e);
	return e;
}

edge_t *cdt_new_edge_(cdt_t *cdt, point_t *p1, point_t *p2, int constrain)
{
	return new_edge(cdt, p1, p2, constrain);
}

edge_t *get_edge_from_points(point_t *p1, point_t *p2)
{
	EDGELIST_FOREACH(e1, p1->adj_edges)
		EDGELIST_FOREACH(e2, p2->adj_edges)
			if (e1 == e2)
				return e1;
		POINTLIST_FOREACH_END();
	EDGELIST_FOREACH_END();
	return NULL;
}

static void order_triangle_points_ccw(point_t **p1, point_t **p2, point_t **p3)
{
	pointlist_node_t *points = NULL;
	points = pointlist_prepend(points, p1);
	points = pointlist_prepend(points, p2);
	points = pointlist_prepend(points, p3);

	/* first point */
	*p1 = LEFTPOINT(*p1, *p2) ? *p1 : *p2;
	*p1 = LEFTPOINT(*p1, *p3) ? *p1 : *p3;
	points = pointlist_remove_item(points, p1);

	/* two other points */
	if (ORIENT_CCW(*p1, points->item, points->next->item)) {
		*p2 = points->item;
		*p3 = points->next->item;
	} else {
		*p2 = points->next->item;
		*p3 = points->item;
	}
	points = pointlist_remove_front(points);
	points = pointlist_remove_front(points);
}

static triangle_t *new_triangle(cdt_t *cdt, point_t *p1, point_t *p2, point_t *p3)
{
	edge_t *e1, *e2, *e3;
	triangle_t *t = *vttriangle_alloc_append(&cdt->triangles, 1);

	assert(!ORIENT_COLLINEAR(p1, p2, p3));	/* points cannot be colinear */

	order_triangle_points_ccw(&p1, &p2, &p3);
	t->p[0] = p1;
	t->p[1] = p2;
	t->p[2] = p3;
	p1->adj_triangles = trianglelist_prepend(p1->adj_triangles, &t);
	p2->adj_triangles = trianglelist_prepend(p2->adj_triangles, &t);
	p3->adj_triangles = trianglelist_prepend(p3->adj_triangles, &t);

	e1 = get_edge_from_points(p1, p2);
	e2 = get_edge_from_points(p2, p3);
	e3 = get_edge_from_points(p3, p1);
	assert(e1 != NULL && e2 != NULL && e3 != NULL);
	t->e[0] = e1;
	t->e[1] = e2;
	t->e[2] = e3;

#define connect_adjacent_triangle(_t_, _edge_num_, _e_) \
	if ((_e_)->adj_t[0] == NULL && (_e_)->adj_t[1] == NULL) \
		(_e_)->adj_t[0] = (_t_); \
	else { \
		int i = (_e_)->adj_t[0] == NULL ? 0 : 1; \
		(_e_)->adj_t[i] = (_t_); \
		(_t_)->adj_t[(_edge_num_)] = (_e_)->adj_t[i^1]; \
		if ((_t_)->adj_t[(_edge_num_)] != NULL) { \
			for (i = 0; i < 3; i++) { \
				if ((_t_)->adj_t[(_edge_num_)]->e[i] == (_e_)) \
					(_t_)->adj_t[(_edge_num_)]->adj_t[i] = (_t_); \
			} \
		} \
	}

	connect_adjacent_triangle(t, 0, e1);
	connect_adjacent_triangle(t, 1, e2);
	connect_adjacent_triangle(t, 2, e3);
#undef connect_adjacent_triangle

	return t;
}

triangle_t *cdt_new_triangle_(cdt_t *cdt, point_t *p1, point_t *p2, point_t *p3)
{
	return new_triangle(cdt, p1, p2, p3);
}


static void remove_triangle(cdt_t *cdt, triangle_t *t)
{
	int i, j;

	for (i = 0; i < 3; i++) {
		/* disconnect adjacent points */
		t->p[i]->adj_triangles = trianglelist_remove_item(t->p[i]->adj_triangles, &t);
		/* disconnect adjacent edges */
		for (j = 0; j < 2; j++)
			if (t->e[i]->adj_t[j] == t)
				t->e[i]->adj_t[j] = NULL;
		/* disconnect adjacent triangles */
		for (j = 0; j < 3; j++) {
			triangle_t *adj_t = t->adj_t[i];
			if (adj_t != NULL && adj_t->adj_t[j] == t)
				adj_t->adj_t[j] = NULL;
		}
	}

	/* remove triangle */
	for(i = 0; i < vttriangle_len(&cdt->triangles); i++) {
		if (cdt->triangles.array[i] == t) {
			vttriangle_remove(&cdt->triangles, i, 1);
			break;
		}
	}
}

static void remove_edge(cdt_t *cdt, edge_t *e)
{
	int i;

	assert(e != NULL);

	/* disconnect adjacent triangles */
	for (i = 0; i < 2; i++)
		if (e->adj_t[i])
			remove_triangle(cdt, e->adj_t[i]);
	/* disconnect endpoints */
	for (i = 0; i < 2; i++)
		e->endp[i]->adj_edges = edgelist_remove_item(e->endp[i]->adj_edges, &e);

	/* remove edge */
	for(i = 0; i < vtedge_len(&cdt->edges); i++) {
		if (cdt->edges.array[i] == e) {
			vtedge_remove(&cdt->edges, i, 1);
			break;
		}
	}
}

int cdt_is_point_in_circumcircle(point_t *p, triangle_t *t)
{
	double x1 = t->p[0]->pos.x, x2 = t->p[1]->pos.x, x3 = t->p[2]->pos.x;
	double y1 = t->p[0]->pos.y, y2 = t->p[1]->pos.y, y3 = t->p[2]->pos.y;
	double d1, d2, d3, d4;
	double n1, n2, n3;
	double px = p->pos.x, py = p->pos.y;

	n1 = (x1 * x1) + (y1 * y1);
	n2 = (x2 * x2) + (y2 * y2);
	n3 = (x3 * x3) + (y3 * y3);

	d1 = (x1 * y2) + (x2 * y3) + (x3 * y1) - (x3 * y2) - (x1 * y3) - (x2 * y1);
	d2 = (n1 * y2) + (n2 * y3) + (n3 * y1) - (n3 * y2) - (n1 * y3) - (n2 * y1);
	d3 = (n1 * x2) + (n2 * x3) + (n3 * x1) - (n3 * x2) - (n1 * x3) - (n2 * x1);
	d4 = (n1 * x2 * y3) + (n2 * x3 * y1) + (n3 * x1 * y2) - (n3 * x2 * y1) - (n1 * x3 * y2) - (n2 * x1 * y3);

	return CROUND(d1 * ( (px * (px*d1 - d2)) + (py * (py*d1 + d3)) - d4 )) < 0 ? 1 : 0;
}

static void init_bbox(cdt_t *cdt, coord_t bbox_x1, coord_t bbox_y1, coord_t bbox_x2, coord_t bbox_y2)
{
	pos_t bbox;
	point_t *p_tl, *p_tr, *p_bl, *p_br;

	bbox_x1 = CROUND(bbox_x1);
	bbox_y1 = CROUND(bbox_y1);
	bbox_x2 = CROUND(bbox_x2);
	bbox_y2 = CROUND(bbox_y2);

	bbox.x = bbox_x1;
	bbox.y = bbox_y1;
	cdt->bbox_tl = bbox;
	p_bl = new_point(cdt, bbox);
	bbox.x = bbox_x2;
	bbox.y = bbox_y1;
	p_br = new_point(cdt, bbox);
	bbox.x = bbox_x1;
	bbox.y = bbox_y2;
	p_tl = new_point(cdt, bbox);
	bbox.x = bbox_x2;
	bbox.y = bbox_y2;
	p_tr = new_point(cdt, bbox);
	cdt->bbox_br = bbox;

	new_edge(cdt, p_tl, p_bl, 1);
	new_edge(cdt, p_bl, p_br, 1);
	new_edge(cdt, p_br, p_tr, 1);
	new_edge(cdt, p_tr, p_tl, 1);
	new_edge(cdt, p_bl, p_tr, 0);	/* diagonal */

	new_triangle(cdt, p_bl, p_br, p_tr);
	new_triangle(cdt, p_tl, p_bl, p_tr);
}

void cdt_init_(cdt_t *cdt)
{
	cdt->points.elem_constructor = vtpoint_constructor;
	cdt->points.elem_destructor = vtpoint_destructor;
	cdt->points.elem_copy = NULL;
	vtpoint_init(&cdt->points);
	cdt->edges.elem_constructor = vtedge_constructor;
	cdt->edges.elem_destructor = vtedge_destructor;
	cdt->edges.elem_copy = NULL;
	vtedge_init(&cdt->edges);
	cdt->triangles.elem_constructor = vttriangle_constructor;
	cdt->triangles.elem_destructor = vttriangle_destructor;
	cdt->triangles.elem_copy = NULL;
	vttriangle_init(&cdt->triangles);
}

void cdt_init(cdt_t *cdt, coord_t bbox_x1, coord_t bbox_y1, coord_t bbox_x2, coord_t bbox_y2)
{
	cdt_init_(cdt);
	init_bbox(cdt, bbox_x1, bbox_y1, bbox_x2, bbox_y2);
}


void cdt_free(cdt_t *cdt)
{
	vttriangle_uninit(&cdt->triangles);
	vtedge_uninit(&cdt->edges);
	VTPOINT_FOREACH(p, &cdt->points)
		trianglelist_free(p->adj_triangles);
		edgelist_free(p->adj_edges);
	VTPOINT_FOREACH_END();
	vtpoint_uninit(&cdt->points);
}

static int is_point_in_triangle(point_t *p, triangle_t *t)
{
	return ORIENT_CCW_CL(t->p[0], t->p[1], p) && ORIENT_CCW_CL(t->p[1], t->p[2], p) && ORIENT_CCW_CL(t->p[2], t->p[0], p);
}

typedef struct {
	edgelist_node_t *border_edges;
	edgelist_node_t *edges_to_remove;
} retriangulation_region_t;

static void check_adjacent_triangle(triangle_t *adj_t, edge_t *adj_e, point_t *new_p, retriangulation_region_t *region)
{
	int i;

	assert(adj_t != NULL && adj_e != NULL);

	if (cdt_is_point_in_circumcircle(new_p, adj_t)) {
		region->edges_to_remove = edgelist_prepend(region->edges_to_remove, &adj_e);
		region->border_edges = edgelist_remove_item(region->border_edges, &adj_e);
		for (i = 0; i < 3; i++) {
			if (adj_t->e[i] == adj_e)
				continue;
			else {
				edge_t *next_adj_e = adj_t->e[i];
				triangle_t *next_adj_t = adj_t->adj_t[i];

				region->border_edges = edgelist_prepend(region->border_edges, &next_adj_e);
				if (!next_adj_e->is_constrained)
					check_adjacent_triangle(next_adj_t, next_adj_e, new_p, region); /* recursive call */
			}
		}
	}
}

static pointlist_node_t *order_edges_adjacently(edgelist_node_t *edges)
{
	pointlist_node_t *plist_ordered = NULL;
	edge_t *e1 = edges->item;
	point_t *p = e1->endp[0];
	int i = 1;

	plist_ordered = pointlist_prepend(plist_ordered, &p);
	edges = edgelist_remove_front(edges);

	while (edges != NULL) {
		p = e1->endp[i];
		EDGELIST_FOREACH(e2, edges)
			if (e2->endp[0] == p || e2->endp[1] == p) {
				plist_ordered = pointlist_prepend(plist_ordered, &p);
				edges = edgelist_remove(edges, _node_);
				i = e2->endp[0] == p ? 1 : 0;
				e1 = e2;
				break;
			}
		EDGELIST_FOREACH_END();
	}

	return plist_ordered;
}

void cdt_triangulate_polygon(cdt_t *cdt, pointlist_node_t *polygon)
{
	pointlist_node_t *current_point_node;
	point_t *p[3];
	int i;

	assert(pointlist_length(polygon) >= 3);

	current_point_node = polygon;
	while (pointlist_length(polygon) > 3) {
		triangle_t candidate_t;
		edge_t candidate_e;
		pointlist_node_t *pnode;

		candidate_t.p[0] = current_point_node->item;
		candidate_e.endp[0] = candidate_t.p[0];
		if (current_point_node->next == NULL)
			current_point_node = polygon; /* wrap around */
		else
			current_point_node = current_point_node->next;
		candidate_t.p[1] = current_point_node->item;
		if (current_point_node->next == NULL)
			candidate_t.p[2] = polygon->item; /* wrap around */
		else
			candidate_t.p[2] = current_point_node->next->item;
		candidate_e.endp[1] = candidate_t.p[2];

		if (ORIENT_COLLINEAR(candidate_t.p[0], candidate_t.p[1], candidate_t.p[2]))
			goto skip;

		/* case 1: another point of the polygon violates the circle criterion */
		POINTLIST_FOREACH(p, polygon)
			if (p != candidate_t.p[0] && p != candidate_t.p[1] && p != candidate_t.p[2])
				if (cdt_is_point_in_circumcircle(p, &candidate_t))
					goto skip;
		POINTLIST_FOREACH_END();

		/* case 2: edge to be added already exists */
		EDGELIST_FOREACH(e1, candidate_e.endp[0]->adj_edges)
			EDGELIST_FOREACH(e2, candidate_e.endp[1]->adj_edges)
				if (e1 == e2)
					goto skip;
			EDGELIST_FOREACH_END();
		EDGELIST_FOREACH_END();

		/* case 3: edge to be added intersects an existing edge */
		/* case 4: a point adjacent to the current_point is in the candidate triangle */
		EDGELIST_FOREACH(e, candidate_t.p[1]->adj_edges)
			if (e != get_edge_from_points(candidate_t.p[0], candidate_t.p[1])
					&& e != get_edge_from_points(candidate_t.p[1], candidate_t.p[2])) {
				triangle_t ord_t;
				if (LINES_INTERSECT(candidate_e.endp[0], candidate_e.endp[1], e->endp[0], e->endp[1]))
					goto skip;
				ord_t.p[0] = candidate_t.p[0];
				ord_t.p[1] = candidate_t.p[1];
				ord_t.p[2] = candidate_t.p[2];
				order_triangle_points_ccw(&ord_t.p[0], &ord_t.p[1], &ord_t.p[2]);
				if (is_point_in_triangle(e->endp[0] != candidate_t.p[1] ? e->endp[0] : e->endp[1], &ord_t))
					goto skip;
			}
		EDGELIST_FOREACH_END();

		new_edge(cdt, candidate_e.endp[0], candidate_e.endp[1], 0);
		new_triangle(cdt, candidate_t.p[0], candidate_t.p[1], candidate_t.p[2]);

		/* update polygon: remove the point now covered by the new edge */
		if (current_point_node->next == NULL)
			pnode = polygon; /* wrap around */
		else
			pnode = current_point_node->next;
		polygon = pointlist_remove(polygon, current_point_node);
		current_point_node = pnode;

skip:
		continue;
	}

	/* create triangle from the remaining edges */
	for (i = 0; i < 3; i++) {
		p[i] = polygon->item;
		polygon = pointlist_remove_front(polygon);
	}
	new_triangle(cdt, p[0], p[1], p[2]);
}

static int insert_point_(cdt_t *cdt, point_t *new_p)
{
	triangle_t *enclosing_triangle = NULL;
	retriangulation_region_t region = {NULL, NULL};
	pointlist_node_t *points_to_attach, *prev_point_node;
	edge_t *crossing_edge = NULL;
	point_t *split_edge_endp[2] = {NULL, NULL};
	void *split_edge_user_data;
	int i, j;

	/* find enclosing triangle */
	VTTRIANGLE_FOREACH(t, &cdt->triangles)
		if (is_point_in_triangle(new_p, t)) {
			enclosing_triangle = t;
			break;
		}
	VTTRIANGLE_FOREACH_END();
	if (enclosing_triangle == NULL)
		return -1;

	/* check if the new point is on a constrained edge and unconstrain it
	 * (it will be removed, and then created as 2 segments connecting the new point)
	 */
	for (i = 0; i < 3; i++) {
		edge_t *e = enclosing_triangle->e[i];
		if (ORIENT_COLLINEAR(e->endp[0], e->endp[1], new_p)) {
			crossing_edge = e;
			if (crossing_edge->is_constrained) {
				for (j = 0; j < 2; j++)
					split_edge_endp[j] = enclosing_triangle->e[i]->endp[j];
				if (cdt->ev_split_constrained_edge_pre != NULL)
					cdt->ev_split_constrained_edge_pre(cdt, crossing_edge, new_p);
				split_edge_user_data = crossing_edge->data;
				crossing_edge->is_constrained = 0;
			}
			break;
		}
	}

	/* remove invalid edges and attach enclosing points */
	for (i = 0; i < 3; i++) {
		edge_t *adj_e = enclosing_triangle->e[i];
		triangle_t *adj_t = enclosing_triangle->adj_t[i];

		region.border_edges = edgelist_prepend(region.border_edges, &adj_e);
		if (!adj_e->is_constrained && adj_t != NULL)
			check_adjacent_triangle(adj_t, adj_e, new_p, &region);
	}
	remove_triangle(cdt, enclosing_triangle);
	EDGELIST_FOREACH(e, region.edges_to_remove)
		remove_edge(cdt, e);
	EDGELIST_FOREACH_END();

	points_to_attach = order_edges_adjacently(region.border_edges);
	prev_point_node = points_to_attach;
	new_edge(cdt, points_to_attach->item, new_p, 0);
	POINTLIST_FOREACH(p, points_to_attach->next)
		point_t *prev_p = prev_point_node->item;
		new_edge(cdt, p, new_p, 0);
		if (!ORIENT_COLLINEAR(prev_p, p, new_p))
			new_triangle(cdt, prev_p, p, new_p);
		prev_point_node = _node_;
	POINTLIST_FOREACH_END();
	if (!ORIENT_COLLINEAR(prev_point_node->item, points_to_attach->item, new_p))
		new_triangle(cdt, prev_point_node->item, points_to_attach->item, new_p);
	pointlist_free(points_to_attach);

	/* delete the crossing edge if it wasn't before (point on a boundary edge case) */
	if (crossing_edge != NULL && edgelist_find(region.edges_to_remove, &crossing_edge) == NULL)
		remove_edge(cdt, crossing_edge);
	edgelist_free(region.edges_to_remove);

	/* recreate, now splitted, constrained edge */
	if (split_edge_endp[0] != NULL) {
		edge_t *e1 = cdt_insert_constrained_edge(cdt, split_edge_endp[0], new_p);
		edge_t *e2 = cdt_insert_constrained_edge(cdt, new_p, split_edge_endp[1]);
		e1->data = e2->data = split_edge_user_data;
		if (cdt->ev_split_constrained_edge_post != NULL)
			cdt->ev_split_constrained_edge_post(cdt, e1, e2, new_p);
	}
	return 0;
}

point_t *cdt_insert_point(cdt_t *cdt, coord_t x, coord_t y)
{
	pos_t pos;
	point_t *new_p;

	pos.x = CROUND(x);
	pos.y = CROUND(y);
	VTPOINT_FOREACH(p, &cdt->points)
		if (p->pos.x == pos.x && p->pos.y == pos.y)	/* point in the same pos already exists */
			return p;
	VTPOINT_FOREACH_END();

	new_p = new_point(cdt, pos);
	if (insert_point_(cdt, new_p) != 0) {
		cdt->points.used--; /* new_point() allocated one slot at the end */
		return NULL;
	}

	return new_p;
}

void cdt_delete_point(cdt_t *cdt, point_t *p)
{
	edgelist_node_t *polygon_edges = NULL;
	pointlist_node_t *polygon_points;
	int i;

	/* find opposite edges of adjacent triangles and add them to the polygon */
	TRIANGLELIST_FOREACH(t, p->adj_triangles)
		i = 0;
		while(i < 3) {
			edge_t *edge_of_triangle = t->e[i];
			EDGELIST_FOREACH(edge_of_point, p->adj_edges)
				if (edge_of_point == edge_of_triangle)
					goto next_i;
			EDGELIST_FOREACH_END();
			polygon_edges = edgelist_prepend(polygon_edges, &edge_of_triangle);
			break;
next_i:
			i++;
		}
	TRIANGLELIST_FOREACH_END();

	/* remove adjacent edges */
	EDGELIST_FOREACH(e, p->adj_edges)
		assert(e->is_constrained == 0);
		_node_ = _node_->next;
		remove_edge(cdt, e);
		continue;
	EDGELIST_FOREACH_END();

	/* remove point */
	for(i = 0; i < vtpoint_len(&cdt->points); i++) {
		if (cdt->points.array[i] == p) {
			vtpoint_remove(&cdt->points, i, 1);
			break;
		}
	}

	polygon_points = order_edges_adjacently(polygon_edges);
	cdt_triangulate_polygon(cdt, polygon_points);
}

static trianglelist_node_t *triangles_intersecting_line(point_t *p1, point_t *p2)
{
	trianglelist_node_t *triangles = NULL;
	triangle_t *next_t = NULL;
	edge_t *adj_e;
	int i;

	/* find first triangle */
	TRIANGLELIST_FOREACH(t, p1->adj_triangles)
		for (i = 0; i < 3; i++)
			if (t->e[i]->endp[0] != p1 && t->e[i]->endp[1] != p1) /* opposite edge */
				if (LINES_INTERSECT(p1, p2, t->e[i]->endp[0], t->e[i]->endp[1])) {
					adj_e = t->e[i];
					next_t = adj_e->adj_t[0] != t ? adj_e->adj_t[0] : adj_e->adj_t[1];
					triangles = trianglelist_prepend(triangles, &t);
					goto first_triangle_found;
				}
	TRIANGLELIST_FOREACH_END();

first_triangle_found:
	assert(next_t != NULL);

	/* follow the path */
	while (next_t->p[0] != p2 && next_t->p[1] != p2 && next_t->p[2] != p2) {
		triangle_t *t = next_t;
		for (i = 0; i < 3; i++)
			if (t->e[i] != adj_e && LINES_INTERSECT(p1, p2, t->e[i]->endp[0], t->e[i]->endp[1])) {
				adj_e = t->e[i];
				next_t = adj_e->adj_t[0] != t ? adj_e->adj_t[0] : adj_e->adj_t[1];
				triangles = trianglelist_prepend(triangles, &t);
				break;
			}
	}

	triangles = trianglelist_prepend(triangles, &next_t);
	return triangles;
}

static void insert_constrained_edge_(cdt_t *cdt, point_t *p1, point_t *p2, pointlist_node_t **left_poly, pointlist_node_t **right_poly)
{
	edge_t *e;
	triangle_t *t;
	trianglelist_node_t *triangles;
	pointlist_node_t *left_polygon = NULL, *right_polygon = NULL;
	int i;

	/* find intersecting edges and remove them */
	triangles = triangles_intersecting_line(p1, p2);
	t = triangles->item;
	triangles = trianglelist_remove_front(triangles);
	left_polygon = pointlist_prepend(left_polygon, &p2);	/* triangle list begins from p2 */
	right_polygon = pointlist_prepend(right_polygon, &p2);
	for (i = 0; i < 3; i++)
		if (t->p[i] != p2) {
			if (ORIENT_CCW(p1, p2, t->p[i]))
				left_polygon = pointlist_prepend(left_polygon, &t->p[i]);
			else
				right_polygon = pointlist_prepend(right_polygon, &t->p[i]);
		}

	while (triangles->next != NULL) {
		t = triangles->item;
		triangles = trianglelist_remove_front(triangles);
		e = get_edge_from_points(left_polygon->item, right_polygon->item);
		for (i = 0; i < 3; i++)
			if (t->p[i] != left_polygon->item && t->p[i] != right_polygon->item) {
				if (ORIENT_CCW(p1, p2, t->p[i]))
					left_polygon = pointlist_prepend(left_polygon, &t->p[i]);
				else
					right_polygon = pointlist_prepend(right_polygon, &t->p[i]);
				break;
			}
		remove_edge(cdt, e);
	}
	triangles = trianglelist_remove_front(triangles);
	remove_edge(cdt, get_edge_from_points(left_polygon->item, right_polygon->item));
	left_polygon = pointlist_prepend(left_polygon, &p1);
	right_polygon = pointlist_prepend(right_polygon, &p1);
	*left_poly = left_polygon;
	*right_poly = right_polygon;
}

edge_t *cdt_insert_constrained_edge(cdt_t *cdt, point_t *p1, point_t *p2)
{
	edge_t *e;
	pointlist_node_t *left_polygon, *right_polygon;

	/* edge already exists - just constrain it */
	e = get_edge_from_points(p1, p2);
	if (e != NULL) {
		e->is_constrained = 1;
		return e;
	}

	insert_constrained_edge_(cdt, p1, p2, &left_polygon, &right_polygon);

	/* add new edge */
	e = new_edge(cdt, p1, p2, 1);

	/* triangulate the created polygons */
	cdt_triangulate_polygon(cdt, left_polygon);
	cdt_triangulate_polygon(cdt, right_polygon);

	return e;
}

void cdt_delete_constrained_edge(cdt_t *cdt, edge_t *edge)
{
	pointlist_node_t *polygon = NULL;
	edgelist_node_t *border_edges = NULL;
	edgelist_node_t *constrained_edges_within_scope = NULL;
	pointlist_node_t *isolated_points = NULL;
	int i, j;

	assert(edge->is_constrained);

	/* initial polygon */
	polygon = pointlist_prepend(polygon, &edge->endp[0]);
	polygon = pointlist_prepend(polygon, &edge->endp[1]);
	for (i = 0; i < 2; i++) {
		triangle_t *t = edge->adj_t[i];
		for(j = 0; j < 3; j++) {
			if (t->p[j] != edge->endp[0] && t->p[j] != edge->endp[1])
				polygon = pointlist_prepend(polygon, &edge->adj_t[i]->p[j]);
			if (t->e[j] != edge)
				border_edges = edgelist_prepend(border_edges, &t->e[j]);
		}
	}
	remove_edge(cdt, edge);

	/* find invalid edges and remove them */
	EDGELIST_FOREACH(e, border_edges)
		triangle_t *t = e->adj_t[0] != NULL ? e->adj_t[0] : e->adj_t[1];
		edgelist_node_t *e_node = _node_;
		int invalid_edge = 0;
		if (t != NULL) {
			POINTLIST_FOREACH(p, polygon)
				if (p != t->p[0] && p != t->p[1] && p != t->p[2] && cdt_is_point_in_circumcircle(p, t)) {
					for (i = 0; i < 3; i++) {
						if (t->p[i] != e->endp[0] && t->p[i] != e->endp[1])
							polygon = pointlist_prepend(polygon, &t->p[i]);
						if (t->e[i] != e)
							border_edges = edgelist_prepend(border_edges, &t->e[i]);
					}
					if (e->is_constrained) {	/* don't remove constrained edges - only detach them */
						constrained_edges_within_scope = edgelist_prepend(constrained_edges_within_scope, &e);
						for (i = 0; i < 2; i++)
							e->endp[i]->adj_edges = edgelist_remove_item(e->endp[i]->adj_edges, &e);
						remove_triangle(cdt, t);
					}
					else
						remove_edge(cdt, e);
					border_edges = edgelist_remove(border_edges, e_node);
					invalid_edge = 1;
					break;
				}
			POINTLIST_FOREACH_END();
		}
		else { /* triangle beyond a border edge was removed earlier - the edge should be removed too */
			point_t *endp[2];
			endp[0] = e->endp[0];
			endp[1] = e->endp[1];
			for (i = 0; i < 2; i++) /* check if the edge is not bbox of the triangulation */
				if ((e->endp[i]->pos.x == cdt->bbox_tl.x && e->endp[i]->pos.y == cdt->bbox_tl.y)
						|| (e->endp[i]->pos.x == cdt->bbox_br.x && e->endp[i]->pos.y == cdt->bbox_br.y))
					goto next_edge;
			border_edges = edgelist_remove(border_edges, e_node);
			border_edges = edgelist_remove_item(border_edges, &e);
			invalid_edge = 1; /* TODO: the loop doesn't have to start from the beginning, but border removal should preserve current node */
			if (e->is_constrained) {
				constrained_edges_within_scope = edgelist_prepend(constrained_edges_within_scope, &e);
				for (i = 0; i < 2; i++)
					endp[i]->adj_edges = edgelist_remove_item(endp[i]->adj_edges, &e); /* detach the edge */
			}
			else
				remove_edge(cdt, e);
			for (i = 0; i < 2; i++) {
				if (endp[i]->adj_edges == NULL) { /* isolated point */
					polygon = pointlist_remove_item(polygon, &endp[i]);
					isolated_points = pointlist_prepend(isolated_points, &endp[i]);
				}
			}
		}
		if (invalid_edge) {
			_node_ = border_edges;	/* start from the beginning (new point to check was added) */
			continue;
		}
next_edge:
	EDGELIST_FOREACH_END();
	pointlist_free(polygon);

	/* triangulate the resultant polygon */
	polygon = order_edges_adjacently(border_edges);
	cdt_triangulate_polygon(cdt, polygon);

	/* reattach isolated points and constrained edges */
	POINTLIST_FOREACH(p, isolated_points)
		insert_point_(cdt, p);
	POINTLIST_FOREACH_END();
	pointlist_free(isolated_points);
	EDGELIST_FOREACH(e, constrained_edges_within_scope)
		pointlist_node_t *left_polygon, *right_polygon;
		insert_constrained_edge_(cdt, e->endp[0], e->endp[1], &left_polygon, &right_polygon);
		for (i = 0; i < 2; i++)
			e->endp[i]->adj_edges = edgelist_prepend(e->endp[i]->adj_edges, &e); /* reattach the edge to the endpoints */
		cdt_triangulate_polygon(cdt, left_polygon);
		cdt_triangulate_polygon(cdt, right_polygon);
	EDGELIST_FOREACH_END();
	edgelist_free(constrained_edges_within_scope);
}
