/****************************************************************************
*
* MODULE:       Vector library 
*   	    	
* AUTHOR(S):    Radim Blazek.
*
* PURPOSE:      Lower level functions for reading/writing/manipulating vectors.
*
* COPYRIGHT:    (C) 2001 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 <string.h>
#include <grass/gis.h>
#include <grass/Vect.h>

int 
dig_write_cidx_head ( GVFILE * fp, struct Plus_head *plus)
{
    int i;
    unsigned char buf[5];
    long length = 9;

    G_debug (3, "dig_write_cidx_head()" );

    dig_rewind (fp);
    dig_set_cur_port (&(plus->cidx_port));

    /* Head of header */
    /* bytes 1 - 5 */
    buf[0] = GV_CIDX_VER_MAJOR;
    buf[1] = GV_CIDX_VER_MINOR;
    buf[2] = GV_CIDX_EARLIEST_MAJOR;
    buf[3] = GV_CIDX_EARLIEST_MINOR;
    buf[4] = plus->cidx_port.byte_order;
    if (0 >= dig__fwrite_port_C (buf, 5, fp)) return (-1);

    /* bytes 6 - 9 : header size */
    if (0 >= dig__fwrite_port_L ( &length, 1, fp)) return (0);

    /* Body of header - info about all fields */
    /* Number of fields */
    if (0 >= dig__fwrite_port_I ( &(plus->n_cidx), 1, fp)) return (-1);

    for ( i = 0; i < plus->n_cidx; i++ ) {
	int t;
        struct Cat_index *ci;

	ci = &(plus->cidx[i]);
    
	G_debug (3, "cidx %d head offset: %d", i, dig_ftell( fp) );

	/* Field number */
        if (0 >= dig__fwrite_port_I ( &(ci->field), 1, fp)) return (-1);

	/* Number of categories */
        if (0 >= dig__fwrite_port_I ( &(ci->n_cats), 1, fp)) return (-1);
	
	/* Number of unique categories */
        if (0 >= dig__fwrite_port_I ( &(ci->n_ucats), 1, fp)) return (-1);

	/* Number of types */
        if (0 >= dig__fwrite_port_I ( &(ci->n_types), 1, fp)) return (-1);

	/* Types */
	for ( t = 0; t < ci->n_types; t++ ) {
	    int wtype;
	    
	    /* type */
	    wtype = dig_type_to_store ( ci->type[t][0] );
            if (0 >= dig__fwrite_port_I ( &wtype, 1, fp)) return (-1);

	    /* number of items */
            if (0 >= dig__fwrite_port_I ( &(ci->type[t][1]), 1, fp)) return (-1);

	}

	/* Offset */
	if (0 >= dig__fwrite_port_L ( &(ci->offset), 1, fp)) return (0);
	G_debug (3, "cidx %d offset: %d", i, ci->offset);
    }

    G_debug (3, "cidx body offset %d", dig_ftell( fp) );

    return (0);
}

