/****************************************************************
 *
 * MODULE:     v.generalize
 *
 * AUTHOR(S):  Daniel Bundala, Markus Metz
 *
 * PURPOSE:    miscellaneous functions of v.generalize
 *
 *
 * COPYRIGHT:  (C) 2002-2005 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.
 *
 ****************************************************************/

#include <stdlib.h>
#include <grass/gis.h>
#include <grass/vector.h>
#include <grass/dbmi.h>
#include <grass/glocale.h>
#include "misc.h"

static int topo_debug = 0;

int set_topo_debug(void)
{
    if (getenv("GRASS_VECTOR_TOPO_DEBUG"))
        topo_debug = 1;

    return topo_debug;
}

int type_mask(struct Option *type_opt)
{
    int res = 0;
    int i;

    for (i = 0; type_opt->answers[i]; i++)
        switch (type_opt->answers[i][0]) {
        case 'l':
            res |= GV_LINE;
            break;
        case 'b':
            res |= GV_BOUNDARY;
            break;
        case 'a':
            res |= GV_BOUNDARY;
        }

    return res;
}

int get_furthest(struct line_pnts *Points, int a, int b, int with_z,
                 double *dist)
{
    int index = a;
    double d = 0;

    int i;
    double x0 = Points->x[a];
    double x1 = Points->x[b];
    double y0 = Points->y[a];
    double y1 = Points->y[b];
    double z0 = Points->z[a];
    double z1 = Points->z[b];

    double px, py, pz, pdist, di;
    int status;

    for (i = a + 1; i < b; i++) {
        di = dig_distance2_point_to_line(
            Points->x[i], Points->y[i], Points->z[i], x0, y0, z0, x1, y1, z1,
            with_z, &px, &py, &pz, &pdist, &status);
        if (di > d) {
            d = di;
            index = i;
        }
    }
    *dist = d;
    return index;
}

/* TODO: The collection of categories is horrible in current version!
 * Everything repeats many times. We need some data structure
 * implementing set! */
int copy_tables_by_cats(struct Map_info *In, struct Map_info *Out)
{
    /* this is the (mostly) code from v.extract, it should be moved to
     * some vector library (probably) */

    int nlines, line, nfields;
    int ttype, ntabs = 0;
    struct field_info *IFi, *OFi;
    struct line_cats *Cats;
    int **ocats, *nocats, *fields;
    int i;

    /* Collect list of output cats */
    Cats = Vect_new_cats_struct();
    nfields = Vect_cidx_get_num_fields(In);
    ocats = (int **)G_malloc(nfields * sizeof(int *));
    nocats = (int *)G_malloc(nfields * sizeof(int));
    fields = (int *)G_malloc(nfields * sizeof(int));
    for (i = 0; i < nfields; i++) {
        nocats[i] = 0;
        ocats[i] = (int *)G_malloc(Vect_cidx_get_num_cats_by_index(In, i) *
                                   sizeof(int));
        fields[i] = Vect_cidx_get_field_number(In, i);
    }
    nlines = Vect_get_num_lines(Out);
    for (line = 1; line <= nlines; line++) {
        Vect_read_line(Out, NULL, Cats, line);
        for (i = 0; i < Cats->n_cats; i++) {
            int f = 0, j;

            for (j = 0; j < nfields; j++) { /* find field */
                if (fields[j] == Cats->field[i]) {
                    f = j;
                    break;
                }
            }
            ocats[f][nocats[f]] = Cats->cat[i];
            nocats[f]++;
        }
    }

    /* Copy tables */
    G_message(_("Writing attributes..."));

    /* Number of output tabs */
    for (i = 0; i < Vect_get_num_dblinks(In); i++) {
        int j, f = -1;

        IFi = Vect_get_dblink(In, i);

        for (j = 0; j < nfields; j++) { /* find field */
            if (fields[j] == IFi->number) {
                f = j;
                break;
            }
        }
        if (f >= 0 && nocats[f] > 0)
            ntabs++;
    }

    if (ntabs > 1)
        ttype = GV_MTABLE;
    else
        ttype = GV_1TABLE;

    for (i = 0; i < nfields; i++) {
        int ret;

        if (fields[i] == 0)
            continue;
        if (nocats[i] == 0)
            continue;
        /*        if ( fields[i] == field && new_cat != -1 ) continue; */

        G_message(_("Layer %d"), fields[i]);
        /* Make a list of categories */
        IFi = Vect_get_field(In, fields[i]);
        if (!IFi) { /* no table */
            G_warning(_("Database connection not defined for layer %d"),
                      fields[i]);
            continue;
        }

        OFi = Vect_default_field_info(Out, IFi->number, IFi->name, ttype);

        ret = db_copy_table_by_ints(IFi->driver, IFi->database, IFi->table,
                                    OFi->driver,
                                    Vect_subst_var(OFi->database, Out),
                                    OFi->table, IFi->key, ocats[i], nocats[i]);

        if (ret == DB_FAILED) {
            G_warning(_("Unable to copy table <%s>"), IFi->table);
        }
        else {
            Vect_map_add_dblink(Out, OFi->number, OFi->name, OFi->table,
                                IFi->key, OFi->database, OFi->driver);
        }
    }

    for (i = 0; i < nfields; i++)
        G_free(ocats[i]);
    G_free(ocats);
    G_free(nocats);
    G_free(fields);
    return 1;
}

