#include <math.h>
#include <string.h>
#include "grib_int.h"
#include "grib_decode.h"
#include "dmemory.h"

unsigned
correct_bdslen(unsigned bdslen, long recsize, long gribpos)
{
  /*
    If a very large product, the section 4 length field holds
    the number of bytes in the product after section 4 upto
    the end of the padding bytes.
    This is a fixup to get round the restriction on product lengths
    due to the count being only 24 bits. It is only possible because
    the (default) rounding for GRIB products is 120 bytes.
  */
  if (recsize > JP23SET && bdslen <= 120) bdslen = (unsigned) (recsize - gribpos - bdslen);
  return bdslen;
}

int
grib1Sections(unsigned char *gribbuffer, long gribbufsize, unsigned char **pdsp, unsigned char **gdsp, unsigned char **bmsp,
              unsigned char **bdsp, long *gribrecsize)
{
  *gribrecsize = 0;
  *pdsp = NULL;
  *gdsp = NULL;
  *bmsp = NULL;
  *bdsp = NULL;

  unsigned char *section = gribbuffer;
  unsigned char *is = gribbuffer;
  if (!GRIB_START(section))
    {
      fprintf(stderr, "Wrong GRIB indicator section: found >%c%c%c%c<\n", section[0], section[1], section[2], section[3]);
      return -1;
    }

  unsigned recsize = GET_UINT3(section[4], section[5], section[6]);

  int gribversion = GRIB_EDITION(section);
  if (gribversion != 0 && gribversion != 1)
    {
      fprintf(stderr, "Error while decoding GRIB1 sections: GRIB edition %d records not supported!\n", gribversion);
      return -1;
    }

  unsigned grib1offset = (gribversion == 1) ? 4 : 0;

  unsigned char *pds = is + 4 + grib1offset;
  unsigned char *bufpointer = pds + PDS_Len;
  unsigned gribsize = 4 + grib1offset + PDS_Len;

  unsigned char *gds = NULL;
  if (PDS_HAS_GDS)
    {
      gds = bufpointer;
      bufpointer += GDS_Len;
      gribsize += GDS_Len;
    }

  unsigned char *bms = NULL;
  if (PDS_HAS_BMS)
    {
      bms = bufpointer;
      bufpointer += BMS_Len;
      gribsize += BMS_Len;
    }

  unsigned char *bds = bufpointer;
  unsigned bdslen = BDS_Len;
  if (recsize > JP23SET && bdslen <= 120)
    {
      recsize &= JP23SET;
      recsize *= 120;
      bdslen = correct_bdslen(bdslen, recsize, gribsize);
    }
  bufpointer += bdslen;
  gribsize += bdslen;
  gribsize += 4;

  *pdsp = pds;
  *gdsp = gds;
  *bmsp = bms;
  *bdsp = bds;

  *gribrecsize = gribsize;
  if (gribbufsize < gribsize)
    {
      fprintf(stderr, "Inconsistent length of GRIB message (grib_buffer_size=%ld < grib_record_size=%u)!\n", gribbufsize, gribsize);
      return 1;
    }

  if (!GRIB_FIN(bufpointer))  // end section - "7777" in ASCII
    {
      fprintf(stderr, "Missing GRIB end section: found >%c%c%c%c<\n", bufpointer[0], bufpointer[1], bufpointer[2], bufpointer[3]);
      return -2;
    }

  return 0;
}

