/** @file
 * @brief Unpack Section 3 (Grid Definition Section) as defined
 * in GRIB Edition 2.
 * @author Stephen Gilbert @date 2002-10-31
 */
#include <stdio.h>
#include <stdlib.h>
#include "grib2.h"

/**
 * This routine unpacks Section 3 (Grid Definition Section) as defined
 * in GRIB Edition 2.
 *
 * PROGRAM HISTORY LOG:
 * - 2002-10-31  Gilbert
 * - 2009-01-14  Vuong Changed structure name template to gtemplate
 *
 * @param cgrib Char array ontaining Section 3 of the GRIB2 message.
 * @param iofst Bit offset for the beginning of Section 3 in cgrib.
 * @param igds Contains information read from the appropriate GRIB
 * Grid Definition Section 3 for the field being returned.
 * - igds[0] Source of grid definition (see Code Table 3.0)
 * - igds[1] Number of grid points in the defined grid.
 * - igds[2] Number of octets needed for each additional grid points
 *   definition. Used to define number of points in each row (or
 *   column) for non-regular grids. = 0, if using regular grid.
 * - igds[3] Interpretation of list for optional points
 *   definition. (Code Table 3.11)
 * - igds[4] Grid Definition Template Number (Code Table 3.1).
 * @param igdstmpl - Pointer to integer array containing the data
 * values for the specified Grid Definition Template
 * (NN=igds[4]). Each element of this integer array contains an entry
 * (in the order specified) of Grid Defintion Template 3.NN
 * @param mapgridlen- Number of elements in igdstmpl[]. i.e. number of
 * entries in Grid Defintion Template 3.NN (NN=igds[4]).
 * @param ideflist (Used if igds[2] .ne. 0) Pointer to integer array
 * containing the number of grid points contained in each row ( or
 * column ). (part of Section 3).
 * @param idefnum (Used if igds[2] .ne. 0) The number of entries in
 * array ideflist. i.e. number of rows (or columns) for which optional
 * grid points are defined.
 *
 * @return
 * - 0 no error
 * - 2 Not Section 3
 * - 5 "GRIB" message contains an undefined Grid Definition Template.
 * - 6 memory allocation error
 *
 * @author Stephen Gilbert @date 2002-10-31
 */