static int cmp(const void *a, const void *b)
{
    int ai = *(int *)a;
    int bi = *(int *)b;

    return (ai < bi ? -1 : (ai > bi));
}

/* check topology corruption by boundary modification
 * return 0 on corruption, 1 if modification is ok */
int check_topo(struct Map_info *Out, int line, struct line_pnts *APoints,
               struct line_pnts *Points, struct line_cats *Cats)
{
    int i, j, k, intersect, newline;
    struct bound_box box, abox, tbox;
    struct bound_box lbox, rbox, areabox;
    struct line_pnts **AXLines, **BXLines;
    int naxlines, nbxlines;
    static struct line_pnts *BPoints = NULL;
    static struct boxlist *List = NULL;
    static struct line_pnts *BPoints2 = NULL;
    static struct ilist *BList = NULL;
    int area, isle, centr;
    int left_o, left_n, right_o, right_n;
    int node, node_n_lines;
    float angle1, angle2;
    off_t offset;

    /* topology debugging */
    int area_l_o, area_r_o, area_l_n, area_r_n;
    int centr_l_o, centr_r_o, centr_l_n, centr_r_n;
    int *isles_l_o, nisles_l_o, *isles_l_n, nisles_l_n;
    int *isles_r_o, nisles_r_o, *isles_r_n, nisles_r_n;
    int found;

    if (!BPoints) {
        BPoints = Vect_new_line_struct();
        BPoints2 = Vect_new_line_struct();
        List = Vect_new_boxlist(1);
        BList = Vect_new_list();
    }

    if (APoints->n_points == Points->n_points) {
        int same = 1;

        for (i = 0; i < APoints->n_points; i++) {
            if (APoints->x[i] != Points->x[i] ||
                APoints->y[i] != Points->y[i]) {
                same = 0;
                break;
            }
        }
        if (same)
            return 1;
    }

    i = APoints->n_points - 1;
    if (APoints->x[0] == APoints->x[i] && APoints->y[0] == APoints->y[i]) {
        i = Points->n_points - 1;
        if (Points->x[0] != Points->x[i] || Points->y[0] != Points->y[i]) {
            /* input line forms a loop, output not */
            return 0;
        }
    }

    /* test node angles
     * an area can be built only if there are no two lines with the same
     * angle at the same node */
    /* line start */
    angle1 = dig_calc_begin_angle(Points, 0);
    if (angle1 == -9)
        return 0;

    node =
        dig_find_node(&(Out->plus), Points->x[0], Points->y[0], Points->z[0]);
    if (node) {
        /* if another line exists with the same angle at this node,
         * an area can not be constructed -> error */
        node_n_lines = Vect_get_node_n_lines(Out, node);
        for (i = 0; i < node_n_lines; i++) {

            if (abs(Vect_get_node_line(Out, node, i)) == line)
                continue;

            if (angle1 == Vect_get_node_line_angle(Out, node, i))
                return 0;
        }
    }

