/*!
   \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->flat_val = 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 succcess
 */
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;
}
