
/****************************************************************
 *
 * MODULE:     v.edit
 *
 * PURPOSE:    Editing vector map.
 *
 * AUTHOR(S):  GRASS Development Team
 *             Wolf Bergenheim, Jachym Cepicky, Martin Landa
 *
 * COPYRIGHT:  (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.
 *
 * TODO:       3D support
 ****************************************************************/

#include <string.h>
#include "global.h"

#define BUFFSIZE 128

/**
   \brief Add new vector features to the vector map
   
   Input format is GRASS ASCII vector, the code adopted from v.in.ascii

   \param[in] ascii file containing definition of new vector features
   \param[in] Map vector map
   \param[out] List list of added features (if given)

   \return number of added features
*/
int asc_to_bin(FILE * ascii, struct Map_info *Map, struct ilist *List)
{
    char ctype;
    char buff[BUFFSIZE];
    double *xarray;
    double *yarray;
    double *zarray;
    double *x, *y, *z;
    int i, n_points, n_coors, n_cats;
    int type, newline;
    int alloc_points;
    int end_of_file;
    int catn;
    int cat;
    int nlines;

    struct line_pnts *Points;
    struct line_cats *Cats;

    nlines = 0;

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

    if (List) {
	Vect_reset_list(List);
    }

    end_of_file = 0;
    /*alloc_points     = 1000 ; */
    alloc_points = 1;
    xarray = (double *)G_calloc(alloc_points, sizeof(double));
    yarray = (double *)G_calloc(alloc_points, sizeof(double));
    zarray = (double *)G_calloc(alloc_points, sizeof(double));

    while (G_getl2(buff, BUFFSIZE - 1, ascii) != 0) {
	n_cats = 0;
	if (buff[0] == '\0') {
	    G_debug(3, "a2b: skipping blank line");
	    continue;
	}

	if (sscanf(buff, "%1c%d%d", &ctype, &n_coors, &n_cats) < 2 ||
	    n_coors < 0 || n_cats < 0) {
	    if (ctype == '#') {
		G_debug(2, "a2b: skipping commented line");
		continue;
	    }
	    G_warning(_("Error reading ASCII file: '%s'"), buff);
	    return -1;
	}
	if (ctype == '#') {
	    G_debug(2, "a2b: Skipping commented line");
	    continue;
	}

	switch (ctype) {
	case 'A':
	    type = GV_BOUNDARY;
	    break;
	case 'B':
	    type = GV_BOUNDARY;
	    break;
	case 'C':
	    type = GV_CENTROID;
	    break;
	case 'L':
	    type = GV_LINE;
	    break;
	case 'P':
	    type = GV_POINT;
	    break;
	case 'F':
	    type = GV_FACE;
	    break;
	case 'K':
	    type = GV_KERNEL;
	    break;
	case 'a':
	case 'b':
	case 'c':
	case 'l':
	case 'p':
	    type = 0;		/* dead -> ignore */
	    break;
	default:
	    G_warning(_("Error reading ASCII file: '%s'"), buff);
	    return -1;
	}
	G_debug(5, "feature type = %d", type);

	n_points = 0;
	x = xarray;
	y = yarray;
	z = zarray;

	/* Collect the points */
	for (i = 0; i < n_coors; i++) {
	    if (G_getl2(buff, BUFFSIZE - 1, ascii) == 0) {
		G_warning(_("End of ASCII file reached before end of coordinates"));
		return -1;
	    }
	    if (buff[0] == '\0') {
		G_debug(3, "a2b: skipping blank line while reading vertices");
		i--;
		continue;
	    }

	    *z = 0;
	    if (sscanf(buff, "%lf%lf%lf", x, y, z) < 2) {
		G_warning(_("Error reading ASCII file: '%s'"), buff);
		return -1;
	    }
	    G_debug(5, "coor in: %s -> x = %f y = %f z = %f", G_chop(buff),
		    *x, *y, *z);

	    n_points++;
	    x++;
	    y++;
	    z++;

	    if (n_points >= alloc_points) {
		alloc_points = n_points + 1000;
		xarray =
		    (double *)G_realloc((void *)xarray,
					alloc_points * sizeof(double));
		yarray =
		    (double *)G_realloc((void *)yarray,
					alloc_points * sizeof(double));
		zarray =
		    (double *)G_realloc((void *)zarray,
					alloc_points * sizeof(double));
		x = xarray + n_points;
		y = yarray + n_points;
		z = zarray + n_points;
	    }
	}

	/* Collect the cats */
	for (i = 0; i < n_cats; i++) {
	    if (G_getl2(buff, BUFFSIZE - 1, ascii) == 0) {
		G_warning(_("End of ascii file reached before end of categories"));
		return -1;
	    }
	    if (buff[0] == '\0') {
		G_debug(3,
			"a2b: skipping blank line while reading category info");
		i--;
		continue;
	    }

	    if (sscanf(buff, "%u%u", &catn, &cat) != 2) {
		G_warning(_("Error reading categories: '%s'"), buff);
		return -1;
	    }
	    Vect_cat_set(Cats, catn, cat);
	}

	/* Allocation is handled for line_pnts */
	if (0 >
	    Vect_copy_xyz_to_pnts(Points, xarray, yarray, zarray, n_points))
	    G_fatal_error(_("Out of memory"));

	if (type > 0) {
	    newline = Vect_write_line(Map, type, Points, Cats);
	    if (List) {
		Vect_list_append(List, newline);
	    }
	    nlines++;

	    Vect_reset_cats(Cats);
	}
    }

    return nlines;
}