    /* line end */
    angle2 = dig_calc_end_angle(Points, 0);
    if (angle2 == -9)
        return 0;

    i = Points->n_points - 1;
    if (Points->x[0] == Points->x[i] && Points->y[0] == Points->y[i]) {
        if (angle1 == angle2) {
            /* same angle, same start and end coordinates,
             * an area can not be constructed -> error */
            return 0;
        }
    }
    else {
        node = dig_find_node(&(Out->plus), Points->x[i], Points->y[i],
                             Points->z[i]);
    }

    if (node) {
        /* if another line exists with the same angle at this node,
         * an area can not be constructed -> error */
        node_n_lines = Vect_get_node_n_lines(Out, node);
        for (i = 0; i < node_n_lines; i++) {

            if (abs(Vect_get_node_line(Out, node, i)) == line)
                continue;

            if (angle2 == Vect_get_node_line_angle(Out, node, i))
                return 0;
        }
    }

    Vect_line_box(Points, &box);

    /* Check the modified boundary for self-intersection */
    AXLines = BXLines = NULL;
    Vect_line_intersection2(Points, NULL, &box, &box, &AXLines, &BXLines,
                            &naxlines, &nbxlines, 0);
    /* Free */
    if (naxlines > 0) {
        for (j = 0; j < naxlines; j++) {
            Vect_destroy_line_struct(AXLines[j]);
        }
    }
    if (AXLines)
        G_free(AXLines);
    if (naxlines > 0)
        return 0;

    /* Check intersection of the modified boundary with other boundaries */
    Vect_select_lines_by_box(Out, &box, GV_BOUNDARY, List);

    intersect = 0;
    for (i = 0; i < List->n_values; i++) {
        int bline;

        bline = List->id[i];
        if (bline == line)
            continue;

        Vect_read_line(Out, BPoints, NULL, bline);

        /* Vect_line_intersection is quite slow, hopefully not so bad because
         * only few intersections should be found if any */

        AXLines = BXLines = NULL;
        Vect_line_intersection2(Points, BPoints, &box, &List->box[i], &AXLines,
                                &BXLines, &naxlines, &nbxlines, 0);

        G_debug(4, "bline = %d intersect = %d naxlines = %d nbxlines = %d",
                bline, intersect, naxlines, nbxlines);

        /* Free */
        if (naxlines > 0) {
            for (j = 0; j < naxlines; j++) {
                Vect_destroy_line_struct(AXLines[j]);
            }
        }
        if (AXLines)
            G_free(AXLines);
        if (nbxlines > 0) {
            for (j = 0; j < nbxlines; j++) {
                Vect_destroy_line_struct(BXLines[j]);
            }
        }
        if (BXLines)
            G_free(BXLines);

        if (naxlines > 1 || nbxlines > 1) {
            intersect = 1;
            break;
        }
    }

    /* modified boundary intersects another boundary */
    if (intersect)
        return 0;

    /* test centroid attachment (point-in-poly) */
    Vect_get_line_areas(Out, line, &left_o, &right_o);

    Vect_line_box(APoints, &abox);

    /* centroid on the left side */
    isle = centr = 0;
    area = left_o;
    if (area < 0) {
        isle = -area;
        area = Vect_get_isle_area(Out, isle);
    }
    if (area > 0)
        centr = Vect_get_area_centroid(Out, area);
    centr_l_o = centr;
    area_l_o = area;
    lbox = box;

    if (isle)
        Vect_get_isle_boundaries(Out, isle, BList);
    else
        Vect_get_area_boundaries(Out, area, BList);