int
grib2Sections(unsigned char *gribbuffer, long gribbufsize, unsigned char **idsp, unsigned char **lusp, unsigned char **gdsp,
              unsigned char **pdsp, unsigned char **drsp, unsigned char **bmsp, unsigned char **bdsp)
{
  UNUSED(gribbufsize);

  *idsp = NULL;
  *lusp = NULL;
  *gdsp = NULL;
  *pdsp = NULL;
  *drsp = NULL;
  *bmsp = NULL;
  *bdsp = NULL;

  unsigned char *section = gribbuffer;
  unsigned sec_len = 16;

  if (!GRIB_START(section))
    {
      fprintf(stderr, "wrong indicator section >%c%c%c%c<\n", section[0], section[1], section[2], section[3]);
      return -1;
    }

  int gribversion = GRIB_EDITION(section);
  if (gribversion != 2)
    {
      fprintf(stderr, "wrong GRIB version %d\n", gribversion);
      return -1;
    }

  unsigned gribsize = 0;
  for (int i = 0; i < 8; ++i) gribsize = (gribsize << 8) | section[8 + i];

  unsigned grib_len = sec_len;
  section += sec_len;

  /* section 1 */
  sec_len = GRIB2_SECLEN(section);
  int sec_num = GRIB2_SECNUM(section);
  // fprintf(stderr, "ids %d %ld\n", sec_num, sec_len);

  if (sec_num != 1)
    {
      fprintf(stderr, "Unexpected section1 number %d\n", sec_num);
      return -1;
    }

  *idsp = section;

  grib_len += sec_len;
  section += sec_len;

  /* section 2 and 3 */
  sec_len = GRIB2_SECLEN(section);
  sec_num = GRIB2_SECNUM(section);
  // fprintf(stderr, "lus %d %ld\n", sec_num, sec_len);

  if (sec_num == 2)
    {
      *lusp = section;

      grib_len += sec_len;
      section += sec_len;

      /* section 3 */
      sec_len = GRIB2_SECLEN(section);
      // sec_num = GRIB2_SECNUM(section);
      // fprintf(stderr, "gds %d %ld\n", sec_num, sec_len);

      *gdsp = section;
    }
  else if (sec_num == 3)
    {
      *gdsp = section;
    }
  else
    {
      fprintf(stderr, "Unexpected section3 number %d\n", sec_num);
      return -1;
    }

  grib_len += sec_len;
  section += sec_len;

  /* section 4 */
  sec_len = GRIB2_SECLEN(section);
  sec_num = GRIB2_SECNUM(section);
  // fprintf(stderr, "pds %d %ld\n", sec_num, sec_len);

  if (sec_num != 4)
    {
      fprintf(stderr, "Unexpected section4 number %d\n", sec_num);
      return -1;
    }

  *pdsp = section;

  grib_len += sec_len;
  section += sec_len;

  /* section 5 */
  sec_len = GRIB2_SECLEN(section);
  sec_num = GRIB2_SECNUM(section);
  // fprintf(stderr, "drs %d %ld\n", sec_num, sec_len);

  if (sec_num != 5)
    {
      fprintf(stderr, "Unexpected section5 number %d\n", sec_num);
      return -1;
    }

  *drsp = section;

  grib_len += sec_len;
  section += sec_len;

  /* section 6 */
  sec_len = GRIB2_SECLEN(section);
  sec_num = GRIB2_SECNUM(section);
  // fprintf(stderr, "bms %d %ld\n", sec_num, sec_len);

  if (sec_num != 6)
    {
      fprintf(stderr, "Unexpected section6 number %d\n", sec_num);
      return -1;
    }

  *bmsp = section;

  grib_len += sec_len;
  section += sec_len;

  /* section 7 */
  sec_len = GRIB2_SECLEN(section);
  sec_num = GRIB2_SECNUM(section);
  // fprintf(stderr, "bds %d %ld\n", sec_num, sec_len);

  if (sec_num != 7)
    {
      fprintf(stderr, "Unexpected section7 number %d\n", sec_num);
      return -1;
    }

  *bdsp = section;

  grib_len += sec_len;
  section += sec_len;

  /* skip multi GRIB sections */
  int msec = 1;
  while (!GRIB_FIN(section))
    {
      sec_len = GRIB2_SECLEN(section);
      sec_num = GRIB2_SECNUM(section);

      if (sec_num < 1 || sec_num > 7) break;

      if (sec_num == 7) fprintf(stderr, "Skipped unsupported multi GRIB section %d!\n", ++msec);

      if ((grib_len + sec_len) > gribsize) break;

      grib_len += sec_len;
      section += sec_len;
    }

  /* end section - "7777" in ASCII */
  if (!GRIB_FIN(section))
    {
      fprintf(stderr, "Missing end section >%2x %2x %2x %2x<\n", section[0], section[1], section[2], section[3]);
      return -2;
    }

  return 0;
}

