/** @file
 * @brief Unpack Section 4 (Product 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 subroutine unpacks Section 4 (Product 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 containing Section 4 of the GRIB2 message.
 * @param iofst Bit offset of the beginning of Section 4 in
 * cgrib. Returned with updated bit offset.
 * @param ipdsnum Product Definition Template Number (see Code Table 4.0).
 * @param ipdstmpl Pointer to integer array containing the data
 * values for the specified Product Definition Template
 * (N=ipdsnum). Each element of this integer array contains an entry
 * (in the order specified) of Product Defintion Template 4.N.
 * @param mappdslen Number of elements in ipdstmpl. i.e. number of
 * entries in Product Defintion Template 4.N (N=ipdsnum).
 * @param coordlist Pointer to real array containing floating point
 * values intended to document the vertical discretisation associated
 * to model data on hybrid coordinate vertical levels. (part of
 * Section 4).
 * @param numcoord number of values in array coordlist.
 *
 * @returns
 * - 0 no error
 * - 2 Not section 4
 * - 5 "GRIB" message contains an undefined Product Definition Template.
 * - 6 memory allocation error
 *
 * @author Stephen Gilbert @date 2002-10-31
 */
g2int
g2_unpack4(unsigned char *cgrib, g2int *iofst, g2int *ipdsnum, g2int **ipdstmpl,
           g2int *mappdslen, g2float **coordlist, g2int *numcoord)
{

    g2int ierr,needext,i,j,nbits,isecnum;
    g2int lensec,isign,newlen;
    g2int *coordieee;
    g2int *lipdstmpl=0;
    g2float *lcoordlist;
    gtemplate *mappds;

    ierr=0;
    *ipdstmpl=0;    /* NULL */
    *coordlist=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 != 4 ) {
        ierr=2;
        *numcoord=0;
        *mappdslen=0;
        /* fprintf(stderr,"g2_unpack4: Not Section 4 data.\n");*/
        return(ierr);
    }

    gbit(cgrib,numcoord,*iofst,16);    /* Get num of coordinate values */
    *iofst=*iofst+16;
    gbit(cgrib,ipdsnum,*iofst,16);    /* Get Prod. Def Template num. */
    *iofst=*iofst+16;

    /* Get Product Definition Template */
    mappds=getpdstemplate(*ipdsnum);
    if (mappds == 0) {       /* undefine template */
        ierr=5;
        *mappdslen=0;
        return(ierr);
    }
    *mappdslen=mappds->maplen;
    needext=mappds->needext;

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

    /* Check to see if the Product 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 ( needext ==1 ) {
        free(mappds);
        mappds=extpdstemplate(*ipdsnum,lipdstmpl);
        newlen=mappds->maplen+mappds->extlen;
        lipdstmpl=(g2int *)realloc(lipdstmpl,newlen*sizeof(g2int));
        *ipdstmpl=lipdstmpl;
        /*   Unpack the rest of the Product Definition Template */
        j=0;
        for (i=*mappdslen;i<newlen;i++) {
            nbits=abs(mappds->ext[j])*8;
            if ( mappds->ext[j] >= 0 ) {
                gbit(cgrib,lipdstmpl+i,*iofst,nbits);
            }
            else {
                gbit(cgrib,&isign,*iofst,1);
                gbit(cgrib,lipdstmpl+i,*iofst+1,nbits-1);
                if (isign == 1) lipdstmpl[i]=-1*lipdstmpl[i];
            }
            *iofst=*iofst+nbits;
            j++;
        }
        *mappdslen=newlen;
    }
    if( mappds->ext != 0 ) free(mappds->ext);
    if( mappds != 0 ) free(mappds);

    /* Get Optional list of vertical coordinate values after the
     * Product Definition Template, if necessary. */
    *coordlist=0;    /* NULL */
    if ( *numcoord != 0 ) {
        coordieee=(g2int *)calloc(*numcoord,sizeof(g2int));
        lcoordlist=(g2float *)calloc(*numcoord,sizeof(g2float));
        if (coordieee == 0 || lcoordlist == 0) {
            ierr=6;
            *numcoord=0;
            *coordlist=0;    /* NULL */
            if( coordieee != 0 ) free(coordieee);
            if( lcoordlist != 0 ) free(lcoordlist);
            return(ierr);
        }
        else {
            *coordlist=lcoordlist;
        }
        gbits(cgrib,coordieee,*iofst,32,0,*numcoord);
        rdieee(coordieee,*coordlist,*numcoord);
        free(coordieee);
        *iofst=*iofst+(32*(*numcoord));
    }

    return(ierr);    /* End of Section 4 processing */

}
