/*!
   \file lib/vector/Vlib/write_sfa.c

   \brief Vector library - write vector feature - simple feature access (level 2)

   Higher level functions for reading/writing/manipulating vectors.

   See write_ogr.c (OGR interface) and write_pg.c (PostGIS interface)
   for imlementation issues.

   \todo SFA version of V2__delete_area_cats_from_cidx_nat()
   \todo function to delete corresponding entry in fidx
   \todo SFA version of V2__add_area_cats_to_cidx_nat
   \todo SFA version of V2__add_line_to_topo_nat

   (C) 2011-2012 by Martin Landa, and 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 Martin Landa <landa.martin gmail.com>
 */

#include <grass/vector.h>
#include <grass/glocale.h>

#include "local_proto.h"

#ifdef HAVE_POSTGRES
#include "pg_local_proto.h"
#endif

#if defined HAVE_OGR || defined HAVE_POSTGRES
static void V2__add_line_to_topo_sfa(struct Map_info *, int, const struct line_pnts *,
                                     const struct line_cats *);
#endif

/*!
  \brief Writes feature on level 2 (OGR/PostGIS interface, pseudo-topological level)

  \param Map pointer to Map_info structure
  \param type feature type (see V1_write_line_ogr() for list of supported types)
  \param points pointer to line_pnts structure (feature geometry) 
  \param cats pointer to line_cats structure (feature categories)
  
  \return feature index in offset array (related to pseudo-topology)
  \return -1 on error
*/
off_t V2_write_line_sfa(struct Map_info *Map, int type,
			const struct line_pnts *points, const struct line_cats *cats)
{
#if defined HAVE_OGR || defined HAVE_POSTGRES
    int line;
    off_t offset;
    struct Plus_head *plus;
    struct bound_box box;
    struct Format_info_offset *offset_info;
    
    line = 0;
    plus = &(Map->plus);
    
    G_debug(3, "V2_write_line_sfa(): type = %d (format = %d)",
	    type, Map->format);
    
    if (Map->format == GV_FORMAT_POSTGIS) {
	offset_info = &(Map->fInfo.pg.offset);
	offset = V1_write_line_pg(Map, type, points, cats);
    }
    else {
	offset_info = &(Map->fInfo.pg.offset);
	offset = V1_write_line_ogr(Map, type, points, cats);
    }
    if (offset < 0)
	return -1;

    if (!(plus->update_cidx)) {
	plus->cidx_up_to_date = FALSE; /* category index will be outdated */
    }

    /* Update topology */
    if (plus->built >= GV_BUILD_BASE) {
	dig_line_box(points, &box);
	line = dig_add_line(plus, type, points, &box, offset);
	G_debug(3, "\tline added to topo with line = %d", line);
	if (line == 1)
	    Vect_box_copy(&(plus->box), &box);
	else
	    Vect_box_extend(&(plus->box), &box);

	if (type == GV_BOUNDARY) {
	    int ret, cline;
	    long fid;
	    double x, y;
	    
	    struct bound_box box;
	    struct line_pnts *CPoints;

	    /* add virtual centroid to pseudo-topology */
	    ret = Vect_get_point_in_poly(points, &x, &y);
	    if (ret == 0) {
		CPoints = Vect_new_line_struct();
		Vect_append_point(CPoints, x, y, 0.0);
		
		fid = offset_info->array[offset];

		dig_line_box(CPoints, &box);
		cline = dig_add_line(plus, GV_CENTROID,
				     CPoints, &box, fid);
		G_debug(4, "\tCentroid: x = %f, y = %f, cat = %lu, line = %d",
			x, y, fid, cline);	  
		dig_cidx_add_cat(plus, 1, (int) fid,
				 cline, GV_CENTROID);
		
		Vect_destroy_line_struct(CPoints);
	    }
	    else {
		G_warning(_("Unable to calculate centroid for area"));
	    }
	}
	V2__add_line_to_topo_sfa(Map, line, points, cats);
    }


    G_debug(3, "updated lines : %d , updated nodes : %d", plus->uplist.n_uplines,
	    plus->uplist.n_upnodes);

    /* returns int line, but is defined as off_t for compatibility with
     * Write_line_array in write.c */
    return line;
#else
    G_fatal_error(_("GRASS is not compiled with OGR/PostgreSQL support"));
    return -1;
#endif
}