int
grib_info_for_grads(off_t recpos, long recsize, unsigned char *gribbuffer, int *intnum, float *fltnum, off_t *bignum)
{
  long gribsize = 0;
  off_t bpos = 0;

  unsigned char *section = gribbuffer;
  unsigned char *is = gribbuffer;
  if (!GRIB_START(section))
    {
      fprintf(stderr, "wrong indicator section >%c%c%c%c<\n", section[0], section[1], section[2], section[3]);
      return -1;
    }

  int gribversion = GRIB_EDITION(section);
  if (recsize == 24 && gribversion == 0) gribversion = 0;

  unsigned grib1offset = (gribversion == 1) ? 4 : 0;

  unsigned char *pds = is + 4 + grib1offset;
  unsigned char *bufpointer = pds + PDS_Len;
  gribsize += 4 + grib1offset + PDS_Len;

  unsigned char *gds = NULL;
  if (PDS_HAS_GDS)
    {
      gds = bufpointer;
      bufpointer += GDS_Len;
      gribsize += GDS_Len;
    }

  unsigned char *bms = NULL;
  if (PDS_HAS_BMS)
    {
      bms = bufpointer;
      bufpointer += BMS_Len;
      bpos = recpos + gribsize + 6;
      gribsize += BMS_Len;
    }

  unsigned char *bds = bufpointer;

  off_t dpos = recpos + gribsize + 11;

  unsigned bdslen = BDS_Len;
  bdslen = correct_bdslen(bdslen, recsize, bds - gribbuffer);
  bufpointer += bdslen;
  gribsize += bdslen;
  gribsize += 4;

  if (gribsize > recsize)
    {
      fprintf(stderr, "GRIB buffer size %ld too small! Min size = %ld\n", recsize, gribsize);
      return 1;
    }

  /* end section - "7777" in ascii */
  if (!GRIB_FIN(bufpointer))
    {
      fprintf(stderr, "Missing end section >%2x %2x %2x %2x<\n", bufpointer[0], bufpointer[1], bufpointer[2], bufpointer[3]);
    }

  int bs = BDS_BinScale;
  if (bs > 32767) bs = 32768 - bs;
  float bsf = ldexpf(1.0f, bs);

  bignum[0] = dpos;
  bignum[1] = bms ? bpos : -999;
  intnum[0] = BDS_NumBits;

  /*  fltnum[0] = 1.0; */
  fltnum[0] = powf(10.0f, (float) PDS_DecimalScale);
  fltnum[1] = bsf;
  fltnum[2] = (float) BDS_RefValue;
  /*
  printf("intnum %d %d %d\n", intnum[0], intnum[1], intnum[2]);
  printf("fltnum %g %g %g\n", fltnum[0], fltnum[1], fltnum[2]);
  */
  return 0;
}

static int
get_level(unsigned char *pds)
{
  int level = 0;

  if (PDS_LevelType == 100)
    level = (int) (PDS_Level) *100;
  else if (PDS_LevelType == 99 || PDS_LevelType == 109)
    level = (int) (PDS_Level);
  else
    level = PDS_Level1;

  return level;
}

static double
get_cr(unsigned char *w1, unsigned char *w2)
{
  unsigned s1 = GET_UINT3(w1[0], w1[1], w1[2]);
  unsigned s2 = GET_UINT3(w2[0], w2[1], w2[2]);
  return ((double) s1) / s2;
}

