/*!
  \file lib/vector/vedit/vertex.c
  
  \brief Vedit library - vertex manipulation
  
  (C) 2006-2008 by the GRASS Development Team
  
  This program is free software under the GNU General Public License
  (>=v2).  Read the file COPYING that comes with GRASS for details.
  
  \author Jachym Cepicky <jachym.cepicky gmail.com>
  \author Martin Landa <landa.martin gmail.com>
*/

#include <grass/vedit.h>

/*!
  \brief Move all vertices in bounding box(es)
  
  \param Map pointer to Map_info
  \param BgMap, nbgmaps list of background vector maps for snapping
  \param List list of selected lines
  \param coord points location
  \param thresh_coords threshold value for selecting lines
  \param thresh_snap threshold value used for snapping
  \param move_x,move_y,move_z direction (move_z is used when map is 3D)
  \param move_first move only first vertex found in the bounding box
  \param snap snapping mode (see vedit.h)
  
  \return number of moved vertices
  \return -1 on error
*/
int Vedit_move_vertex(struct Map_info *Map, struct Map_info **BgMap,
		      int nbgmaps, struct ilist *List,
		      struct line_pnts *coord, double thresh_coords,
		      double thresh_snap, double move_x, double move_y,
		      double move_z, int move_first, int snap)
{
    int nvertices_moved, nlines_modified, nvertices_snapped;

    int i, j, k;
    int line, type, rewrite;
    int npoints;
    double east, north, dist;
    double *x, *y, *z;
    char *moved;

    struct line_pnts *Points, *Points_snap;
    struct line_cats *Cats;

    nlines_modified = 0;
    nvertices_moved = nvertices_snapped = 0;
    moved = NULL;

    Points = Vect_new_line_struct();
    Points_snap = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();

    for (i = 0; i < List->n_values; i++) {
	line = List->value[i];

	if (!Vect_line_alive(Map, line))
	    continue;

	type = Vect_read_line(Map, Points, Cats, line);

	if (!(type & GV_LINES))
	    continue;

	npoints = Points->n_points;
	x = Points->x;
	y = Points->y;
	z = Points->z;

	/* vertex moved 
	   0 not moved
	   1 moved
	   2 moved and snapped
	 */
	moved =
	    (char *)G_realloc((void *)moved, Points->n_points * sizeof(char));
	G_zero((void *)moved, Points->n_points * sizeof(char));

	rewrite = 0;
	for (j = 0; j < coord->n_points; j++) {
	    east = coord->x[j];
	    north = coord->y[j];

	    /* move all vertices in the bounding box */
	    for (k = 0; k < Points->n_points; k++) {
		if (moved[k] == 0) {
		    dist = Vect_points_distance(east, north, 0.0,
						x[k], y[k], z[k], WITHOUT_Z);
		    if (dist <= thresh_coords) {
			G_debug(3,
				"Vedit_move_vertex(): line=%d; x=%f, y=%f -> x=%f, y=%f",
				line, x[k], y[k], x[k] + move_x,
				y[k] + move_y);
			x[k] += move_x;
			y[k] += move_y;
			if (Vect_is_3d(Map))
			    z[k] += move_z;

			moved[k] = 1;

			G_debug(3, "Vedit_move_vertex(): line=%d, point=%d",
				line, k);

			if (snap != NO_SNAP) {
			    if (Vedit_snap_point
				(Map, line, &x[k], &y[k], &z[k], thresh_snap,
				 (snap == SNAPVERTEX) ? 1 : 0) == 0) {
				/* check also background maps */
				int bgi;

				for (bgi = 0; bgi < nbgmaps; bgi++) {
				    if (Vedit_snap_point
					(BgMap[bgi], line, &x[k], &y[k],
					 &z[k], thresh_snap,
					 (snap == SNAPVERTEX) ? 1 : 0))
					moved[k] = 2;
				    break;	/* snapped, don't continue */
				}
			    }
			    else {
				moved[k] = 2;
			    }
			}

			rewrite = 1;
			nvertices_moved++;

			if (move_first)
			    break;
		    }
		}
	    }			/* for each line vertex */

	    /* close line or boundary */
	    if ((type & GV_LINES) &&
		Vect_points_distance(x[0], y[0], z[0],
				     x[npoints - 1], y[npoints - 1],
				     z[npoints - 1],
				     WITHOUT_Z) <= thresh_snap) {

		if (moved[0] == 1) {	/* first node moved */
		    x[0] = x[npoints - 1];
		    y[0] = y[npoints - 1];
		    if (Vect_is_3d(Map))
			z[0] = z[npoints - 1];
		}
		else if (moved[npoints - 1] == 1) {	/* last node moved */
		    x[npoints - 1] = x[0];
		    y[npoints - 1] = y[0];
		    if (Vect_is_3d(Map))
			z[npoints - 1] = z[0];
		}
	    }
	}			/* for each coord */

	if (rewrite) {
	    if (Vect_rewrite_line(Map, line, type, Points, Cats) < 0) {
		return -1;
	    }

	    nlines_modified++;
	}
    }				/* for each selected line */

    /* destroy structures */
    Vect_destroy_line_struct(Points);
    Vect_destroy_line_struct(Points_snap);
    Vect_destroy_cats_struct(Cats);
    /*     G_free ((void *) moved); */

    return nvertices_moved;
}