g2int
g2_unpack3(unsigned char *cgrib, g2int *iofst, g2int **igds, g2int **igdstmpl,
           g2int *mapgridlen, g2int **ideflist, g2int *idefnum)
{
    g2int ierr,i,j,nbits,isecnum;
    g2int lensec,ibyttem=0,isign,newlen;
    g2int *ligds,*ligdstmpl=0,*lideflist=0;
    gtemplate *mapgrid;

    ierr=0;
    *igds=0;       /*  NULL */
    *igdstmpl=0;       /* NULL */
    *ideflist=0;       /* NULL */

    gbit(cgrib,&lensec,*iofst,32);        /* Get Length of Section */
    *iofst=*iofst+32;
    gbit(cgrib,&isecnum,*iofst,8);         /* Get Section Number */
    *iofst=*iofst+8;

    if ( isecnum != 3 ) {
        ierr=2;
        *idefnum=0;
        *mapgridlen=0;
        /* fprintf(stderr,"g2_unpack3: Not Section 3 data.\n"); */
        return(ierr);
    }

    ligds=(g2int *)calloc(5,sizeof(g2int));
    *igds=ligds;

    gbit(cgrib,ligds+0,*iofst,8);     /* Get source of Grid def. */
    *iofst=*iofst+8;
    gbit(cgrib,ligds+1,*iofst,32);    /* Get number of grid pts. */
    *iofst=*iofst+32;
    gbit(cgrib,ligds+2,*iofst,8);     /* Get num octets for opt. list */
    *iofst=*iofst+8;
    gbit(cgrib,ligds+3,*iofst,8);     /* Get interpret. for opt. list */
    *iofst=*iofst+8;
    gbit(cgrib,ligds+4,*iofst,16);    /* Get Grid Def Template num. */
    *iofst=*iofst+16;

    if (ligds[4] != 65535) {
        /*   Get Grid Definition Template */
        mapgrid=getgridtemplate(ligds[4]);
        if (mapgrid == 0) {         /* undefined template */
            ierr=5;
            return(ierr);
        }
        *mapgridlen=mapgrid->maplen;

        /*   Unpack each value into array igdstmpl from the the
         *   appropriate number of octets, which are specified in
         *   corresponding entries in array mapgrid. */
        if (*mapgridlen > 0) {
            ligdstmpl=0;
            ligdstmpl=(g2int *)calloc(*mapgridlen,sizeof(g2int));
            if (ligdstmpl == 0) {
                ierr=6;
                *mapgridlen=0;
                *igdstmpl=0;    /* NULL */
                if( mapgrid != 0 ) free(mapgrid);
                return(ierr);
            }
            else {
                *igdstmpl=ligdstmpl;
            }
        }
        ibyttem=0;
        for (i=0;i<*mapgridlen;i++) {
            nbits=abs(mapgrid->map[i])*8;
            if ( mapgrid->map[i] >= 0 ) {
                gbit(cgrib,ligdstmpl+i,*iofst,nbits);
            }
            else {
                gbit(cgrib,&isign,*iofst,1);
                gbit(cgrib,ligdstmpl+i,*iofst+1,nbits-1);
                if (isign == 1) ligdstmpl[i]=-1*ligdstmpl[i];
            }
            *iofst=*iofst+nbits;
            ibyttem=ibyttem+abs(mapgrid->map[i]);
        }

        /*   Check to see if the Grid Definition Template needs to be
         *   extended. The number of values in a specific template may
         *   vary depending on data specified in the "static" part of
         *   the gtemplate. */
        if ( mapgrid->needext == 1 ) {
            free(mapgrid);
            mapgrid=extgridtemplate(ligds[4],ligdstmpl);
            /*   Unpack the rest of the Grid Definition Template */
            newlen=mapgrid->maplen+mapgrid->extlen;
            ligdstmpl=(g2int *)realloc(ligdstmpl,newlen*sizeof(g2int));
            *igdstmpl=ligdstmpl;
            j=0;
            for (i=*mapgridlen;i<newlen;i++) {
                nbits=abs(mapgrid->ext[j])*8;
                if ( mapgrid->ext[j] >= 0 ) {
                    gbit(cgrib,ligdstmpl+i,*iofst,nbits);
                }
                else {
                    gbit(cgrib,&isign,*iofst,1);
                    gbit(cgrib,ligdstmpl+i,*iofst+1,nbits-1);
                    if (isign == 1) ligdstmpl[i]=-1*ligdstmpl[i];
                }
                *iofst=*iofst+nbits;
                ibyttem=ibyttem+abs(mapgrid->ext[j]);
                j++;
            }
            *mapgridlen=newlen;
        }
        if( mapgrid->ext != 0 ) free(mapgrid->ext);
        if( mapgrid != 0 ) free(mapgrid);
    }
    else {              /* No Grid Definition Template */
        *mapgridlen=0;
        *igdstmpl=0;
    }

    /* Unpack optional list of numbers defining number of points in
     * each row or column, if included.  This is used for non
     * regular grids. */
    if ( ligds[2] != 0 ) {
        nbits=ligds[2]*8;
        *idefnum=(lensec-14-ibyttem)/ligds[2];
        if (*idefnum > 0) lideflist=(g2int *)calloc(*idefnum,sizeof(g2int));
        if (lideflist == 0) {
            ierr=6;
            *idefnum=0;
            *ideflist=0;   /* NULL */
            return(ierr);
        }
        else {
            *ideflist=lideflist;
        }
        gbits(cgrib,lideflist,*iofst,nbits,0,*idefnum);
        *iofst=*iofst+(nbits*(*idefnum));
    }
    else {
        *idefnum=0;
        *ideflist=0;    /* NULL */
    }

    return(ierr);    /* End of Section 3 processing */
}