static void
grib1PrintALL(int nrec, long offset, long recpos, long recsize, unsigned char *gribbuffer)
{
  static bool header = true;
  unsigned char *is = NULL, *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;

  if (header)
    {
      fprintf(stdout, "  Rec : Off Position   Size : V PDS  GDS    BMS    BDS : Code Level :  LType GType: CR LL\n");
      //               ----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+
      header = false;
    }

  is = gribbuffer;

  unsigned gribsize = GET_UINT3(is[4], is[5], is[6]);

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d :%4ld %8ld %6ld : GRIB message error\n", nrec, offset, recpos, recsize);
      return;
    }

  int GridType = (gds == NULL) ? -1 : (int) GDS_GridType;

  int level = get_level(pds);

  unsigned bdslen = BDS_Len;

  bool llarge = (gribsize > JP23SET && bdslen <= 120);

  bdslen = correct_bdslen(bdslen, recsize, bds - gribbuffer);

  double cr = (((BDS_Flag >> 4) & 1) && (BDS_Z == 128 || BDS_Z == 130)) ? get_cr(&bds[14], &gribbuffer[4]) : 1;

  fprintf(stdout, "%5d :%4ld %8ld %6ld :%2d%4d%5d %6d %6d : %3d %6d : %5d %5d %6.4g  %c", nrec, offset, recpos, recsize,
          GRIB_EDITION(is), PDS_Len, GDS_Len, BMS_Len, bdslen, PDS_Parameter, level, PDS_LevelType, GridType, cr,
          llarge ? 'T' : 'F');

  if (nerr > 0) fprintf(stdout, " <-- GRIB data corrupted!");
  fprintf(stdout, "\n");
}

static void
grib2PrintALL(int nrec, long offset, long recpos, long recsize, unsigned char *gribbuffer)
{
  static bool header = true;
  unsigned char *is = NULL, *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;
  unsigned char *ids = NULL, *lus = NULL, *drs = NULL;
  long ids_len = 0, lus_len = 0, gds_len = 0, pds_len = 0, drs_len = 0, bms_len = 0, bds_len = 0;
  double cr = 1;

  if (header)
    {
      fprintf(stdout,
              "  Rec : Off Position   Size : V IDS LUS GDS PDS  DRS    BMS    BDS : Parameter   Level :  LType GType: CR\n");
      //       ----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+
      header = false;
    }

  is = gribbuffer;

  int nerr = grib2Sections(gribbuffer, recsize, &ids, &lus, &gds, &pds, &drs, &bms, &bds);
  if (nerr)
    {
      fprintf(stdout, "%5d :%4ld %8ld %6ld : error\n", nrec, offset, recpos, recsize);
      return;
    }

  if (ids) ids_len = GRIB2_SECLEN(ids);
  if (lus) lus_len = GRIB2_SECLEN(lus);
  if (gds) gds_len = GRIB2_SECLEN(gds);
  if (pds) pds_len = GRIB2_SECLEN(pds);
  if (drs) drs_len = GRIB2_SECLEN(drs);
  if (bms) bms_len = GRIB2_SECLEN(bms);
  if (bds) bds_len = GRIB2_SECLEN(bds);

  // double cr = (((BDS_Flag >> 4)&1) && (BDS_Z == 128 || BDS_Z == 130)) ? get_cr(&bds[14], &gribbuffer[4]) : 1;

  int dis = GET_UINT1(is[6]);
  int gridtype = (int) (GET_UINT2(gds[12], gds[13]));
  int paramcat = GET_UINT1(pds[9]);
  int paramnum = GET_UINT1(pds[10]);
  int level1type = GET_UINT1(pds[22]);
  /* level1sf   = GET_UINT1(pds[23]); */
  int level1 = (int) (GET_UINT4(pds[24], pds[25], pds[26], pds[27]));
  /* level2type = GET_UINT1(pds[28]); */
  /* level2sf   = GET_UINT1(pds[29]); */
  /* level2     = GET_UINT4(pds[30],pds[31],pds[32],pds[33]); */
  /*
  printf("level %d %d %d %d %d %d %d\n", level1type, level1sf, level1, level1*level1sf, level2sf, level2, level2*level2sf);
  */
  char paramstr[16];
  snprintf(paramstr, sizeof(paramstr), "%d.%d.%d", paramnum, paramcat, dis);
  fprintf(stdout, "%5d :%4ld %8ld %6ld :%2d %3ld %3ld %3ld %3ld %4ld %6ld %6ld : %-9s %7d : %5d %5d %6.4g\n", nrec, offset, recpos,
          recsize, GRIB_EDITION(is), ids_len, lus_len, gds_len, pds_len, drs_len, bms_len, bds_len, paramstr, level1, level1type,
          gridtype, cr);
}