    Vect_reset_line(BPoints2);
    for (i = 0; i < BList->n_values; i++) {
        int bline = BList->value[i];
        int dir = bline > 0 ? GV_FORWARD : GV_BACKWARD;

        if (abs(bline) != line) {
            Vect_read_line(Out, BPoints, NULL, abs(bline));
            Vect_line_box(BPoints, &tbox);
            Vect_box_extend(&lbox, &tbox);
            Vect_append_points(BPoints2, BPoints, dir);
        }
        else
            Vect_append_points(BPoints2, Points, dir);

        BPoints2->n_points--; /* skip last point, avoids duplicates */
    }
    BPoints2->n_points++; /* close polygon */

    if (centr > 0) {
        int ret;
        double cx, cy, cz;

        Vect_read_line(Out, BPoints, NULL, centr);
        cx = BPoints->x[0];
        cy = BPoints->y[0];
        cz = BPoints->z[0];

        if (Vect_point_in_box(cx, cy, cz, &box) ||
            Vect_point_in_box(cx, cy, cz, &abox)) {

            ret = Vect_point_in_poly(cx, cy, BPoints2);
            if (!isle) {
                /* area: centroid must be inside */
                if (ret != 1)
                    return 0;
            }
            else {
                /* isle: centroid must be outside */
                if (ret > 0)
                    return 0;
            }
        }
    }

    /* centroid on the right side */
    isle = centr = 0;
    area = right_o;
    if (area < 0) {
        isle = -area;
        area = Vect_get_isle_area(Out, isle);
    }
    if (area > 0)
        centr = Vect_get_area_centroid(Out, area);
    centr_r_o = centr;
    area_r_o = area;
    rbox = box;

    if (isle)
        Vect_get_isle_boundaries(Out, isle, BList);
    else
        Vect_get_area_boundaries(Out, area, BList);

    Vect_reset_line(BPoints2);
    for (i = 0; i < BList->n_values; i++) {
        int bline = BList->value[i];
        int dir = bline > 0 ? GV_FORWARD : GV_BACKWARD;

        if (abs(bline) != line) {
            Vect_read_line(Out, BPoints, NULL, abs(bline));
            Vect_line_box(BPoints, &tbox);
            Vect_box_extend(&rbox, &tbox);
            Vect_append_points(BPoints2, BPoints, dir);
        }
        else
            Vect_append_points(BPoints2, Points, dir);

        BPoints2->n_points--; /* skip last point, avoids duplicates */
    }
    BPoints2->n_points++; /* close polygon */

    if (centr > 0) {
        int ret;
        double cx, cy, cz;

        Vect_read_line(Out, BPoints, NULL, centr);
        cx = BPoints->x[0];
        cy = BPoints->y[0];
        cz = BPoints->z[0];

        if (Vect_point_in_box(cx, cy, cz, &box) ||
            Vect_point_in_box(cx, cy, cz, &abox)) {

            ret = Vect_point_in_poly(cx, cy, BPoints2);
            if (!isle) {
                /* area: centroid must be inside */
                if (ret != 1)
                    return 0;
            }
            else {
                /* isle: centroid must be outside */
                if (ret > 0)
                    return 0;
            }
        }
    }

    /* all fine:
     * areas/isles can be built
     * no intersection with another boundary, e.g. isle attachment will be
     * preserved centroids are still on the correct side of the boundary */

    if (!topo_debug) {
        /* update only those parts of topology that actually get changed */
        /* boundary:
         * node in case of loop support
         * node angles
         * bounding box */

        /* rewrite boundary on level 1 */
        offset = Out->plus.Line[line]->offset;
        Out->level = 1;
        offset = Vect_rewrite_line(Out, offset, GV_BOUNDARY, Points, Cats);
        Out->level = 2;
        /* delete line from topo */
        dig_del_line(&Out->plus, line, APoints->x[0], APoints->y[0],
                     APoints->z[0]);
        /* restore line in topo */
        dig_restore_line(&Out->plus, line, GV_BOUNDARY, Points, &box, offset);

        /* update area/isle box to the left */
        if (left_o < 0) {
            dig_spidx_del_isle(&Out->plus, -left_o);
            dig_spidx_add_isle(&Out->plus, -left_o, &lbox);
        }
        else if (left_o > 0) {
            dig_spidx_del_area(&Out->plus, left_o);
            dig_spidx_add_area(&Out->plus, left_o, &lbox);
        }
        /* update area/isle box to the right */
        if (right_o < 0) {
            dig_spidx_del_isle(&Out->plus, -right_o);
            dig_spidx_add_isle(&Out->plus, -right_o, &rbox);
        }
        else if (right_o > 0) {
            dig_spidx_del_area(&Out->plus, right_o);
            dig_spidx_add_area(&Out->plus, right_o, &rbox);
        }

        /* done */
        return 1;
    }

