#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#ifdef HAVE_LIBGRIB_API

#include "gribapi_utilities.h"

#include "cdi.h"
#include "dmemory.h"
#include "error.h"
#include "gribapi.h"
#include "grid.h"

#include <assert.h>
#include <time.h>

#define FAIL_ON_GRIB_ERROR(function, gribHandle, key, ...) do\
{\
  int errorCode = (int)function(gribHandle, key, __VA_ARGS__);  \
  if(errorCode)\
    {\
      fprintf(stderr, "%s:%d: Error in function `%s`: `%s` returned error code %d for key \"%s\"", __FILE__, __LINE__, __func__, #function, errorCode, key);\
      exit(errorCode);\
    }\
} while(0)

//A simple wrapper for grib_get_string() that returns a newly allocated string.
char* gribCopyString(grib_handle* gribHandle, const char* key)
{
  char* result = NULL;
  size_t length;
#ifdef HAVE_GRIB_GET_LENGTH
  if(!grib_get_length(gribHandle, key, &length))
    {
    char* result = (char *) Malloc(length);
    if(!grib_get_string(gribHandle, key, result, &length))
    result = (char *) Realloc(result, length);

    else
      {
          Free(result);
          result = NULL;
      }
    }
#else
  length = 1024;         /* there's an implementation limit
                          * that makes strings longer than
                          * this unlikely in grib_api versions
                          * not providing grib_get_length */
  int rc;
  result = (char *) Malloc(length);
  while ((rc = grib_get_string(gribHandle, key, result, &length))
         == GRIB_BUFFER_TOO_SMALL || rc == GRIB_ARRAY_TOO_SMALL)
    {
      if (length <= 1024UL * 1024UL)
        {
          length *= 2;
          result = Realloc(result, length);
        }
      else
        break;
    }
  if (!rc)
    result = Realloc(result, length);
  else
    {
      Free(result);
      result = NULL;
    }
#endif
  return result;
}

//A simple wrapper for grib_get_string() for the usecase that the result is only compared to a given constant string.
//Returns true if the key exists and the value is equal to the given string.
bool gribCheckString(grib_handle* gribHandle, const char* key, const char* expectedValue)
{
  size_t expectedLength = strlen(expectedValue) + 1;
#ifdef HAVE_GRIB_GET_LENGTH
  size_t length;
  if(grib_get_length(gribHandle, key, &length)) return false;
  if(length != expectedLength) return false;
  char *value = (char *) Malloc(length);
  if(grib_get_string(gribHandle, key, value, &length)) return false;
  int rc = !strcmp(value, expectedValue);
  Free(value);
#else
  char *value = gribCopyString(gribHandle, key);
  int rc;
  if (value)
  {
    rc = strlen(value) + 1 == expectedLength ?
      !strcmp(value, expectedValue)
      : false;
  }
  else
    rc = false;
  Free(value);
#endif
  return rc;
}

//A simple wrapper for grib_get_long() for the usecase that the result is only compared to a given constant value.
//Returns true if the key exists and the value is equal to the given one.
bool gribCheckLong(grib_handle* gribHandle, const char* key, long expectedValue)
{
  long value;
  if(grib_get_long(gribHandle, key, &value)) return false;
  return value == expectedValue;
}

//A simple wrapper for grib_get_long() for the usecase that failure to fetch the value is fatal.
long gribGetLong(grib_handle* gh, const char* key)
{
  long result;
  FAIL_ON_GRIB_ERROR(grib_get_long, gh, key, &result);
  return result;
}

//A simple wrapper for grib_get_long() for the usecase that a default value is used in the case that the operation fails.
long gribGetLongDefault(grib_handle* gribHandle, const char* key, long defaultValue)
{
  long result;
  if(grib_get_long(gribHandle, key, &result)) return defaultValue;
  if(result == GRIB_MISSING_LONG) return defaultValue;
  return result;
}

//A simple wrapper for grib_get_double() for the usecase that failure to fetch the value is fatal.
double gribGetDouble(grib_handle* gh, const char* key)
{
  double result;
  FAIL_ON_GRIB_ERROR(grib_get_double, gh, key, &result);
  return result;
}

//A sample wrapper for grib_get_double() for the usecase that a default value is used in the case that the operation fails.
double gribGetDoubleDefault(grib_handle* gribHandle, const char* key, double defaultValue)
{
  double result;
  if(grib_get_double(gribHandle, key, &result)) return defaultValue;
  if(IS_EQUAL(result, GRIB_MISSING_DOUBLE)) return defaultValue;
  return result;
}

//A simple wrapper for grib_get_size() for the usecase that failure to fetch the value is fatal.
size_t gribGetArraySize(grib_handle* gribHandle, const char* key)
{
  size_t result;
  FAIL_ON_GRIB_ERROR(grib_get_size, gribHandle, key, &result);
  return result;
}

//A simple wrapper for grib_get_double_array() for the usecase that failure to fetch the data is fatal.
void gribGetDoubleArray(grib_handle* gribHandle, const char* key, double* array)
{
  size_t valueCount = gribGetArraySize(gribHandle, key);
  FAIL_ON_GRIB_ERROR(grib_get_double_array, gribHandle, key, array, &valueCount);
}

//A simple wrapper for grib_get_long_array() for the usecase that failure to fetch the data is fatal.
void gribGetLongArray(grib_handle* gribHandle, const char* key, long* array)
{
  size_t valueCount = gribGetArraySize(gribHandle, key);
  FAIL_ON_GRIB_ERROR(grib_get_long_array, gribHandle, key, array, &valueCount);
}


//We need the edition number so frequently, that it's convenient to give it its own function.
long gribEditionNumber(grib_handle* gh)
{
  return gribGetLong(gh, "editionNumber");
}

//This return value of this should be passed to a call to resetTz(), it is a malloc'ed string with the content of the TZ environment variable before the call (or NULL if that was not set).
static char* setUtc()
{
  char* temp = getenv("TZ"), *result = NULL;
  if(temp) result = strdup(temp);
  setenv("TZ", "UTC", 1);
  return result;
}

//Undoes the effect of setUtc(), pass to it the return value of the corresponding setUtc() call, it will free the string.
static void resetTz(char* savedTz)
{
  if(savedTz)
    {
      setenv("TZ", savedTz, 1);
      Free(savedTz);
    }
  else
    {
      unsetenv("TZ");
    }
}

//This function uses the system functions to normalize the date representation according to the gregorian calendar.
//Returns zero on success.
static int normalizeDays(struct tm* me)
{
  char* savedTz = setUtc();     //Ensure that mktime() does not interprete the date according to our local time zone.

  int result = mktime(me) == (time_t)-1;        //This does all the heavy lifting.

  resetTz(savedTz);
  return result;
}

//Returns zero on success.
static int addSecondsToDate(struct tm* me, long long amount)
{
  //It is irrelevant here whether days are zero or one based, the correction would have be undone again so that it is effectless.
  long long seconds = ((me->tm_mday*24ll + me->tm_hour)*60 + me->tm_min)*60 + me->tm_sec;    //The portion of the date that uses fixed increments.
  seconds += amount;
  me->tm_mday = (int)(seconds / 24 / 60 / 60);
  seconds -= (long long)me->tm_mday * 24 * 60 * 60;
  me->tm_hour = (int)(seconds / 60 / 60);
  seconds -= (long long)me->tm_hour * 60 * 60;
  me->tm_min = (int)(seconds / 60);
  seconds -= (long long)(me->tm_min * 60);
  me->tm_sec = (int)seconds;
  return normalizeDays(me);
}

static void addMonthsToDate(struct tm* me, long long amount)
{
  long long months = me->tm_year*12ll + me->tm_mon;
  months += amount;
  me->tm_year = (int)(months/12);
  months -= (long long)me->tm_year*12;
  me->tm_mon = (int)months;
}

//unit is a value according to code table 4.4 of the GRIB2 specification, returns non-zero on error
static int addToDate(struct tm* me, long long amount, long unit)
{
  switch(unit)
    {
      case 0: return addSecondsToDate(me,       60*amount);   // minute
      case 1: return addSecondsToDate(me,    60*60*amount);   // hour
      case 2: return addSecondsToDate(me, 24*60*60*amount);   // day

      case 3: addMonthsToDate(me,        amount); return 0;   // month
      case 4: addMonthsToDate(me,     12*amount); return 0;   // year
      case 5: addMonthsToDate(me,  10*12*amount); return 0;   // decade
      case 6: addMonthsToDate(me,  30*12*amount); return 0;   // normal
      case 7: addMonthsToDate(me, 100*12*amount); return 0;   // century

      case 10: return addSecondsToDate(me,  3*60*60*amount);  // eighth of a day
      case 11: return addSecondsToDate(me,  6*60*60*amount);  // quarter day
      case 12: return addSecondsToDate(me, 12*60*60*amount);  // half day
      case 13: return addSecondsToDate(me,          amount);  // second

      default: return 1;        //reserved, unknown, or missing
    }
}

static char* makeDateString(struct tm* me)
{
  char *result
    = (char *) Malloc(       4+1+ 2+1+ 2+1+ 2+1+ 2+1+ 2+ 4+ 1);
  sprintf(result, "%04d-%02d-%02dT%02d:%02d:%02d.000", me->tm_year + 1900, me->tm_mon + 1, me->tm_mday, me->tm_hour, me->tm_min, me->tm_sec);
  return result;
}

//FIXME: This ignores any calendar definition that might be present.
//XXX: Identification templates are not implemented in grib_api-1.12.3, so even if I implemented the other calendars now, it wouldn't be possible to use them.
static int getAvailabilityOfRelativeTimes(grib_handle* gh, bool* outHaveForecastTime, bool* outHaveTimeRange)
{
  switch(gribGetLong(gh, "productDefinitionTemplateNumber"))
    {
      case 20: case 30: case 31: case 254: case 311: case 2000:
        *outHaveForecastTime = false, *outHaveTimeRange = false;
        return 0;

      //case 55 and case 40455 are the same: 55 is the proposed standard value, 40455 is the value in the local use range that is used by the dwd until the standard is updated.
      case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 15: case 32: case 33: case 40: case 41: case 44: case 45: case 48: case 51: case 53: case 54: case 55: case 60: case 1000: case 1002: case 1100: case 40033: case 40455:
        *outHaveForecastTime = true, *outHaveTimeRange = false;
        return 0;

      case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 34: case 42: case 43: case 46: case 47: case 61: case 91: case 1001: case 1101: case 40034:
        *outHaveForecastTime = true, *outHaveTimeRange = true;
        return 0;

      default:
        return 1;
    }
}

char* gribMakeTimeString(grib_handle* gh, CdiTimeType timeType)
{
  //Get the parts of the reference date.
  struct tm date;
  date.tm_mon = (int)gribGetLong(gh, "month") - 1;   //months are zero based in struct tm and one based in GRIB
  date.tm_mday = (int)gribGetLong(gh, "day");
  date.tm_hour = (int)gribGetLong(gh, "hour");
  date.tm_min = (int)gribGetLong(gh, "minute");

  if(gribEditionNumber(gh) == 1)
    {
      date.tm_year = (int)gribGetLong(gh, "yearOfCentury");  //years are -1900 based both in struct tm and GRIB1
    }
  else
    {
      date.tm_year = (int)gribGetLong(gh, "year") - 1900;   //years are -1900 based in struct tm and zero based in GRIB2
      date.tm_sec = (int)gribGetLong(gh, "second");

      //If the start or end time are requested, we need to take the relative times into account.
      if(timeType != kCdiTimeType_referenceTime)
        {
          //Determine whether we have a forecast time and a time range.
          bool haveForecastTime, haveTimeRange;
          if(getAvailabilityOfRelativeTimes(gh, &haveForecastTime, &haveTimeRange)) return NULL;
          if(timeType == kCdiTimeType_endTime && !haveTimeRange) return NULL;     //tell the caller that the requested time does not exist

          //If we have relative times, apply the relative times to the date
          if(haveForecastTime)
            {
              long offset = gribGetLongDefault(gh, "forecastTime", 0);  //if(stepUnits == indicatorOfUnitOfTimeRange) assert(startStep == forecastTime)
              long offsetUnit = gribGetLongDefault(gh, "indicatorOfUnitOfTimeRange", 255);
              if(addToDate(&date, offset, offsetUnit)) return NULL;
              if(timeType == kCdiTimeType_endTime)
                {
                  assert(haveTimeRange);
                  long range = gribGetLongDefault(gh, "lengthOfTimeRange", 0);       //if(stepUnits == indicatorOfUnitForTimeRange) assert(endStep == startStep + lengthOfTimeRange)
                  long rangeUnit = gribGetLongDefault(gh, "indicatorOfUnitForTimeRange", 255);
                  if(addToDate(&date, range, rangeUnit)) return NULL;
                }
            }
        }
    }

  //Bake the date into a string.
  return makeDateString(&date);
}

int gribapiTimeIsFC(grib_handle *gh)
{
  if(gribEditionNumber(gh) <= 1) return true;

  long sigofrtime;
  FAIL_ON_GRIB_ERROR(grib_get_long, gh, "significanceOfReferenceTime", &sigofrtime);
  return sigofrtime != 3;
}

//Fetches the value of the "stepType" key and converts it into a constant in the TSTEP_* range.
int gribapiGetTsteptype(grib_handle *gh)
{
  int tsteptype = TSTEP_INSTANT;
  static bool lprint = true;

  if ( gribapiTimeIsFC(gh) )
    {
      int status;
      size_t len = 256;
      char stepType[256];

      status = grib_get_string(gh, "stepType", stepType, &len);
      if ( status == 0 && len > 1 && len < 256 )
        {
          if      ( strncmp("instant", stepType, len) == 0 ) tsteptype = TSTEP_INSTANT;
          else if ( strncmp("avg",     stepType, len) == 0 ) tsteptype = TSTEP_AVG;
          else if ( strncmp("accum",   stepType, len) == 0 ) tsteptype = TSTEP_ACCUM;
          else if ( strncmp("max",     stepType, len) == 0 ) tsteptype = TSTEP_MAX;
          else if ( strncmp("min",     stepType, len) == 0 ) tsteptype = TSTEP_MIN;
          else if ( strncmp("diff",    stepType, len) == 0 ) tsteptype = TSTEP_DIFF;
          else if ( strncmp("rms",     stepType, len) == 0 ) tsteptype = TSTEP_RMS;
          else if ( strncmp("sd",      stepType, len) == 0 ) tsteptype = TSTEP_SD;
          else if ( strncmp("cov",     stepType, len) == 0 ) tsteptype = TSTEP_COV;
          else if ( strncmp("ratio",   stepType, len) == 0 ) tsteptype = TSTEP_RATIO;
          else if ( lprint )
            {
              Message("Time stepType %s unsupported, set to instant!", stepType);
              lprint = false;
            }

          // printf("stepType: %s %ld %d\n", stepType, len, tsteptype);
        }
    }

  return (tsteptype);
}

int gribGetDatatype(grib_handle* gribHandle)
{
  int datatype;
  if(gribEditionNumber(gribHandle) > 1 && gribCheckString(gribHandle, "packingType", "grid_ieee"))
    {
      datatype = gribCheckLong(gribHandle, "precision", 1) ? DATATYPE_FLT32 : DATATYPE_FLT64;
    }
  else
    {
      long bitsPerValue;
      datatype = (!grib_get_long(gribHandle, "bitsPerValue", &bitsPerValue) && bitsPerValue > 0 && bitsPerValue <= 32) ? (int)bitsPerValue : DATATYPE_PACK;
    }
  return datatype;
}

int gribapiGetParam(grib_handle *gh)
{
  long pdis, pcat, pnum;
  if ( gribEditionNumber(gh) <= 1 )
    {
      pdis = 255;
      FAIL_ON_GRIB_ERROR(grib_get_long, gh, "table2Version", &pcat);
      FAIL_ON_GRIB_ERROR(grib_get_long, gh, "indicatorOfParameter", &pnum);
    }
  else
    {
      FAIL_ON_GRIB_ERROR(grib_get_long, gh, "discipline", &pdis);
      if(grib_get_long(gh, "parameterCategory", &pcat)) pcat = 0;
      if(grib_get_long(gh, "parameterNumber", &pnum)) pnum = 0;
    }
  return cdiEncodeParam((int)pnum, (int)pcat, (int)pdis);
}

int gribapiGetGridType(grib_handle *gh)
{
  int gridtype = GRID_GENERIC;
  switch (gribGetLongDefault(gh, "gridDefinitionTemplateNumber", -1))
    {
      case  GRIB2_GTYPE_LATLON:
        gridtype = ( gribGetLong(gh, "Ni") == (long) GRIB_MISSING_LONG ) ? GRID_GENERIC : GRID_LONLAT;
        break;

      case  GRIB2_GTYPE_GAUSSIAN:
        gridtype = ( gribGetLong(gh, "Ni") == (long) GRIB_MISSING_LONG ) ? GRID_GAUSSIAN_REDUCED : GRID_GAUSSIAN;
        break;

      case GRIB2_GTYPE_LATLON_ROT:   gridtype = GRID_LONLAT; break;
      case GRIB2_GTYPE_LCC:          gridtype = GRID_LCC; break;
      case GRIB2_GTYPE_SPECTRAL:     gridtype = GRID_SPECTRAL; break;
      case GRIB2_GTYPE_GME:          gridtype = GRID_GME; break;
      case GRIB2_GTYPE_UNSTRUCTURED: gridtype = GRID_UNSTRUCTURED; break;
    }

  return gridtype;
}

static
int gribapiGetIsRotated(grib_handle *gh)
{
  return gribGetLongDefault(gh, "gridDefinitionTemplateNumber", -1) == GRIB2_GTYPE_LATLON_ROT;
}

//TODO: Simplify by use of the convenience functions (gribGetLong(), gribGetLongDefault(), etc.).
void gribapiGetGrid(grib_handle *gh, grid_t *grid)
{
  long editionNumber = gribEditionNumber(gh);
  int gridtype = gribapiGetGridType(gh);
  /*
  if ( streamptr->unreduced && gridtype == GRID_GAUSSIAN_REDUCED )
    {
      gridtype = GRID_GAUSSIAN;
      ISEC2_NumLon = 2*ISEC2_NumLat;
      ISEC4_NumValues = ISEC2_NumLon*ISEC2_NumLat;
    }
  */
  grid_init(grid);
  cdiGridTypeInit(grid, gridtype, 0);

  size_t datasize;
  FAIL_ON_GRIB_ERROR(grib_get_size, gh, "values", &datasize);
  long numberOfPoints;
  FAIL_ON_GRIB_ERROR(grib_get_long, gh, "numberOfPoints", &numberOfPoints);

  switch (gridtype)
    {
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
      {
        long lpar;
        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "Ni", &lpar);
        /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
        int nlon = (int)lpar;
        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "Nj", &lpar);
        /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
        int nlat = (int)lpar;

        if ( gridtype == GRID_GAUSSIAN )
          {
            FAIL_ON_GRIB_ERROR(grib_get_long, gh, "numberOfParallelsBetweenAPoleAndTheEquator", &lpar);
            grid->np = (int)lpar;
          }

        if ( numberOfPoints != nlon*nlat )
          Error("numberOfPoints (%ld) and gridSize (%d) differ!", numberOfPoints, nlon*nlat);

        /* FIXME: assert(numberOfPoints <= INT_MAX && numberOfPoints >= INT_MIN) */
        grid->size  = (int)numberOfPoints;
        grid->xsize = nlon;
        grid->ysize = nlat;
        grid->xinc  = 0;
        grid->yinc  = 0;
        grid->xdef  = 0;
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "longitudeOfFirstGridPointInDegrees", &grid->xfirst);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "longitudeOfLastGridPointInDegrees",  &grid->xlast);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "latitudeOfFirstGridPointInDegrees",  &grid->yfirst);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "latitudeOfLastGridPointInDegrees",   &grid->ylast);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "iDirectionIncrementInDegrees", &grid->xinc);
        if ( gridtype == GRID_LONLAT )
          FAIL_ON_GRIB_ERROR(grib_get_double, gh, "jDirectionIncrementInDegrees", &grid->yinc);

        if ( grid->xinc < -999 || grid->xinc > 999 ) grid->xinc = 0;
        if ( grid->yinc < -999 || grid->yinc > 999 ) grid->yinc = 0;

        /* if ( IS_NOT_EQUAL(grid->xfirst, 0) || IS_NOT_EQUAL(grid->xlast, 0) ) */
          {
            if ( grid->xsize > 1 )
              {
                if ( (grid->xfirst >= grid->xlast) && (grid->xfirst >= 180) ) grid->xfirst -= 360;

                if ( editionNumber <= 1 )
                  {
                    /* correct xinc if necessary */
                    if ( IS_EQUAL(grid->xfirst, 0) && grid->xlast > 354 )
                      {
                        double xinc = 360. / grid->xsize;

                        if ( fabs(grid->xinc-xinc) > 0.0 )
                          {
                            grid->xinc = xinc;
                            if ( CDI_Debug ) Message("set xinc to %g", grid->xinc);
                          }
                      }
                  }
              }
            grid->xdef = 2;
          }
        grid->ydef = 0;
        /* if ( IS_NOT_EQUAL(grid->yfirst, 0) || IS_NOT_EQUAL(grid->ylast, 0) ) */
          {
            if ( grid->ysize > 1 )
              {
                if ( editionNumber <= 1 )
                  {
                  }
              }
            grid->ydef = 2;
          }
        break;
      }
    case GRID_GAUSSIAN_REDUCED:
      {
        size_t dummy;
        long *pl;

        long lpar;
        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "numberOfParallelsBetweenAPoleAndTheEquator", &lpar);
        /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
        grid->np = (int)lpar;

        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "Nj", &lpar);
        /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
        int nlat = (int)lpar;

        /* FIXME: assert(numberOfPoints <= INT_MAX && numberOfPoints >= INT_MIN) */
        grid->size   = (int)numberOfPoints;

        grid->rowlon = (int *) Malloc((size_t)nlat * sizeof (int));
        pl          = (long *) Malloc((size_t)nlat * sizeof (long));
        dummy       = (size_t)nlat;
        FAIL_ON_GRIB_ERROR(grib_get_long_array, gh, "pl", pl, &dummy);
        /* FIXME: assert(pl[i] >= INT_MIN && pl[i] <= INT_MIN) */
        for (int i = 0; i < nlat; ++i ) grid->rowlon[i] = (int)pl[i];
        Free(pl);

        grid->ysize  = nlat;
        grid->xinc   = 0;
        grid->yinc   = 0;
        grid->xdef   = 0;
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "longitudeOfFirstGridPointInDegrees", &grid->xfirst);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "longitudeOfLastGridPointInDegrees",  &grid->xlast);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "latitudeOfFirstGridPointInDegrees",  &grid->yfirst);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "latitudeOfLastGridPointInDegrees",   &grid->ylast);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "iDirectionIncrementInDegrees", &grid->xinc);

        if ( IS_EQUAL(grid->xinc, GRIB_MISSING_DOUBLE) ) grid->xinc = 0;

        /* if ( IS_NOT_EQUAL(grid->xfirst, 0) || IS_NOT_EQUAL(grid->xlast, 0) ) */
          {
            if ( grid->xsize > 1 )
              {
                if ( (grid->xfirst > grid->xlast) && (grid->xfirst >= 180) ) grid->xfirst -= 360;

                if ( editionNumber <= 1 )
                  {
                    /* correct xinc if necessary */
                    if ( IS_EQUAL(grid->xfirst, 0) && grid->xlast > 354 )
                      {
                        double xinc = 360. / grid->xsize;

                        if ( fabs(grid->xinc-xinc) > 0.0 )
                          {
                            grid->xinc = xinc;
                            if ( CDI_Debug ) Message("set xinc to %g", grid->xinc);
                          }
                      }
                  }
              }
            grid->xdef = 2;
          }
        grid->ydef  = 0;
        /* if ( IS_NOT_EQUAL(grid->yfirst, 0) || IS_NOT_EQUAL(grid->ylast, 0) ) */
          {
            if ( grid->ysize > 1 )
              {
                if ( editionNumber <= 1 )
                  {
                  }
              }
            grid->ydef = 2;
          }
        break;
      }
    case GRID_LCC:
      {
        int nlon, nlat;
        long lpar;

        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "Nx", &lpar);
        nlon = (int)lpar;
        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "Ny", &lpar);
        nlat = (int)lpar;

        if ( numberOfPoints != nlon*nlat )
          Error("numberOfPoints (%d) and gridSize (%d) differ!", (int)numberOfPoints, nlon*nlat);

        grid->size  = (int)numberOfPoints;
        grid->xsize = nlon;
        grid->ysize = nlat;

        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "DxInMetres", &grid->lcc_xinc);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "DyInMetres", &grid->lcc_yinc);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "longitudeOfFirstGridPointInDegrees", &grid->lcc_originLon);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "latitudeOfFirstGridPointInDegrees", &grid->lcc_originLat);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "LoVInDegrees", &grid->lcc_lonParY);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "Latin1InDegrees", &grid->lcc_lat1);
        FAIL_ON_GRIB_ERROR(grib_get_double, gh, "Latin2InDegrees", &grid->lcc_lat2);

        if ( editionNumber <= 1 )
          {
            FAIL_ON_GRIB_ERROR(grib_get_long, gh, "projectionCenterFlag", &lpar);
            grid->lcc_projflag  = (int) lpar;
            FAIL_ON_GRIB_ERROR(grib_get_long, gh, "scanningMode", &lpar);
            grid->lcc_scanflag  = (int) lpar;
          }

        grid->xdef   = 0;
        grid->ydef   = 0;

        break;
      }
    case GRID_SPECTRAL:
      {
        size_t len = 256;
        char typeOfPacking[256];
        FAIL_ON_GRIB_ERROR(grib_get_string, gh, "packingType", typeOfPacking, &len);
        grid->lcomplex = 0;
        if ( strncmp(typeOfPacking, "spectral_complex", len) == 0 ) grid->lcomplex = 1;

        /* FIXME: assert(datasize >= INT_MIN && datasize <= INT_MAX) */
        grid->size  = (int)datasize;
        long lpar;
        FAIL_ON_GRIB_ERROR(grib_get_long, gh, "J", &lpar);
        /* FIXME: assert(lpar >= INT_MIN && lpar <= INT_MAX) */
        grid->trunc = (int)lpar;

        break;
      }
    case GRID_GME:
      {
        /* FIXME: assert(numberOfPoints <= INT_MAX && numberOfPoints >= INT_MIN) */
        grid->size  = (int)numberOfPoints;
        long lpar;
        /* FIXME: assert(lpar >= INT_MIN && lpar <= INT_MAX) */
        if ( grib_get_long(gh, "nd", &lpar) == 0 ) grid->nd  = (int)lpar;
        /* FIXME: assert(lpar >= INT_MIN && lpar <= INT_MAX) */
        if ( grib_get_long(gh, "Ni", &lpar) == 0 ) grid->ni  = (int)lpar;
        /* FIXME: assert(lpar >= INT_MIN && lpar <= INT_MAX) */
        if ( grib_get_long(gh, "n2", &lpar) == 0 ) grid->ni2 = (int)lpar;
        /* FIXME: assert(lpar >= INT_MIN && lpar <= INT_MAX) */
        if ( grib_get_long(gh, "n3", &lpar) == 0 ) grid->ni3 = (int)lpar;

        break;
      }
    case GRID_UNSTRUCTURED:
      {
        unsigned char uuid[CDI_UUID_SIZE];
        /*
        char reference_link[8192];
        size_t len = sizeof(reference_link);
        reference_link[0] = 0;
        */

        /* FIXME: assert(numberOfPoints <= INT_MAX && numberOfPoints >= INT_MIN) */
            grid->size  = (int)numberOfPoints;
        long lpar;
        if ( grib_get_long(gh, "numberOfGridUsed", &lpar) == 0 )
          {
            /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
            grid->number   = (int)lpar;
            /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
            if ( grib_get_long(gh, "numberOfGridInReference", &lpar) == 0 )
              grid->position = (int)lpar;
            /*
            if ( grib_get_string(gh, "gridDescriptionFile", reference_link, &len) == 0 )
              {
                if ( strncmp(reference_link, "file://", 7) == 0 )
                  grid->reference = strdupx(reference_link);
              }
            */
            size_t len = (size_t)CDI_UUID_SIZE;
            if ( grib_get_bytes(gh, "uuidOfHGrid", uuid, &len) == 0)
              {
                memcpy(grid->uuid, uuid, CDI_UUID_SIZE);
              }
          }
        break;
      }
    case GRID_GENERIC:
      {
        int nlon = 0, nlat = 0;
        long lpar;
        /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
        if ( grib_get_long(gh, "Ni", &lpar) == 0 ) nlon = (int)lpar;
        /* FIXME: assert(lpar <= INT_MAX && lpar >= INT_MIN) */
        if ( grib_get_long(gh, "Nj", &lpar) == 0 ) nlat = (int)lpar;

        /* FIXME: assert(numberOfPoints <= INT_MAX && numberOfPoints >= INT_MIN) */
        grid->size  = (int)numberOfPoints;

        if ( nlon > 0 && nlat > 0 && nlon*nlat == grid->size )
          {
            grid->xsize = nlon;
            grid->ysize = nlat;
          }
        else
          {
            grid->xsize = 0;
            grid->ysize = 0;
          }

        break;
      }
    default:
      {
        Error("Unsupported grid type: %s", gridNamePtr(gridtype));
        break;
      }
    }

  grid->isRotated = FALSE;
  if ( gribapiGetIsRotated(gh) )
    {
      grid->isRotated = TRUE;
      FAIL_ON_GRIB_ERROR(grib_get_double, gh, "latitudeOfSouthernPoleInDegrees",  &grid->ypole);
      FAIL_ON_GRIB_ERROR(grib_get_double, gh, "longitudeOfSouthernPoleInDegrees", &grid->xpole);
      FAIL_ON_GRIB_ERROR(grib_get_double, gh, "angleOfRotation", &grid->angle);
      /* change from south to north pole */
      if ( fabs(grid->ypole) > 0 ) grid->ypole = -grid->ypole;
      grid->xpole =  grid->xpole - 180;
      if ( fabs(grid->angle) > 0 ) grid->angle = -grid->angle;
    }

  grid->xvals = NULL;
  grid->yvals = NULL;
  grid->type  = gridtype;
}
#endif
/*
 * Local Variables:
 * c-file-style: "Java"
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * show-trailing-whitespace: t
 * require-trailing-newline: t
 * End:
 */