void
gribPrintALL(int nrec, long offset, long recpos, long recsize, unsigned char *gribbuffer)
{
  int gribversion = gribVersion(gribbuffer, (size_t) recsize);

  if (gribversion == 0 || gribversion == 1)
    grib1PrintALL(nrec, offset, recpos, recsize, gribbuffer);
  else if (gribversion == 2)
    grib2PrintALL(nrec, offset, recpos, recsize, gribbuffer);
  else
    {
      fprintf(stdout, "%5d :%4ld%9ld%7ld : GRIB version %d unsupported\n", nrec, offset, recpos, recsize, gribversion);
    }
}

static void
grib1PrintPDS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  static int header = 1;
  unsigned char *is = NULL, *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;
  int century, subcenter, decimalscale;
  int fc_num = 0;
  int year = 0, date;

  UNUSED(recpos);

  if (header)
    {
      fprintf(stdout, "  Rec : PDS Tab Cen Sub Ver Grid Code LTyp Level1 Level2    Date  Time P1 P2 TU TR NAVE Scale FCnum CT\n");
      //               ----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+
      header = 0;
    }

  is = gribbuffer;

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d : GRIB message error\n", nrec);
      return;
    }

  switch (GRIB_EDITION(is))
    {
    case 0:
      year = GET_UINT1(pds[12]);
      century = 1;
      subcenter = 0;
      decimalscale = 0;
      break;
    case 1:
      year = PDS_Year;
      century = PDS_Century;
      subcenter = PDS_Subcenter;
      decimalscale = PDS_DecimalScale;
      break;
    default: fprintf(stderr, "Grib version %d not supported!", GRIB_EDITION(is)); exit(EXIT_FAILURE);
    }

  if (PDS_Len > 28)
    if (PDS_CenterID == 98 || PDS_Subcenter == 98 || (PDS_CenterID == 7 && PDS_Subcenter == 98))
      if (pds[40] == 1) fc_num = GET_UINT1(pds[49]);

  if (year < 0)
    {
      date = (-year) * 10000 + (int) PDS_Month * 100 + (int) PDS_Day;
      century = -century;
    }
  else
    {
      date = year * 10000 + (int) PDS_Month * 100 + (int) PDS_Day;
    }

  fprintf(stdout, "%5d :%4d%4d%4d%4d%4d %4d %4d%4d%7d%7d %8d%6d%3d%3d%3d%3d%5d%6d%5d%4d", nrec, PDS_Len, PDS_CodeTable,
          PDS_CenterID, subcenter, PDS_ModelID, PDS_GridDefinition, PDS_Parameter, PDS_LevelType, PDS_Level1, PDS_Level2, date,
          PDS_Time, PDS_TimePeriod1, PDS_TimePeriod2, PDS_TimeUnit, PDS_TimeRange, PDS_AvgNum, decimalscale, fc_num, century);

  if (nerr > 0) fprintf(stdout, " <-- GRIB data corrupted!");
  fprintf(stdout, "\n");
}

void
gribPrintPDS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  int gribversion = gribVersion(gribbuffer, (size_t) recsize);

  if (gribversion == 0 || gribversion == 1) grib1PrintPDS(nrec, recpos, recsize, gribbuffer);
  /*
  else if ( gribversion == 2 )
    grib2PrintPDS(nrec, recpos, recsize, gribbuffer);
  */
  else
    {
      fprintf(stdout, "%5d :%4ld%9ld%7ld : GRIB version %d unsupported\n", nrec, 0L, recpos, recsize, gribversion);
    }
}

static void
grib1PrintGDS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  static int header = 1;
  unsigned char *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;

  UNUSED(recpos);

  if (header)
    {
      fprintf(stdout, "  Rec : GDS  NV PVPL Typ : xsize ysize   Lat1   Lon1   Lat2   Lon2    dx    dy\n");
      //               ----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+
      header = 0;
    }

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d : GRIB message error\n", nrec);
      return;
    }

  fprintf(stdout, "%5d :", nrec);

  if (gds)
    fprintf(stdout, "%4d%4d%4d %4d :%6d%6d%7d%7d%7d%7d%6d%6d", GDS_Len, GDS_NV, GDS_PVPL, GDS_GridType, GDS_NumLon, GDS_NumLat,
            GDS_FirstLat, GDS_FirstLon, GDS_LastLat, GDS_LastLon, GDS_LonIncr, GDS_LatIncr);
  else
    fprintf(stdout, " Grid Description Section not defined");

  if (nerr > 0) fprintf(stdout, " <-- GRIB data corrupted!");
  fprintf(stdout, "\n");
}