/*!
  \brief Add new vertex to line
  
  Shape of line is not changed.
  
  \todo 3D
  
  \param Map pointer to Map_info
  \param List list of lines
  \param coord points location
  \param thresh find line in given threshold
  
  \return number of add vertices
  \return -1 on error
*/
int Vedit_add_vertex(struct Map_info *Map, struct ilist *List,
		     struct line_pnts *coord, double thresh)
{
    int i, j;
    int type, line, seg;
    int nlines_modified, nvertices_added, rewrite;
    double east, north, dist;
    double *x, *y, *z;
    double px, py;

    struct line_pnts *Points;
    struct line_cats *Cats;

    nlines_modified = 0;
    nvertices_added = 0;
    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();

    for (i = 0; i < List->n_values; i++) {
	line = List->value[i];

	if (!Vect_line_alive(Map, line))
	    continue;

	type = Vect_read_line(Map, Points, Cats, line);

	if (!(type & GV_LINES))
	    continue;

	G_debug(3, "Vedit_add_vertex(): line = %d, thresh = %f",
		line, thresh);

	x = Points->x;
	y = Points->y;
	z = Points->z;
	rewrite = FALSE;
	for (j = 0; j < coord->n_points; j++) {
	    east = coord->x[j];
	    north = coord->y[j];

	    seg = Vect_line_distance(Points, east, north, 0.0,	/* standpoint */
				     WITHOUT_Z, &px, &py, NULL,	/* point on line */
				     &dist,	/* distance to line */
				     NULL, NULL);

	    if (dist <= thresh &&
		Vect_points_distance(px, py, 0.0, x[seg], y[seg], z[seg],
				     WITHOUT_Z) > 0 &&
		Vect_points_distance(px, py, 0.0, x[seg - 1], y[seg - 1],
				     z[seg - 1], WITHOUT_Z) > 0) {
		/* add new vertex */
		Vect_line_insert_point(Points, seg, px, py, 0.0);
		G_debug(3, "Vedit_add_vertex(): line=%d; x=%f, y=%f, index=%d",
			line, px, py, seg);
		rewrite = TRUE;
		nvertices_added++;
	    }
	}			/* for each point */

	/* rewrite the line */
	if (rewrite) {
	    Vect_line_prune(Points);
	    if (Vect_rewrite_line(Map, line, type, Points, Cats) < 0) {
		return -1;
	    }

	    nlines_modified++;
	}
    }				/* for each line */

    /* destroy structures */
    Vect_destroy_line_struct(Points);
    Vect_destroy_cats_struct(Cats);

    return nvertices_added;
}

/*!
  \brief Remove vertex from line
  
  \todo 3D
  
  \param Map pointer to Map_info
  \param List list of selected lines
  \param coord points location
  \param thresh threshold value to find a line
  
  \return number of removed vertices
  \return -1 on error
*/
int Vedit_remove_vertex(struct Map_info *Map, struct ilist *List,
			struct line_pnts *coord, double thresh)
{
    int i, j, k;
    int type, line;
    int nvertices_removed, rewrite, nlines_modified;
    double east, north;
    double dist;
    double *x, *y, *z;

    struct line_pnts *Points;
    struct line_cats *Cats;

    nvertices_removed = nlines_modified = 0;

    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();

    for (i = 0; i < List->n_values; i++) {
	line = List->value[i];

	if (!Vect_line_alive(Map, line))
	    continue;

	type = Vect_read_line(Map, Points, Cats, line);

	if (!(type & GV_LINES))
	    continue;

	x = Points->x;
	y = Points->y;
	z = Points->z;
	rewrite = 0;
	for (j = 0; j < coord->n_points; j++) {
	    east = coord->x[j];
	    north = coord->y[j];

	    for (k = 0; k < Points->n_points; k++) {
		dist = Vect_points_distance(east, north, 0.0,
					    x[k], y[k], z[k], WITHOUT_Z);
		if (dist <= thresh) {
		    /* remove vertex */
		    Vect_line_delete_point(Points, k);
		    G_debug(3,
			    "Vedit_remove_vertex(): line=%d; x=%f, y=%f, index=%d",
			    line, x[k], y[k], k);
		    k--;
		    nvertices_removed++;
		    rewrite = 1;
		}
	    }			/* for each point */
	}			/* for each bounding box */

	if (rewrite) {
	    /* rewrite the line */
	    if (Vect_rewrite_line(Map, line, type, Points, Cats) < 0) {
		return -1;
	    }

	    nlines_modified++;
	}
    }				/* for each line */

    /* destroy structures */
    Vect_destroy_line_struct(Points);
    Vect_destroy_cats_struct(Cats);

    return nvertices_removed;
}
