#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "grib2.h"

g2int g2_getfld(unsigned char *cgrib,g2int cgrib_length, g2int ifldnum,g2int unpack,g2int expand,
                gribfield **gfld)
//$$$  SUBPROGRAM DOCUMENTATION BLOCK
//                .      .    .                                       .
// SUBPROGRAM:    g2_getfld
//   PRGMMR: Gilbert         ORG: W/NP11    DATE: 2002-10-28
//
// ABSTRACT: This subroutine returns all the metadata, template values,
//   Bit-map ( if applicable ), and the unpacked data for a given data
//   field.  All of the information returned is stored in a gribfield
//   structure, which is defined in file grib2.h.
//   Users of this routine will need to include "grib2.h" in their source
//   code that calls this routine.  Each component of the gribfield
//   struct is also described in the OUTPUT ARGUMENTS section below.
//
//   Since there can be multiple data fields packed into a GRIB2
//   message, the calling routine indicates which field is being requested
//   with the ifldnum argument.
//
// PROGRAM HISTORY LOG:
// 2002-10-28  Gilbert
//
// USAGE:    #include "grib2.h"
//           int g2_getfld(unsigned char *cgrib,g2int ifldnum,g2int unpack,
//                         g2int expand,gribfield **gfld)
//   INPUT ARGUMENTS:
//     cgrib    - Character pointer to the GRIB2 message
//     ifldnum  - Specifies which field in the GRIB2 message to return.
//     unpack   - Boolean value indicating whether to unpack bitmap/data field
//                1 = unpack bitmap (if present) and data values
//                0 = do not unpack bitmap and data values
//     expand   - Boolean value indicating whether the data points should be
//                expanded to the correspond grid, if a bit-map is present.
//                1 = if possible, expand data field to grid, inserting zero
//                    values at gridpoints that are bitmapped out.
//                    (SEE REMARKS2)
//                0 = do not expand data field, leaving it an array of
//                    consecutive data points for each "1" in the bitmap.
//                This argument is ignored if unpack == 0 OR if the
//                returned field does not contain a bit-map.
//
//   OUTPUT ARGUMENT:
//     gribfield gfld; - pointer to structure gribfield containing
//                       all decoded data for the data field.
//
//        gfld->version = GRIB edition number ( currently 2 )
//        gfld->discipline = Message Discipline ( see Code Table 0.0 )
//        gfld->idsect = Contains the entries in the Identification
//                        Section ( Section 1 )
//                        This element is a pointer to an array
//                        that holds the data.
//            gfld->idsect[0]  = Identification of originating Centre
//                                    ( see Common Code Table C-1 )
//                             7 - US National Weather Service
//            gfld->idsect[1]  = Identification of originating Sub-centre
//            gfld->idsect[2]  = GRIB Master Tables Version Number
//                                    ( see Code Table 1.0 )
//                             0 - Experimental
//                             1 - Initial operational version number
//            gfld->idsect[3]  = GRIB Local Tables Version Number
//                                    ( see Code Table 1.1 )
//                             0     - Local tables not used
//                             1-254 - Number of local tables version used
//            gfld->idsect[4]  = Significance of Reference Time (Code Table 1.2)
//                             0 - Analysis
//                             1 - Start of forecast
//                             2 - Verifying time of forecast
//                             3 - Observation time
//            gfld->idsect[5]  = Year ( 4 digits )
//            gfld->idsect[6]  = Month
//            gfld->idsect[7)  = Day
//            gfld->idsect[8]  = Hour
//            gfld->idsect[9]  = Minute
//            gfld->idsect[10]  = Second
//            gfld->idsect[11]  = Production status of processed data
//                                    ( see Code Table 1.3 )
//                              0 - Operational products
//                              1 - Operational test products
//                              2 - Research products
//                              3 - Re-analysis products
//            gfld->idsect[12]  = Type of processed data ( see Code Table 1.4 )
//                              0  - Analysis products
//                              1  - Forecast products
//                              2  - Analysis and forecast products
//                              3  - Control forecast products
//                              4  - Perturbed forecast products
//                              5  - Control and perturbed forecast products
//                              6  - Processed satellite observations
//                              7  - Processed radar observations
//        gfld->idsectlen = Number of elements in gfld->idsect[].
//        gfld->local   = Pointer to character array containing contents
//                       of Local Section 2, if included
//        gfld->locallen = length of array gfld->local[]
//        gfld->ifldnum = field number within GRIB message
//        gfld->griddef = Source of grid definition (see Code Table 3.0)
//                      0 - Specified in Code table 3.1
//                      1 - Predetermined grid Defined by originating centre
//        gfld->ngrdpts = Number of grid points in the defined grid.
//        gfld->numoct_opt = 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.
//        gfld->interp_opt = Interpretation of list for optional points
//                          definition.  (Code Table 3.11)
//        gfld->igdtnum = Grid Definition Template Number (Code Table 3.1)
//        gfld->igdtmpl  = Contains the data values for the specified Grid
//                         Definition Template ( NN=gfld->igdtnum ).  Each
//                         element of this integer array contains an entry (in
//                         the order specified) of Grid Definition Template 3.NN
//                         This element is a pointer to an array
//                         that holds the data.
//        gfld->igdtlen = Number of elements in gfld->igdtmpl[].  i.e. number of
//                       entries in Grid Definition Template 3.NN
//                       ( NN=gfld->igdtnum ).
//        gfld->list_opt  = (Used if gfld->numoct_opt .ne. 0)  This array
//                          contains the number of grid points contained in
//                          each row ( or column ).  (part of Section 3)
//                          This element is a pointer to an array
//                          that holds the data.  This pointer is nullified
//                          if gfld->numoct_opt=0.
//        gfld->num_opt = (Used if gfld->numoct_opt .ne. 0)
//                        The number of entries
//                       in array ideflist.  i.e. number of rows ( or columns )
//                       for which optional grid points are defined.  This value
//                       is set to zero, if gfld->numoct_opt=0.
//        gfdl->ipdtnum = Product Definition Template Number(see Code Table 4.0)
//        gfld->ipdtmpl  = Contains the data values for the specified Product
//                         Definition Template ( N=gfdl->ipdtnum ). Each element
//                         of this integer array contains an entry (in the
//                         order specified) of Product Definition Template 4.N.
//                         This element is a pointer to an array
//                         that holds the data.
//        gfld->ipdtlen = Number of elements in gfld->ipdtmpl[].  i.e. number of
//                       entries in Product Definition Template 4.N
//                       ( N=gfdl->ipdtnum ).
//        gfld->coord_list  = 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)
//                            This element is a pointer to an array
//                            that holds the data.
//        gfld->num_coord = number of values in array gfld->coord_list[].
//        gfld->ndpts = Number of data points unpacked and returned.
//        gfld->idrtnum = Data Representation Template Number
//                       ( see Code Table 5.0)
//        gfld->idrtmpl  = Contains the data values for the specified Data
//                         Representation Template ( N=gfld->idrtnum ).  Each
//                         element of this integer array contains an entry
//                         (in the order specified) of Product Definition
//                         Template 5.N.
//                         This element is a pointer to an array
//                         that holds the data.
//        gfld->idrtlen = Number of elements in gfld->idrtmpl[].  i.e. number
//                       of entries in Data Representation Template 5.N
//                       ( N=gfld->idrtnum ).
//        gfld->unpacked = logical value indicating whether the bitmap and
//                        data values were unpacked.  If false,
//                        gfld->bmap and gfld->fld pointers are nullified.
//        gfld->expanded = Logical value indicating whether the data field
//                         was expanded to the grid in the case where a
//                         bit-map is present.  If true, the data points in
//                         gfld->fld match the grid points and zeros were
//                         inserted at grid points where data was bit-mapped
//                         out.  If false, the data values in gfld->fld were
//                         not expanded to the grid and are just a consecutive
//                         array of data points corresponding to each value of
//                         "1" in gfld->bmap.
//        gfld->ibmap = Bitmap indicator ( see Code Table 6.0 )
//                     0 = bitmap applies and is included in Section 6.
//                     1-253 = Predefined bitmap applies
//                     254 = Previously defined bitmap applies to this field
//                     255 = Bit map does not apply to this product.
//        gfld->bmap  = integer array containing decoded bitmap,
//                      if gfld->ibmap=0 or gfld->ibap=254.  Otherwise nullified
//                      This element is a pointer to an array
//                      that holds the data.
//        gfld->fld  = Array of gfld->ndpts unpacked data points.
//                     This element is a pointer to an array
//                     that holds the data.
//
//
//   RETURN VALUES:
//     ierr     - Error return code.
//                0 = no error
//                1 = Beginning characters "GRIB" not found.
//                2 = GRIB message is not Edition 2.
//                3 = The data field request number was not positive.
//                4 = End string "7777" found, but not where expected.
//                6 = GRIB message did not contain the requested number of
//                    data fields.
//                7 = End string "7777" not found at end of message.
//                8 = Unrecognized Section encountered.
//                9 = Data Representation Template 5.NN not yet implemented.
//               15 = Error unpacking Section 1.
//               16 = Error unpacking Section 2.
//               10 = Error unpacking Section 3.
//               11 = Error unpacking Section 4.
//               12 = Error unpacking Section 5.
//               13 = Error unpacking Section 6.
//               14 = Error unpacking Section 7.
//               17 = Previous bitmap specified, yet none exists.
//
// REMARKS: Note that struct gribfield is allocated by this routine and it
//          also contains pointers to many arrays of data that were allocated
//          during decoding.  Users are encouraged to free up this memory,
//          when it is no longer needed, by an explicit call to routine g2_free.
//          EXAMPLE:
//              #include "grib2.h"
//              gribfield *gfld;
//              ret=g2_getfld(cgrib,1,1,1,&gfld);
//                ...
//              g2_free(gfld);
//
//          Routine g2_info can be used to first determine
//          how many data fields exist in a given GRIB message.
//
// REMARKS2: It may not always be possible to expand a bit-mapped data field.
//           If a pre-defined bit-map is used and not included in the GRIB2
//           message itself, this routine would not have the necessary
//           information to expand the data.  In this case, gfld->expanded would
//           would be set to 0 (false), regardless of the value of input
//           argument expand.
//
// ATTRIBUTES:
//   LANGUAGE: C
//   MACHINE:
//
//$$$
{

      g2int have3,have4,have5,have6,have7,ierr,jerr;
      g2int numfld,j,n,istart,iofst,ipos;
      g2int disc,ver,lensec0,lengrib,lensec,isecnum;
      g2int  *igds;
      g2int *bmpsave;
      g2float *newfld;
      gribfield  *lgfld;
      int iofst_last_sect6 = -1;

      have3=0;
      have4=0;
      have5=0;
      have6=0;
      have7=0;
      ierr=0;
      numfld=0;

      lgfld=(gribfield *)malloc(sizeof(gribfield));
      *gfld=lgfld;

      lgfld->locallen=0;
      lgfld->idsect=0;
      lgfld->local=0;
      lgfld->list_opt=0;
      lgfld->igdtmpl=0;
      lgfld->ipdtmpl=0;
      lgfld->idrtmpl=0;
      lgfld->coord_list=0;
      lgfld->bmap=0;
      lgfld->fld=0;
      lgfld->ngrdpts=0;
//
//  Check for valid request number
//
      if (ifldnum <= 0) {
        printf("g2_getfld: Request for field number must be positive.\n");
        ierr=3;
        return(ierr);
      }
//
//  Check for beginning of GRIB message in the first 100 bytes
//
      istart=-1;
      for (j=0;j<100;j++) {
        if (cgrib[j]=='G' && cgrib[j+1]=='R' &&cgrib[j+2]=='I' &&
            cgrib[j+3]=='B') {
          istart=j;
          break;
        }
      }
      if (istart == -1) {
        printf("g2_getfld:  Beginning characters GRIB not found.\n");
        ierr=1;
        return(ierr);
      }
//
//  Unpack Section 0 - Indicator Section
//
      iofst=8*(istart+6);
      gbit(cgrib,&disc,iofst,8);     // Discipline
      iofst=iofst+8;
      gbit(cgrib,&ver,iofst,8);     // GRIB edition number
      iofst=iofst+8;
      iofst=iofst+32;
      gbit(cgrib,&lengrib,iofst,32);        // Length of GRIB message
      iofst=iofst+32;
      lensec0=16;
      ipos=istart+lensec0;
//
//  Currently handles only GRIB Edition 2.
//
      if (ver != 2) {
        printf("g2_getfld: can only decode GRIB edition 2.\n");
        ierr=2;
        return(ierr);
      }
//
//  Loop through the remaining sections keeping track of the
//  length of each.  Also keep the latest Grid Definition Section info.
//  Unpack the requested field number.
//
      for (;;) {
        //    Check to see if we are at end of GRIB message
        if (cgrib[ipos]=='7' && cgrib[ipos+1]=='7' && cgrib[ipos+2]=='7' &&
            cgrib[ipos+3]=='7') {
          ipos=ipos+4;
          //    If end of GRIB message not where expected, issue error
          if (ipos != (istart+lengrib)) {
            printf("g2_getfld: '7777' found, but not where expected.\n");
            ierr=4;
            return(ierr);
          }
          break;
        }
        //     Get length of Section and Section number
        //iofst=(ipos-1)*8;
        iofst=ipos*8;
        gbit(cgrib,&lensec,iofst,32);        // Get Length of Section
        iofst=iofst+32;
        gbit(cgrib,&isecnum,iofst,8);         // Get Section number
        iofst=iofst+8;
        //printf(" lensec= %ld    secnum= %ld \n",lensec,isecnum);
        //
        //  Check to see if section number is valid
        //
        if ( isecnum<1 || isecnum>7 ) {
          printf("g2_getfld: Unrecognized Section Encountered=%d\n",isecnum);
          ierr=8;
          return(ierr);
        }
        //
        //   If found Section 1, decode elements in Identification Section
        //
        if (isecnum == 1) {
          iofst=iofst-40;       // reset offset to beginning of section
          jerr=g2_unpack1(cgrib,&iofst,&lgfld->idsect,&lgfld->idsectlen);
          if (jerr !=0 ) {
            ierr=15;
            return(ierr);
          }
        }
        //
        //   If found Section 2, Grab local section
        //   Save in case this is the latest one before the requested field.
        //
        if (isecnum == 2) {
          iofst=iofst-40;       // reset offset to beginning of section
          if (lgfld->local!=0) free(lgfld->local);
          jerr=g2_unpack2(cgrib,&iofst,&lgfld->locallen,&lgfld->local);
          if (jerr != 0) {
            ierr=16;
            return(ierr);
          }
        }
        //
        //   If found Section 3, unpack the GDS info using the
        //   appropriate template.  Save in case this is the latest
        //   grid before the requested field.
        //
        if (isecnum == 3) {
          iofst=iofst-40;       // reset offset to beginning of section
          if (lgfld->igdtmpl!=0) free(lgfld->igdtmpl);
          if (lgfld->list_opt!=0) free(lgfld->list_opt);
          jerr=g2_unpack3(cgrib,cgrib_length,&iofst,&igds,&lgfld->igdtmpl,
                          &lgfld->igdtlen,&lgfld->list_opt,&lgfld->num_opt);
          if (jerr == 0) {
            have3=1;
            lgfld->griddef=igds[0];
            lgfld->ngrdpts=igds[1];
            lgfld->numoct_opt=igds[2];
            lgfld->interp_opt=igds[3];
            lgfld->igdtnum=igds[4];
            free(igds);
          }
          else {
            ierr=10;
            free( igds );
            return(ierr);
          }
        }
        //
        //   If found Section 4, check to see if this field is the
        //   one requested.
        //
        if (isecnum == 4) {
          numfld=numfld+1;
          if (numfld == ifldnum) {
            lgfld->discipline=disc;
            lgfld->version=ver;
            lgfld->ifldnum=ifldnum;
            lgfld->unpacked=unpack;
            lgfld->expanded=0;
            iofst=iofst-40;       // reset offset to beginning of section
            jerr=g2_unpack4(cgrib,cgrib_length,&iofst,&lgfld->ipdtnum,
                            &lgfld->ipdtmpl,&lgfld->ipdtlen,&lgfld->coord_list,
                            &lgfld->num_coord);
            if (jerr == 0 || jerr == 5 )
              have4=1;
            else {
              ierr=11;
              return(ierr);
            }
          }
        }
        //
        //   If found Section 5, check to see if this field is the
        //   one requested.
        //
        if (isecnum == 5 && numfld == ifldnum) {
          iofst=iofst-40;       // reset offset to beginning of section
          jerr=g2_unpack5(cgrib,cgrib_length,&iofst,&lgfld->ndpts,&lgfld->idrtnum,
                          &lgfld->idrtmpl,&lgfld->idrtlen);
          if (jerr == 0)
            have5=1;
          else {
            ierr=12;
            return(ierr);
          }
        }
        //
        //   If found Section 6, Unpack bitmap.
        //   Save in case this is the latest
        //   bitmap before the requested field.
        //
        if (isecnum == 6) {
          if (numfld==ifldnum && unpack) {   // unpack bitmap
            iofst=iofst-40;           // reset offset to beginning of section
            bmpsave=lgfld->bmap;      // save pointer to previous bitmap
            jerr=g2_unpack6(cgrib,cgrib_length,&iofst,lgfld->ngrdpts,&lgfld->ibmap,
                         &lgfld->bmap);
            if (jerr == 0) {
              have6=1;
              if (lgfld->ibmap == 254)     // use previously specified bitmap
                 if( bmpsave!=0 )
                    lgfld->bmap=bmpsave;
                 else {

                    if( ifldnum > 1 && iofst_last_sect6 > 0 )
                    {
                        /* Unpack the last valid section 6 we read before */
                        int iofst_backup = iofst;
                        iofst = iofst_last_sect6 - 40;
                        jerr=g2_unpack6(cgrib,cgrib_length,&iofst,lgfld->ngrdpts,&lgfld->ibmap,
                                        &lgfld->bmap);
                        lgfld->ibmap = 254;
                        iofst = iofst_backup;
                        if( jerr != 0 ) {
                            ierr=13;
                            return(ierr);
                        }
                    }
                    else
                    {
                        printf("g2_getfld: Prev bit-map specified, but none exist.\n");
                        ierr=17;
                        return(ierr);
                    }
                 }
              else                         // get rid of it
                 if( bmpsave!=0 ) free(bmpsave);
            }
            else {
              ierr=13;
              return(ierr);
            }
          }
          else {    // do not unpack bitmap
            gbit(cgrib,&lgfld->ibmap,iofst,8);      // Get BitMap Indicator
            have6=1;
            if( lgfld->ibmap == 0 ) {
                /* Save the offset of this section in case we might need to unpack it later for a later subfield */
                iofst_last_sect6 = iofst;
            }
          }
        }
        //
        //   If found Section 7, check to see if this field is the
        //   one requested.
        //
        if (isecnum==7 && numfld==ifldnum && unpack) {
          iofst=iofst-40;       // reset offset to beginning of section
          
          /* If expand is requested and we cannot do it, then early exit */
          /* to avoid useless operations */
          /* Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2183 */
          /* See grib2api.c : */
          /* Check if NCEP had problems expanding the data.  If so we currently
            * abort.  May need to revisit this behavior. */
          if( expand )
          {
              if ( lgfld->ibmap != 255 && lgfld->bmap != 0 )
              {
                  if( lgfld->ngrdpts < lgfld->ndpts )
                  {
                      /* There are more points in the data section than in */
                      /* the bitmap, that doesn't make sense (bitmap only */
                      /* makes sense if it saves the encoding of points in */
                      /* the data section) */
                      ierr=14;
                      return(ierr);
                  }
              }
              else if( lgfld->ngrdpts != lgfld->ndpts )
              {
                  ierr=14;
                  return(ierr);
              }
          }

          jerr=g2_unpack7(cgrib,cgrib_length,&iofst,lgfld->igdtnum,lgfld->igdtmpl,
                          lgfld->idrtnum,lgfld->idrtmpl,lgfld->ndpts,
                          &lgfld->fld);
          if (jerr == 0) {
            have7=1;
            //  If bitmap is used with this field, expand data field
            //  to grid, if possible.
            if ( lgfld->ibmap != 255 && lgfld->bmap != 0 ) {
               if ( expand == 1 ) {
                  n=0;
                  newfld=(g2float *)calloc(lgfld->ngrdpts,sizeof(g2float));
                  for (j=0;j<lgfld->ngrdpts;j++) {
                      if (lgfld->bmap[j]==1)
                      {
                          if( n >= lgfld->ndpts )
                          {
                              printf("g2_getfld: overflow of lgfld->fld array\n");
                              ierr=14;
                              free(newfld);
                              return(ierr);
                          }
                          newfld[j]=lgfld->fld[n++];
                      }
                  }
                  free(lgfld->fld);
                  lgfld->fld=newfld;
                  lgfld->expanded=1;
               }
               else {
                  lgfld->expanded=0;
               }
            }
            else {
               if( lgfld->ngrdpts != lgfld->ndpts )
               {
                   /* Added by E. Rouault to fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2070 */
                   lgfld->expanded=0;
               }
               else 
               {
                    lgfld->expanded=1;
               }
            }
          }
          else {
            printf("g2_getfld: return from g2_unpack7 = %d \n",(int)jerr);
            ierr=14;
            return(ierr);
          }
        }
        //
        //   Check to see if we read pass the end of the GRIB
        //   message and missed the terminator string '7777'.
        //
        ipos=ipos+lensec;                // Update beginning of section pointer
        if (ipos > (istart+lengrib)) {
          printf("g2_getfld: '7777'  not found at end of GRIB message.\n");
          ierr=7;
          return(ierr);
        }
        //
        //  If unpacking requested, return when all sections have been
        //  processed
        //
        if (unpack && have3 && have4 && have5 && have6 && have7)
            return(ierr);
        //
        //  If unpacking is not requested, return when sections
        //  3 through 6 have been processed
        //
        if ((! unpack) && have3 && have4 && have5 && have6)
            return(ierr);

      }

//
//  If exited from above loop, the end of the GRIB message was reached
//  before the requested field was found.
//
      printf("g2_getfld: GRIB message contained %d different fields.\n",numfld);
      printf("g2_getfld: The request was for field %d.\n",ifldnum);
      ierr=6;

      return(ierr);

}