    /* debug topology */

    /* record isles of the old areas to the left and right */
    nisles_l_o = 0;
    isles_l_o = NULL;
    if (area_l_o) {
        nisles_l_o = Out->plus.Area[area_l_o]->n_isles;
        if (nisles_l_o) {
            isles_l_o = G_malloc(nisles_l_o * sizeof(int));
            for (i = 0; i < nisles_l_o; i++) {
                isles_l_o[i] = Out->plus.Area[area_l_o]->isles[i];
            }
            qsort(isles_l_o, nisles_l_o, sizeof(int), cmp);
        }
    }
    nisles_r_o = 0;
    isles_r_o = NULL;
    if (area_r_o) {
        nisles_r_o = Out->plus.Area[area_r_o]->n_isles;
        if (nisles_r_o) {
            isles_r_o = G_malloc(nisles_r_o * sizeof(int));
            for (i = 0; i < nisles_r_o; i++) {
                isles_r_o[i] = Out->plus.Area[area_r_o]->isles[i];
            }
            qsort(isles_r_o, nisles_r_o, sizeof(int), cmp);
        }
    }

    /* OK, rewrite modified boundary */
    newline = Vect_rewrite_line(Out, line, GV_BOUNDARY, Points, Cats);
    if (newline != line)
        G_fatal_error("Vect_rewrite_line(): new line id %d != old line id %d",
                      newline, line);

    /* get new area and centroid ids to the left and right */
    centr_l_n = centr_r_n = 0;
    Vect_get_line_areas(Out, newline, &left_n, &right_n);
    area = left_n;
    if (area < 0)
        area = Vect_get_isle_area(Out, -area);
    if (area > 0)
        centr_l_n = Vect_get_area_centroid(Out, area);
    area_l_n = area;

    area = right_n;
    if (area < 0)
        area = Vect_get_isle_area(Out, -area);
    if (area > 0)
        centr_r_n = Vect_get_area_centroid(Out, area);
    area_r_n = area;

    /* record isles of the new areas to the left and right */
    nisles_l_n = 0;
    isles_l_n = NULL;
    if (area_l_n) {
        nisles_l_n = Out->plus.Area[area_l_n]->n_isles;
        if (nisles_l_n) {
            isles_l_n = G_malloc(nisles_l_n * sizeof(int));
            for (i = 0; i < nisles_l_n; i++) {
                isles_l_n[i] = Out->plus.Area[area_l_n]->isles[i];
            }
            qsort(isles_l_n, nisles_l_n, sizeof(int), cmp);
        }
    }
    nisles_r_n = 0;
    isles_r_n = NULL;
    if (area_r_n) {
        nisles_r_n = Out->plus.Area[area_r_n]->n_isles;
        if (nisles_r_n) {
            isles_r_n = G_malloc(nisles_r_n * sizeof(int));
            for (i = 0; i < nisles_r_n; i++) {
                isles_r_n[i] = Out->plus.Area[area_r_n]->isles[i];
            }
            qsort(isles_r_n, nisles_r_n, sizeof(int), cmp);
        }
    }