void
gribPrintGDS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  int gribversion = gribVersion(gribbuffer, (size_t) recsize);

  if (gribversion == 0 || gribversion == 1) grib1PrintGDS(nrec, recpos, recsize, gribbuffer);
  /*
  else if ( gribversion == 2 )
    grib2PrintGDS(nrec, recpos, recsize, gribbuffer);
  */
  else
    {
      fprintf(stdout, "%5d :%4ld%9ld%7ld : GRIB version %d unsupported\n", nrec, 0L, recpos, recsize, gribversion);
    }
}

static void
grib1PrintBMS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  static int header = 1;
  unsigned char *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;

  UNUSED(recpos);

  if (header)
    {
      fprintf(stdout, "  Rec : Code Level     BMS    Size\n");
      //               ----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+
      header = 0;
    }

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d : GRIB message error\n", nrec);
      return;
    }

  int level = get_level(pds);

  fprintf(stdout, "%5d :", nrec);

  if (bms)
    fprintf(stdout, "%4d%7d %7d %7d", PDS_Parameter, level, BMS_Len, BMS_BitmapSize);
  else
    fprintf(stdout, "%4d%7d Bit Map Section not defined", PDS_Parameter, level);

  if (nerr > 0) fprintf(stdout, " <-- GRIB data corrupted!");
  fprintf(stdout, "\n");
}

void
gribPrintBMS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  int gribversion = gribVersion(gribbuffer, (size_t) recsize);

  if (gribversion == 0 || gribversion == 1) grib1PrintBMS(nrec, recpos, recsize, gribbuffer);
  /*
  else if ( gribversion == 2 )
    grib2PrintBMS(nrec, recpos, recsize, gribbuffer);
  */
  else
    {
      fprintf(stdout, "%5d :%4ld%9ld%7ld : GRIB version %d unsupported\n", nrec, 0L, recpos, recsize, gribversion);
    }
}

static void
grib1PrintBDS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  static int header = 1;
  unsigned char *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;
  double scale;

  UNUSED(recpos);

  if (header)
    {
      fprintf(stdout, "  Rec : Code Level     BDS Flag     Scale   RefValue Bits  CR\n");
      //               ----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+
      header = 0;
    }

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d : GRIB message error\n", nrec);
      return;
    }

  int level = get_level(pds);

  double cr = (((BDS_Flag >> 4) & 1) && BDS_Z == 128) ? get_cr(&bds[17], &bds[20]) : 1;

  double refval = BDS_RefValue;

  if (BDS_BinScale < 0)
    scale = 1.0 / pow(2.0, (double) -BDS_BinScale);
  else
    scale = pow(2.0, (double) BDS_BinScale);

  if (PDS_DecimalScale != 0)
    {
      double decscale = pow(10.0, (double) -PDS_DecimalScale);
      refval *= decscale;
      scale *= decscale;
    }

  fprintf(stdout, "%5d :", nrec);

  if (bds)
    fprintf(stdout, "%4d%7d %7d %4d %8.5g %11.5g%4d %6.4g", PDS_Parameter, level, BDS_Len, BDS_Flag, scale, refval, BDS_NumBits,
            cr);
  else
    fprintf(stdout, " Binary Data Section not defined");

  if (nerr > 0) fprintf(stdout, " <-- GRIB data corrupted!");
  fprintf(stdout, "\n");
}

