/*!
   \file lib/ogsf/gv.c

   \brief OGSF library - loading and manipulating vector sets (lower level
   functions)

   (C) 1999-2008, 2011 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 Bill Brown USACERL (November 1993)
   \author Doxygenized by Martin Landa (June 2008)
 */

#include <stdio.h>
#include <stdlib.h>

#include <grass/ogsf.h>
#include "gsget.h"

#define FIRST_VECT_ID 20656

static geovect *Vect_top = NULL;

/*!
   \brief Get vector set

   \param id vector set id

   \return pointer to geovect struct
   \return NULL on failure
 */
geovect *gv_get_vect(int id)
{
    geovect *gv;

    G_debug(5, "gv_get_vect() id=%d", id);

    for (gv = Vect_top; gv; gv = gv->next) {
        if (gv->gvect_id == id) {
            return gv;
        }
    }

    return NULL;
}

/*!
   \brief Get previous vector set

   \param id vector set id

   \return pointer to geovect struct
   \return NULL on failure
 */
geovect *gv_get_prev_vect(int id)
{
    geovect *pv;

    G_debug(5, "gv_get_prev_vect(): id=%d", id);

    for (pv = Vect_top; pv; pv = pv->next) {
        if (pv->gvect_id == id - 1) {
            return pv;
        }
    }

    return NULL;
}

/*!
   \brief Get number of loaded vector sets

   \return number of vector sets
 */
int gv_num_vects(void)
{
    geovect *gv;
    int i;

    for (i = 0, gv = Vect_top; gv; gv = gv->next, i++)
        ;

    G_debug(5, "gv_num_vects(): num=%d", i);

    return i;
}

/*!
   \brief Get last loaded vector set

   \return pointer to geovect struct
   \return NULL on failure (no vector set available)
 */
geovect *gv_get_last_vect(void)
{
    geovect *lv;

    if (!Vect_top) {
        return NULL;
    }

    for (lv = Vect_top; lv->next; lv = lv->next)
        ;

    G_debug(5, "gv_get_last_vect(): id=%d", lv->gvect_id);

    return lv;
}

/*!
   \brief Allocate memory for new vector set

   \return pointer to geovect struct
   \return NULL on failure
 */
geovect *gv_get_new_vect(void)
{
    geovect *nv, *lv;

    nv = (geovect *)G_malloc(sizeof(geovect));
    if (!nv) {
        /* G_fatal_error */
        return NULL;
    }
    G_zero(nv, sizeof(geovect));

    if ((lv = gv_get_last_vect())) {
        lv->next = nv;
        nv->gvect_id = lv->gvect_id + 1;
    }
    else {
        Vect_top = nv;
        nv->gvect_id = FIRST_VECT_ID;
    }

    nv->style = (gvstyle *)G_malloc(sizeof(gvstyle));
    if (NULL == nv->style)
        return NULL;
    G_zero(nv->style, sizeof(gvstyle));
    nv->hstyle = (gvstyle *)G_malloc(sizeof(gvstyle));
    if (NULL == nv->hstyle)
        return NULL;
    G_zero(nv->hstyle, sizeof(gvstyle));

    G_debug(5, "gv_get_new_vect() id=%d", nv->gvect_id);

    return nv;
}

/*!
   \brief Update drape surfaces

   Call after surface is deleted
 */
void gv_update_drapesurfs(void)
{
    geovect *gv;
    int i, j;

    for (gv = Vect_top; gv; gv = gv->next) {
        if (gv->n_surfs) {
            for (i = 0; i < gv->n_surfs; i++) {
                if (gv->drape_surf_id[i]) {
                    if (NULL == gs_get_surf(gv->drape_surf_id[i])) {
                        for (j = i; j < gv->n_surfs - 1; j++) {
                            gv->drape_surf_id[j] = gv->drape_surf_id[j + 1];
                        }

                        gv->n_surfs = gv->n_surfs - 1;
                    }
                }
            }
        }
    }
}

/*!
   \brief Set attributes of vector set to default values

   \param gv pointer to geovect struct

   \return -1 on error
   \return 0 on success
 */