    /* compare isle numbers and ids on the left and right */
    /* left */
    if (nisles_l_o != nisles_l_n)
        G_fatal_error("Number of isles to the left differ: old %d, new %d",
                      nisles_l_o, nisles_l_n);
    found = 0;
    k = 0;
    for (i = 0; i < nisles_l_o; i++) {
        if (isles_l_o[i] != isles_l_n[k]) {
            if (!found) {
                found = 1;
                k--;
            }
            else {
                for (j = 0; j < nisles_l_o; j++) {
                    G_message("old %d new %d", isles_l_o[j], isles_l_n[j]);
                }
                G_fatal_error("New isle to the left %d is wrong", isles_l_n[i]);
            }
        }
        k++;
    }
    /* right */
    if (nisles_r_o != nisles_r_n)
        G_fatal_error("Number of isles to the left differ: old %d, new %d",
                      nisles_r_o, nisles_r_n);
    found = 0;
    k = 0;
    for (i = 0; i < nisles_r_o; i++) {
        if (isles_r_o[i] != isles_r_n[k]) {
            if (!found) {
                found = 1;
                k--;
            }
            else {
                for (j = 0; j < nisles_r_o; j++) {
                    G_message("old %d new %d", isles_r_o[j], isles_r_n[j]);
                }
                G_fatal_error("New isle to the right %d is wrong",
                              isles_l_n[i]);
            }
        }
        k++;
    }