/* return: 0 OK, -1 error */
int 
dig_read_cidx_head (   GVFILE * fp, struct Plus_head *plus)
{
    unsigned char buf[5];
    int i, byte_order;

    dig_rewind (fp);

    /* bytes 1 - 5 */
    if (0 >= dig__fread_port_C (buf, 5, fp))  return (-1);
    plus->cidx_Version_Major = buf[0];
    plus->cidx_Version_Minor = buf[1];
    plus->cidx_Back_Major    = buf[2];
    plus->cidx_Back_Minor    = buf[3];
    byte_order               = buf[4];

    G_debug (3, "Cidx header: file version %d.%d , supported from GRASS version %d.%d", 
		plus->cidx_Version_Major, plus->cidx_Version_Minor,
		plus->cidx_Back_Major, plus->cidx_Back_Minor );

    G_debug (3, "  byte order %d", byte_order );

    /* check version numbers */
    if ( plus->cidx_Version_Major > GV_CIDX_VER_MAJOR || plus->cidx_Version_Minor > GV_CIDX_VER_MINOR ) {
      /* The file was created by GRASS library with higher version than this one */

  	if ( plus->cidx_Back_Major > GV_CIDX_VER_MAJOR || plus->cidx_Back_Minor > GV_CIDX_VER_MINOR ) {
          /* This version of GRASS lib is lower than the oldest which can read this format */	  
	  G_fatal_error ( "Category index format version %d.%d is not supported by this release."
			  " Try to rebuild topology or upgrade GRASS.", 
			   plus->cidx_Version_Major, plus->cidx_Version_Minor);
	  return (-1);
      }

      G_warning ( "Your GRASS version does not fully support category index format %d.%d of the vector."
		  " Consider to rebuild topology or upgrade GRASS.",
		      plus->cidx_Version_Major, plus->cidx_Version_Minor );
    }

    dig_init_portable ( &(plus->cidx_port), byte_order);
    dig_set_cur_port ( &(plus->cidx_port) );

    /* bytes 6 - 9 : header size */
    if (0 >= dig__fread_port_L (&(plus->cidx_head_size), 1, fp)) return (-1);
    G_debug (3, "  header size %d", plus->cidx_head_size );

    /* Body of header - info about all fields */
    /* Number of fields */
    if (0 >= dig__fread_port_I ( &(plus->n_cidx), 1, fp)) return (-1);
    
    /* alloc space */
    plus->a_cidx = plus->n_cidx;
    plus->cidx = (struct Cat_index*) G_malloc(  plus->a_cidx * sizeof( struct Cat_index ) );

    for ( i = 0; i < plus->n_cidx; i++ ) {
	int t;
        struct Cat_index *ci;

	ci = &(plus->cidx[i]);
	ci->cat = NULL;
	ci->a_cats = 0;

	/* Field number */
        if (0 >= dig__fread_port_I ( &(ci->field), 1, fp)) return (-1);

	/* Number of categories */
        if (0 >= dig__fread_port_I ( &(ci->n_cats), 1, fp)) return (-1);
	
	/* Number of unique categories */
        if (0 >= dig__fread_port_I ( &(ci->n_ucats), 1, fp)) return (-1);

	/* Number of types */
        if (0 >= dig__fread_port_I ( &(ci->n_types), 1, fp)) return (-1);

	/* Types */
	for ( t = 0; t < ci->n_types; t++ ) {
	    int rtype;
	    
	    /* type */
            if (0 >= dig__fread_port_I ( &rtype, 1, fp)) return (-1);
	    ci->type[t][0] = dig_type_from_store ( rtype );

	    /* number of items */
            if (0 >= dig__fread_port_I ( &(ci->type[t][1]), 1, fp)) return (-1);
	}

	/* Offset */
	if (0 >= dig__fread_port_L ( &(ci->offset), 1, fp)) return (0);
    }

    if ( dig_fseek ( fp, plus->cidx_head_size, SEEK_SET ) == -1 ) return (-1);

    return (0);
}

/* Write spatial index */
int
dig_write_cidx ( GVFILE * fp, struct Plus_head *plus)
{
    int i;

    dig_set_cur_port(&(plus->cidx_port));
    dig_rewind (fp);

    dig_write_cidx_head ( fp, plus );

    /* Write category-type-id for each field */ 
    for ( i = 0; i < plus->n_cidx; i++ ) {
	int j;
        struct Cat_index *ci;

	ci = &(plus->cidx[i]);
	ci->offset = dig_ftell ( fp );

        /* convert type  */
	for ( j = 0; j < ci->n_cats; j++ )
	    ci->cat[j][1] = dig_type_to_store ( ci->cat[j][1] );
	
        if (0 >= dig__fwrite_port_I ( (int *)ci->cat, 3 * ci->n_cats, fp)) return (-1);

	/* Return back */
	for ( j = 0; j < ci->n_cats; j++ )
	    ci->cat[j][1] = dig_type_from_store ( ci->cat[j][1] );
    }

    dig_write_cidx_head ( fp, plus ); /* rewrite with offsets */

    return 0; 
}

/* Read spatial index file 
 * returns 0 - OK
 *         1 - error
 */
int
dig_read_cidx ( GVFILE * fp, struct Plus_head *plus, int head_only)
{
    int i;

    G_debug (3, "dig_read_cidx()");

    dig_cidx_init ( plus);

    dig_rewind (fp);
    if ( dig_read_cidx_head ( fp, plus) == -1 ) {
	G_debug (3, "Cannot read cidx head");
	return 1;
    }

    if ( head_only ) {
        plus->cidx_up_to_date = 1; /* OK ?*/
	return 0;
    }
    
    dig_set_cur_port(&(plus->cidx_port));

    /* Read category-type-id for each field */ 
    for ( i = 0; i < plus->n_cidx; i++ ) {
	int j;
        struct Cat_index *ci;

	ci = &(plus->cidx[i]);
	ci->a_cats = ci->n_cats;
	ci->cat = G_malloc ( ci->a_cats * 3 * sizeof(int) );

	if ( dig_fseek ( fp, ci->offset, 0) == -1 ) return 1;
	
        if (0 >= dig__fread_port_I ( (int *)ci->cat, 3 * ci->n_cats, fp)) return 1;

        /* convert type  */
	for ( j = 0; j < ci->n_cats; j++ )
	    ci->cat[j][1] = dig_type_from_store ( ci->cat[j][1] );
    }


    plus->cidx_up_to_date = 1;
	 
    return 0; 
}