/**
   \brief Read header of input file
   
   \param[in] dascii file containing definition of vector features to be added
   \param[in] Map vector map
   
   \return 0
*/
int read_head(FILE * dascii, struct Map_info *Map)
{
    char buff[1024];
    char *ptr;

    for (;;) {
	if (0 == G_getl2(buff, sizeof(buff) - 1, dascii))
	    return 0;

	/* Last line of header */
	if (strncmp(buff, "VERTI:", 6) == 0)
	    return 0;

	if (!(ptr = G_index(buff, ':')))
	    G_fatal_error(_("Unexpected data in vector head: '%s'"), buff);

	ptr++;			/* Search for the start of text */
	while (*ptr == ' ')
	    ptr++;

	if (strncmp(buff, "ORGANIZATION:", 12) == 0)
	    Vect_set_organization(Map, ptr);
	else if (strncmp(buff, "DIGIT DATE:", 11) == 0)
	    Vect_set_date(Map, ptr);
	else if (strncmp(buff, "DIGIT NAME:", 11) == 0)
	    Vect_set_person(Map, ptr);
	else if (strncmp(buff, "MAP NAME:", 9) == 0)
	    Vect_set_map_name(Map, ptr);
	else if (strncmp(buff, "MAP DATE:", 9) == 0)
	    Vect_set_map_date(Map, ptr);
	else if (strncmp(buff, "MAP SCALE:", 10) == 0)
	    Vect_set_scale(Map, atoi(ptr));
	else if (strncmp(buff, "OTHER INFO:", 11) == 0)
	    Vect_set_comment(Map, ptr);
	else if (strncmp(buff, "ZONE:", 5) == 0 ||
		 strncmp(buff, "UTM ZONE:", 9) == 0)
	    Vect_set_zone(Map, atoi(ptr));
	else if (strncmp(buff, "WEST EDGE:", 10) == 0) {
	}
	else if (strncmp(buff, "EAST EDGE:", 10) == 0) {
	}
	else if (strncmp(buff, "SOUTH EDGE:", 11) == 0) {
	}
	else if (strncmp(buff, "NORTH EDGE:", 11) == 0) {
	}
	else if (strncmp(buff, "MAP THRESH:", 11) == 0)
	    Vect_set_thresh(Map, atof(ptr));
	else {
	    G_warning(_("Unknown keyword '%s' in vector head"), buff);
	}
    }
    /* NOTREACHED */
}

/**
   \brief Close lines (boundaries)
   
   Using threshold distance (-1 for no limit)
   
   \param[in] Map vector map
   \param[in] ltype vector feature type (line | boundary)
   \param[in] thresh threshold distance

   \return number of modified features
*/
int close_lines(struct Map_info *Map, int ltype, double thresh)
{
    int nlines, line, type, nlines_modified, newline;
    int npoints;
    double *x, *y, *z;
    double dist;

    struct line_pnts *Points;
    struct line_cats *Cats;

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

    nlines_modified = 0;

    Vect_build_partial(Map, GV_BUILD_BASE);
    nlines = Vect_get_num_lines(Map);

    for (line = 1; line <= nlines; line++) {
	if (!Vect_line_alive(Map, line))
	    continue;

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

	if (!(type & ltype))
	    continue;

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

	dist = Vect_points_distance(x[npoints], y[npoints], z[npoints],
				    x[0], y[0], z[0], WITHOUT_Z);

	if (dist > 0 && (thresh < 0.0 || dist <= thresh)) {
	    Vect_line_delete_point(Points, npoints);
	    Vect_append_point(Points, x[0], y[0], z[0]);

	    newline = Vect_rewrite_line(Map, line, type, Points, Cats);
	    if (newline < 0) {
		G_warning(_("Unable to rewrite line %d"), line);
		return -1;
	    }
	    nlines_modified++;
	}
    }

    Vect_destroy_line_struct(Points);
    Vect_destroy_cats_struct(Cats);

    return nlines_modified;
}