void
gribPrintBDS(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  int gribversion = gribVersion(gribbuffer, (size_t) recsize);

  if (gribversion == 0 || gribversion == 1) grib1PrintBDS(nrec, recpos, recsize, gribbuffer);
  /*
  else if ( gribversion == 2 )
    grib2PrintBDS(nrec, recpos, recsize, gribbuffer);
  */
  else
    {
      fprintf(stdout, "%5d :%4ld%9ld%7ld : GRIB version %d unsupported\n", nrec, 0L, recpos, recsize, gribversion);
    }
}

void
gribCheck1(int nrec, long recpos, long recsize, unsigned char *gribbuffer)
{
  unsigned char *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;

  UNUSED(recpos);

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d : GRIB message error\n", nrec);
      return;
    }

  if (nerr > 0)
    {
      fprintf(stdout, "%5d : <-- GRIB data corrupted!\n", nrec);
      return;
    }

  int level = get_level(pds);

  double cr = (((BDS_Flag >> 4) & 1) && BDS_Z == 128) ? get_cr(&bds[17], &bds[20]) : 1;

  if (IS_EQUAL(cr, 1) && BDS_NumBits == 24)
    fprintf(stdout, "GRIB record %5d : code = %4d   level = %7d\n", nrec, PDS_Parameter, level);
}

static void
repair1(unsigned char *gbuf, long gbufsize)
{
  unsigned char *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;
  /* int recLen; */
  size_t bds_nbits;
  int bds_flag, lspherc, lcomplex /*, lcompress */;
  enum
  {
    bds_head = 11
  };
  size_t bds_ext = 0, bds_ubits;

  long gribrecsize;
  int nerr = grib1Sections(gbuf, gbufsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "GRIB message error\n");
      return;
    }

  if (nerr > 0)
    {
      fprintf(stdout, "GRIB data corrupted!\n");
      return;
    }

  unsigned bds_len = BDS_Len;
  bds_nbits = BDS_NumBits;
  bds_flag = BDS_Flag;
  bds_ubits = (size_t) bds_flag & 15;
  lspherc = bds_flag >> 7;
  lcomplex = (bds_flag >> 6) & 1;
  /* lcompress = (bds_flag >> 4)&1; */

  if (lspherc)
    {
      if (lcomplex)
        {
          size_t jup, ioff;
          jup = (size_t) bds[15];
          ioff = (jup + 1) * (jup + 2);
          bds_ext = 4 + 3 + 4 * ioff;
        }
      else
        {
          bds_ext = 4;
        }
    }

  size_t datstart = bds_head + bds_ext;

  unsigned char *source = bds + datstart;

  size_t sourceLen = ((((bds_len - datstart) * 8 - bds_ubits) / bds_nbits) * bds_nbits) / 8;

  if (bds_nbits == 24)
    {
      unsigned char *pbuf = (unsigned char *) Malloc(sourceLen);
      ;
      size_t nelem = sourceLen / 3;
      for (size_t i = 0; i < nelem; ++i)
        {
          pbuf[3 * i] = source[i];
          pbuf[3 * i + 1] = source[nelem + i];
          pbuf[3 * i + 2] = source[2 * nelem + i];
        }
      memcpy(source, pbuf, sourceLen);
      Free(pbuf);
    }
}

void
gribRepair1(int nrec, long recsize, unsigned char *gribbuffer)
{
  unsigned char *pds = NULL, *gds = NULL, *bms = NULL, *bds = NULL;

  long gribrecsize;
  int nerr = grib1Sections(gribbuffer, recsize, &pds, &gds, &bms, &bds, &gribrecsize);
  if (nerr < 0)
    {
      fprintf(stdout, "%5d : GRIB message error\n", nrec);
      return;
    }

  if (nerr > 0)
    {
      fprintf(stdout, "%5d : <-- GRIB data corrupted!\n", nrec);
      return;
    }

  int level = get_level(pds);

  double cr = (((BDS_Flag >> 4) & 1) && BDS_Z == 128) ? get_cr(&bds[17], &bds[20]) : 1;

  if (IS_EQUAL(cr, 1) && BDS_NumBits == 24)
    {
      fprintf(stdout, "Repair GRIB record %5d : code = %4d   level = %7d\n", nrec, PDS_Parameter, level);
      repair1(gribbuffer, recsize);
    }
}