    /* Check position of centroids */
    if (centr_l_n != centr_l_o || centr_r_n != centr_r_o) {
        /* should not happen if the above topo checks work as expected */
        G_warning("The modified boundary changes attachment of centroid -> "
                  "topo checks failed");

        if (centr_l_n != centr_l_o) {
            G_warning("Left area/isle old: %d, new: %d", left_o, left_n);
            G_warning("Left centroid old: %d, new: %d", centr_l_o, centr_l_n);

            if (centr_l_o) {
                int ret1, ret2, ret3;

                Vect_read_line(Out, BPoints, NULL, centr_l_o);
                Vect_get_area_box(Out, area_l_n, &abox);

                ret1 = (BPoints->x[0] >= abox.W && BPoints->x[0] <= abox.E &&
                        BPoints->y[0] >= abox.S && BPoints->y[0] <= abox.N);

                ret2 = Vect_point_in_area_outer_ring(
                    BPoints->x[0], BPoints->y[0], Out, area_l_n, &abox);

                Vect_get_area_points(Out, area_l_n, BPoints2);
                ret3 =
                    Vect_point_in_poly(BPoints->x[0], BPoints->y[0], BPoints2);

                if (ret2 != ret3) {
                    G_warning("Left old centroid in new area box: %d", ret1);
                    G_warning("Left old centroid in new area outer ring: %d",
                              ret2);
                    G_warning("Left old centroid in new area as poly: %d",
                              ret3);
                }
            }
        }
        if (centr_r_n != centr_r_o) {
            G_warning("Right area/isle old: %d, new: %d", right_o, right_n);
            G_warning("Right centroid old: %d, new: %d", centr_r_o, centr_r_n);

            if (centr_r_o) {
                int ret1, ret2, ret3;

                Vect_read_line(Out, BPoints, NULL, centr_r_o);
                Vect_get_area_box(Out, area_r_n, &abox);

                ret1 = (BPoints->x[0] >= abox.W && BPoints->x[0] <= abox.E &&
                        BPoints->y[0] >= abox.S && BPoints->y[0] <= abox.N);

                ret2 = Vect_point_in_area_outer_ring(
                    BPoints->x[0], BPoints->y[0], Out, area_r_n, &abox);

                Vect_get_area_points(Out, area_r_n, BPoints2);
                ret3 =
                    Vect_point_in_poly(BPoints->x[0], BPoints->y[0], BPoints2);

                if (ret2 != ret3) {
                    G_warning("Right old centroid in new area box: %d", ret1);
                    G_warning("Right old centroid in new area outer ring: %d",
                              ret2);
                    G_warning("Right old centroid in new area as poly: %d",
                              ret3);
                }
            }
        }

        /* rewrite old line */
        newline = Vect_rewrite_line(Out, newline, GV_BOUNDARY, APoints, Cats);

        centr_l_n = centr_r_n = 0;
        Vect_get_line_areas(Out, newline, &area_l_n, &area_r_n);
        area = area_l_n;
        if (area < 0)
            area = Vect_get_isle_area(Out, -area);
        if (area > 0)
            centr_l_n = Vect_get_area_centroid(Out, area);
        area_l_n = area;

        area = area_r_n;
        if (area < 0)
            area = Vect_get_isle_area(Out, -area);
        if (area > 0)
            centr_r_n = Vect_get_area_centroid(Out, area);
        area_r_n = area;

        if (centr_l_n != centr_l_o) {
            Vect_get_area_box(Out, area_l_n, &areabox);

            if (centr_l_n > 0) {
                Vect_read_line(Out, BPoints, NULL, centr_l_n);
                if (Vect_point_in_area(BPoints->x[0], BPoints->y[0], Out,
                                       area_l_n, &areabox)) {

                    G_warning("New left centroid is in new left area %d",
                              area_l_n);

                    G_warning("New left centroid on outer ring: %d",
                              Vect_point_in_area_outer_ring(
                                  BPoints->x[0], BPoints->y[0], Out, area_l_n,
                                  &areabox));

                    G_warning(
                        "Best area for new left centroid: %d",
                        Vect_find_area(Out, BPoints->x[0], BPoints->y[0]));
                }
                else
                    G_warning("New left centroid is not in new left area");
            }

            if (centr_l_o > 0) {
                Vect_read_line(Out, BPoints, NULL, centr_l_o);
                if (Vect_point_in_area(BPoints->x[0], BPoints->y[0], Out,
                                       area_l_n, &areabox)) {

                    G_warning("Old left centroid is in new left area %d",
                              area_l_n);

                    G_warning("Old left centroid on outer ring: %d",
                              Vect_point_in_area_outer_ring(
                                  BPoints->x[0], BPoints->y[0], Out, area_l_n,
                                  &areabox));

                    G_warning(
                        "Best area for old left centroid: %d",
                        Vect_find_area(Out, BPoints->x[0], BPoints->y[0]));
                }
                else
                    G_warning("Old left centroid is not in new left area");
            }

            G_fatal_error("Left centroid old %d, restored %d", centr_l_o,
                          centr_l_n);
            return 0;
        }
        if (centr_r_n != centr_r_o) {
            Vect_get_area_box(Out, area_r_n, &areabox);

            if (centr_r_n > 0) {
                Vect_read_line(Out, BPoints, NULL, centr_r_n);
                if (Vect_point_in_area(BPoints->x[0], BPoints->y[0], Out,
                                       area_r_n, &areabox)) {

                    G_warning("New right centroid is in new right area %d",
                              area_r_n);

                    G_warning("New right centroid on outer ring: %d",
                              Vect_point_in_area_outer_ring(
                                  BPoints->x[0], BPoints->y[0], Out, area_r_n,
                                  &areabox));

                    G_warning(
                        "Best area for new right centroid: %d",
                        Vect_find_area(Out, BPoints->x[0], BPoints->y[0]));
                }
                else
                    G_warning("New right centroid is not in new right area");
            }

            if (centr_r_o > 0) {
                Vect_read_line(Out, BPoints, NULL, centr_r_o);
                if (Vect_point_in_area(BPoints->x[0], BPoints->y[0], Out,
                                       area_r_n, &areabox)) {

                    G_warning("Old right centroid is in new right area %d",
                              area_r_n);

                    G_warning("Old right centroid on outer ring: %d",
                              Vect_point_in_area_outer_ring(
                                  BPoints->x[0], BPoints->y[0], Out, area_r_n,
                                  &areabox));

                    G_warning(
                        "Best area for old right centroid: %d",
                        Vect_find_area(Out, BPoints->x[0], BPoints->y[0]));
                }
                else
                    G_warning("Old right centroid is not in new right area");
            }

            G_fatal_error("Right centroid old %d, restored %d", centr_r_o,
                          centr_r_n);
            return 0;
        }

        G_fatal_error("Topology check failure");
        return 0;
    }
    if (isles_l_o)
        G_free(isles_l_o);
    if (isles_r_o)
        G_free(isles_r_o);
    if (isles_l_n)
        G_free(isles_l_n);
    if (isles_r_n)
        G_free(isles_r_n);

    return 1;
}