/*!
  \brief Rewrites feature at the given offset on level 2 (OGR/PostGIS
  interface, pseudo-topological level)

  Note: Topology must be built at level >= GV_BUILD_BASE

  \param Map pointer to Map_info structure
  \param line feature id to be rewritten
  \param type feature type (see V1_write_line_ogr() for supported types)
  \param points pointer to line_pnts structure (feature geometry)
  \param cats pointer to line_cats structure feature categories
  
  \return feature index in offset array (related to pseudo-topology)
  \return -1 on error
*/
off_t V2_rewrite_line_sfa(struct Map_info *Map, off_t line, int type,
			  const struct line_pnts *points, const struct line_cats *cats)
{
    G_debug(3, "V2_rewrite_line_sfa(): line=%d type=%d",
	    (int)line, type);

    if (line < 1 || line > Map->plus.n_lines) {
        G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
        return -1;
    }

#if defined HAVE_OGR || defined HAVE_POSTGRES
    if (type != V2_read_line_sfa(Map, NULL, NULL, line)) {
	G_warning(_("Unable to rewrite feature (incompatible feature types)"));
	return -1;
    }

    if (V2_delete_line_sfa(Map, line) != 0)
        return -1;

    return V2_write_line_sfa(Map, type, points, cats);
#else
    G_fatal_error(_("GRASS is not compiled with OGR/PostgreSQL support"));
    return -1;
#endif
}

/*!
  \brief Deletes feature on level 2 (OGR/PostGIS interface)

  Note: Topology must be built at level >= GV_BUILD_BASE
  
  \todo Update fidx
  
  \param pointer to Map_info structure
  \param line feature id to be deleted
  
  \return 0 on success
  \return -1 on error
*/
int V2_delete_line_sfa(struct Map_info *Map, off_t line)
{
#if defined HAVE_OGR || defined HAVE_POSTGRES
    int ret, i, type, first;
    struct P_line *Line;
    struct Plus_head *plus;
    static struct line_cats *Cats = NULL;
    static struct line_pnts *Points = NULL;

    G_debug(3, "V2_delete_line_sfa(): line = %d", (int)line);

    type = first = 0;
    Line = NULL;
    plus = &(Map->plus);

    if (line < 1 || line > Map->plus.n_lines) {
        G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
        return -1;
    }

    if (!(plus->update_cidx)) {
	plus->cidx_up_to_date = FALSE; /* category index will be outdated */
    }

    if (plus->built >= GV_BUILD_BASE) {
	Line = Map->plus.Line[line];

	if (Line == NULL)
	    G_fatal_error(_("Attempt to delete dead feature"));
	type = Line->type;
    }

    if (!Cats) {
	Cats = Vect_new_cats_struct();
    }
    if (!Points) {
	Points = Vect_new_line_struct();
    }

    type = V2_read_line_sfa(Map, Points, Cats, line);
    if (type < 0)
        return -1;
    
    /* Update category index */
    if (plus->update_cidx) {
      for (i = 0; i < Cats->n_cats; i++) {
	    dig_cidx_del_cat(plus, Cats->field[i], Cats->cat[i], line, type);
	}
    }
    /* Update fidx */
    /* TODO */
    
    /* delete the line from coor */
    if (Map->format == GV_FORMAT_POSTGIS)
	ret = V1_delete_line_pg(Map, Line->offset);
    else
	ret = V1_delete_line_ogr(Map, Line->offset);
    
    if (ret == -1) {
	return ret;
    }

    /* Update topology */
    if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY) {
	/* TODO */
	/* remove centroid together with boundary (is really an OGR polygon) */
    }
    /* Delete reference from area */
    if (plus->built >= GV_BUILD_CENTROIDS && type == GV_CENTROID) {
	/* for OGR mapsets, virtual centroid will be removed when
	 * polygon is removed */
    }

    /* delete the line from topo */
    dig_del_line(plus, line, Points->x[0], Points->y[0], Points->z[0]);

    /* Rebuild areas/isles and attach centroids and isles */
    if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY) {
	/* maybe not needed VERIFY */
    }
    return ret;