int gv_set_defaults(geovect *gv)
{
    int i;

    if (!gv) {
        return (-1);
    }
    G_debug(5, "gv_set_defaults() id=%d", gv->gvect_id);

    gv->filename = NULL;
    gv->n_lines = gv->n_surfs = gv->use_mem = 0;
    gv->x_trans = gv->y_trans = gv->z_trans = 0.0;
    gv->lines = NULL;
    gv->fastlines = NULL;
    gv->use_z = 0;
    gv->style->color = 0xF0F0F0;
    gv->style->width = 1;
    gv->style->next = NULL;
    gv->hstyle->color = 0xFF0000;
    gv->hstyle->width = 2;
    gv->hstyle->next = NULL;
    gv->tstyle = NULL;
    gv->next = NULL;

    for (i = 0; i < MAX_SURFS; i++) {
        gv->drape_surf_id[i] = 0;
    }

    return 0;
}

/*!
   \brief Initialize geovect struct

   \param gv pointer to geovect struct

   \return -1 on failure
   \return 0 on success
 */
int gv_init_vect(geovect *gv)
{
    if (!gv) {
        return -1;
    }

    G_debug(5, "gv_init_vect() id=%d", gv->gvect_id);

    return 0;
}

/*!
   \brief Delete vector set (unload)

   \param id vector set id
 */
void gv_delete_vect(int id)
{
    geovect *fv;

    G_debug(5, "gv_delete_vect(): id=%d", id);

    fv = gv_get_vect(id);

    if (fv) {
        gv_free_vect(fv);
    }

    return;
}

/*!
   \brief Free allocated memory for geovect struct

   \param fv pointer to geovect struct

   \return -1 on failure
   \return 1 on success
 */
int gv_free_vect(geovect *fv)
{
    geovect *gv;
    int found = 0;

    if (Vect_top) {
        if (fv == Vect_top) {
            if (Vect_top->next) {
                /* can't free top if last */
                found = 1;
                Vect_top = fv->next;
            }
            else {
                gv_free_vectmem(fv);
                G_free(fv);
                Vect_top = NULL;
            }
        }
        else {
            for (gv = Vect_top; gv && !found; gv = gv->next) {
                /* can't free top */
                if (gv->next) {
                    if (gv->next == fv) {
                        found = 1;
                        gv->next = fv->next;
                    }
                }
            }
        }

        if (found) {
            G_debug(5, "gv_free_vect(): id=%d", fv->gvect_id);
            gv_free_vectmem(fv);
            G_free(fv);
            fv = NULL;
        }

        return 1;
    }

    return -1;
}

/*!
   \brief Free allocated memory

   \param fv pointer to geovect struct
 */
void gv_free_vectmem(geovect *fv)
{
    geoline *gln, *tmpln;

    G_free((void *)fv->filename);
    fv->filename = NULL;
    if (fv->style)
        G_free(fv->style);
    if (fv->hstyle)
        G_free(fv->hstyle);

    if (fv->lines) {
        for (gln = fv->lines; gln;) {
            if (gln->dims == 2) {
                sub_Vectmem(gln->npts * sizeof(Point2));
                G_free(gln->p2);
            }

            if (gln->dims == 3) {
                G_free(gln->p3);
            }

            G_free(gln->cats);

            tmpln = gln;
            gln = gln->next;
            sub_Vectmem(sizeof(geoline));
            G_free(tmpln);
        }

        fv->n_lines = 0;
        fv->lines = NULL;
    }

    if (fv->tstyle) {
        G_free(fv->tstyle->color_column);
        G_free(fv->tstyle->symbol_column);
        G_free(fv->tstyle->size_column);
        G_free(fv->tstyle->width_column);
    }

    return;
}

/*!
   \brief Set drape surfaces for vector set

   \param gv pointer to geovect struct
   \param hsurfs array of surfaces (id)
   \param nsurfs number of surfaces
 */
void gv_set_drapesurfs(geovect *gv, int *hsurfs, int nsurfs)
{
    int i;

    for (i = 0; i < nsurfs && i < MAX_SURFS; i++) {
        gv->drape_surf_id[i] = hsurfs[i];
    }

    return;
}