#else
    G_fatal_error(_("GRASS is not compiled with OGR/PostgreSQL support"));
    return -1;
#endif
}

/*!
   \brief Writes area on topological level (Simple Features interface,
   internal use only)

   \param Map pointer to Map_info structure
   \param points feature geometry (exterior + interior rings)
   \param nparts number of parts including exterior ring
   \param cats feature categories
   
   \return feature offset
   \return -1 on error
*/
off_t V2__write_area_sfa(struct Map_info *Map, 
                         const struct line_pnts **points, int nparts,
                         const struct line_cats *cats)
{
    if (Map->format == GV_FORMAT_OGR) {
#ifdef HAVE_OGR
        return V2__write_area_ogr(Map, points, nparts, cats);
#else
        G_fatal_error(_("GRASS is not compiled with OGR support"));
        return -1;
#endif
    }
    else if (Map->format == GV_FORMAT_POSTGIS) {
#ifdef HAVE_POSTGRES
        return V2__write_area_pg(Map, points, nparts, cats);
#else
        G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
        return -1;
#endif
    }
    else {
        G_warning(_("Unsupported vector map format (%d)"), Map->format);
    }
    return -1;
}

#if defined HAVE_OGR || defined HAVE_POSTGRES
/*!
  \brief Add feature to topo file (internal use only)

  \param Map pointer to Map_info structure
  \param line feature id
  \param points pointer to line_pnts structure (feature geometry)
  \param cats pointer to line_cats structure (feature categories)
*/
void V2__add_line_to_topo_sfa(struct Map_info *Map, int line,
			      const struct line_pnts *points,
			      const struct line_cats *cats)
{
    int first, s, i;
    int type, area, side;

    struct Plus_head *plus;
    struct P_line *Line;
    
    struct bound_box box, abox;
    
    G_debug(3, "V2__add_line_to_topo_sfa(): line = %d npoints = %d", line,
	    points->n_points);

    plus = &(Map->plus);
    Line = plus->Line[line];
    type = Line->type;

    if (plus->built >= GV_BUILD_AREAS &&
	type == GV_BOUNDARY) {	
	struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
	
	if (topo->N1 != topo->N2) {
	    G_warning(_("Boundary is not closed. Skipping."));
	    return;
	}
	
	/* Build new areas/isles */
	for (s = 0; s < 2; s++) {
	    side = (s == 0 ? GV_LEFT : GV_RIGHT);
	    area = Vect_build_line_area(Map, line, side);
	    if (area > 0) {	/* area */
		Vect_get_area_box(Map, area, &box);
		if (first) {
		    Vect_box_copy(&abox, &box);
		    first = FALSE;
		}
		else
		    Vect_box_extend(&abox, &box);
	    }
	    else if (area < 0) {
		/* isle -> must be attached -> add to abox */
		Vect_get_isle_box(Map, -area, &box);
		if (first) {
		    Vect_box_copy(&abox, &box);
		    first = FALSE;
		}
		else
		    Vect_box_extend(&abox, &box);
	    }
	    G_debug(4, "Vect_build_line_area(): -> area = %d", area);
	}

	/* Attach centroid/isle to the new area */
	if (plus->built >= GV_BUILD_ATTACH_ISLES)
	    Vect_attach_isles(Map, &abox);
	if (plus->built >= GV_BUILD_CENTROIDS)
	    Vect_attach_centroids(Map, &abox);
    }
    
    /* Add category index */
    for (i = 0; i < cats->n_cats; i++) {
	dig_cidx_add_cat_sorted(plus, cats->field[i], cats->cat[i], line,
				type);
    }
    
    return;
}
#endif /* HAVE_OGR || HAVE_POSTGRES */
