/*****************************************************************************
 * meta.h
 *
 * DESCRIPTION
 *    This file contains the code necessary to handle the meta data that
 * comes out of the GRIB2 decoder.  This includes parsing the integer arrays
 * to create a meta structure, and providing routines to write the meta
 * structure to disk.
 *
 * HISTORY
 *    9/2002 Arthur Taylor (MDL / RSIS): Created.
 *
 * NOTES
 *****************************************************************************
 */
#ifndef META_H
#define META_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <time.h>
/* Include type.h for uChar and sChar */
#include "type.h"
#ifdef MEMWATCH
  #include "memwatch.h"
#endif

#ifdef __cplusplus
#include "cpl_vsi.h"
#endif

#ifndef GRIB2BIT_ENUM
#define GRIB2BIT_ENUM
/* See rule (8) bit 1 is most significant, bit 8 least significant. */
enum {GRIB2BIT_1=128, GRIB2BIT_2=64, GRIB2BIT_3=32, GRIB2BIT_4=16,
      GRIB2BIT_5=8, GRIB2BIT_6=4, GRIB2BIT_7=2, GRIB2BIT_8=1};
#endif

#ifndef UNITCONVERT_ENUM
#define UNITCONVERT_ENUM
typedef enum { UC_NONE, UC_K2F, UC_InchWater, UC_M2Feet, UC_M2Inch,
   UC_MS2Knots, UC_LOG10, UC_UVIndex, UC_M2StatuteMile
} unit_convert;
#endif

/* NDFD_UNDEF is for matching variables that don't have a defined
 *   NDFD enumerated type... User sets elements in genElemDesc
 *   themselves.
 * NDFD_MATCHALL says give me all elements back.
 * NDFD_MATCHALL is maximum.
 */
#ifndef NDFD_ENUM
#define NDFD_ENUM
/*
enum { NDFD_MAX=0, NDFD_MIN=1, NDFD_POP=2, NDFD_TEMP=3, NDFD_WD=4,
       NDFD_WS=5, NDFD_TD=6, NDFD_SKY=7, NDFD_QPF=8, NDFD_SNOW=9,
       NDFD_ICEACC=10, NDFD_WX=11, NDFD_WH=12, NDFD_AT=13, NDFD_RH=14,
       NDFD_WG=15, NDFD_WWA=16, NDFD_INC34=17, NDFD_INC50=18, NDFD_INC64=19,
       NDFD_CUM34=20, NDFD_CUM50=21, NDFD_CUM64=22, NDFD_FWXWINDRH=23,
       NDFD_FWXTSTORM=24, NDFD_CONHAZ=25,
       NDFD_PTORN=26, NDFD_PHAIL=27, NDFD_PTSTMWIND=28, NDFD_PXTORN=29,
       NDFD_PXHAIL=30, NDFD_PXTSTMWIND=31, NDFD_PSTORM=32, NDFD_PXSTORM=33,
       NDFD_TMPABV14D=34, NDFD_TMPBLW14D=35, NDFD_PRCPABV14D=36,
       NDFD_PRCPBLW14D=37, NDFD_TMPABV30D=38, NDFD_TMPBLW30D=39,
       NDFD_PRCPABV30D=40, NDFD_PRCPBLW30D=41, NDFD_TMPABV90D=42,
       NDFD_TMPBLW90D=43, NDFD_PRCPABV90D=44, NDFD_PRCPBLW90D=45, NDFD_MAXRH=46,
       NDFD_MINRH=47, LAMP_TSTMPRB=48, RTMA_PRECIPA=49, RTMA_SKY=50, RTMA_TD=51,
       RTMA_TEMP=52, RTMA_UTD=53, RTMA_UTEMP=54, RTMA_UWDIR=55,
       RTMA_UWSPD=56, RTMA_WDIR=57, RTMA_WSPD=58, NDFD_CANL=59
#ifdef GFSEKD
       GFSEKDMOS_MAXT=60, GFSEKDMOS_MINT=61,
       GFSEKDMOS_TEMP=62, GFSEKDMOS_TD=63, GFSEKDMOS_QPF=64,
#endif
        NDFD_UNDEF, NDFD_MATCHALL
};
*/
enum { NDFD_MAX, NDFD_MIN, NDFD_POP, NDFD_TEMP, NDFD_WD, NDFD_WS,
       NDFD_TD, NDFD_SKY, NDFD_QPF, NDFD_SNOW, NDFD_ICEACC, NDFD_WX, NDFD_WH,
       NDFD_AT, NDFD_RH, NDFD_WG, NDFD_WWA, NDFD_INC34, NDFD_INC50,
       NDFD_INC64, NDFD_CUM34, NDFD_CUM50, NDFD_CUM64, NDFD_FWXWINDRH,
       NDFD_FWXTSTORM, NDFD_CONHAZ, NDFD_PTORN, NDFD_PHAIL, NDFD_PTSTMWIND,
       NDFD_PXTORN, NDFD_PXHAIL, NDFD_PXTSTMWIND, NDFD_PSTORM, NDFD_PXSTORM,
       NDFD_TMPABV14D, NDFD_TMPBLW14D, NDFD_PRCPABV14D, NDFD_PRCPBLW14D,
       NDFD_TMPABV30D, NDFD_TMPBLW30D, NDFD_PRCPABV30D, NDFD_PRCPBLW30D,
       NDFD_TMPABV90D, NDFD_TMPBLW90D, NDFD_PRCPABV90D, NDFD_PRCPBLW90D,
       NDFD_MAXRH, NDFD_MINRH, LAMP_TSTMPRB, RTMA_PRECIPA, RTMA_SKY, RTMA_TD,
       RTMA_TEMP, RTMA_UTD, RTMA_UTEMP, RTMA_UWDIR, RTMA_UWSPD, RTMA_WDIR,
       RTMA_WSPD, NDFD_CANL,
#ifdef GFSEKD
       GFSEKDMOS_MAXT, GFSEKDMOS_MINT,
       GFSEKDMOS_TEMP, GFSEKDMOS_TD, GFSEKDMOS_QPF,
#endif
        NDFD_UNDEF, NDFD_MATCHALL
};
#endif

#ifndef NDFD_OCONUS_SECTOR_ENUM
#define NDFD_OCONUS_SECTOR_ENUM
/* Assumed the same order as in "sector.c" NdfdDefSect[] */
enum { NDFD_OCONUS_5CONUS, NDFD_OCONUS_2_5CONUS, NDFD_OCONUS_PR,
       NDFD_OCONUS_HI, NDFD_OCONUS_GU, NDFD_OCONUS_AK, NDFD_OCONUS_NHEMI,
       NDFD_OCONUS_NPACIFIC, NDFD_OCONUS_UNDEF
};
#endif

/* For GRIB1 GDS Types. */
enum { GB1S2_LATLON = 0, GB1S2_MERCATOR = 1, GB1S2_LAMBERT = 3,
      GB1S2_GAUSSIAN_LATLON = 4, GB1S2_POLAR = 5, GB1S2_ROTATED = 10
};

/* For TDLP GDS Types. */
enum { TDLP_MERCATOR = 7, TDLP_LAMBERT = 3, TDLP_POLAR = 5};

#define GRIB2MISSING_u1 (uChar) (0xff)
#define GRIB2MISSING_s1 (sChar) -1 * (0x7f)
#define GRIB2MISSING_u2 (uShort2) (0xffff)
#define GRIB2MISSING_s2 (sShort2) -1 * (0x7fff)
#define GRIB2MISSING_u4 (uInt4) (0xffffffff)
/* following is -1 * 2&31 because of the way signed integers are stored in
   GRIB2. */
#define GRIB2MISSING_s4 (sInt4) -2147483647

#define NUM_UGLY_WORD 5
#define NUM_UGLY_ATTRIB 5
#define NUM_HAZARD_WORD 5

typedef struct {
   uChar numValid;         /* (0..5) How many valid "types" */
   uChar wx[NUM_UGLY_WORD]; /* (see WxCode) */
   uChar cover[NUM_UGLY_WORD]; /* (see WxCover) */
   uChar intens[NUM_UGLY_WORD]; /* (see WxIntens) */
   uChar vis[NUM_UGLY_WORD]; /* 255 no vis, otherwise in units of 1/32 SM
                            * so 6SM -> 192. P6SM -> 224 */
   uChar f_or[NUM_UGLY_WORD]; /* true if OR, or MX was a hazard. */
   uChar f_priority[NUM_UGLY_WORD]; /* 0 if normal,
                            * 1 if 'include unconditional'
                            * 2 if 'high priority' */
   uChar attrib[NUM_UGLY_WORD][NUM_UGLY_ATTRIB]; /* (see WxAttrib) */
   uChar minVis;           /* vis[] should be constant, but just in case
                            * we use the minimum value. */
   sInt4 validIndex;    /* Which index this is, counting only used
                            * valid indexes.  If it is not used it is -1 */
   char *english[NUM_UGLY_WORD]; /* The English translation of ugly string. */
   uChar wx_inten[NUM_UGLY_WORD]; /* A code to represent the wx and
                              intensity for an "ugly word". */
   sInt4 HazCode[NUM_UGLY_WORD]; /* A code to represent all the attributes. */
   int SimpleCode;         /* Simple weather code for this ugly string. */
   char *errors;           /* if STORE_ERRORS, then it contains any error
                            * messages found while parsing this string. */
} UglyStringType;

typedef struct {
   char **data;              /* Array of text strings (aka "ugly strings) */
   uChar *f_valid;           /* 1 valid may be not-used, 2 valid and used,
                              * 0 invalid may be not-used,
                              * temporarily 3 (invalid and used). */
   uInt4 dataLen;            /* number of text strings in data. */
   int maxLen;               /* Max Length of all of the "ugly strings"
                              * It includes 1 for the \0 character. */
   UglyStringType *ugly;     /* The parsed Ugly string. */
   int maxEng[NUM_UGLY_WORD]; /* Max length of English phrases for all ugly
                               * word number X. */
} sect2_WxType;

typedef struct {
   double *data;             /* Array of actual values (cast from int or
                                float to double). */
   uInt4 dataLen;         /* Number of elements in data. */
} sect2_UnknownType;

typedef struct {
   uChar numValid;         /* (0..5) How many valid "types" */
   uChar haz[NUM_HAZARD_WORD]; /* (see WxCode) */
   uChar sig[NUM_HAZARD_WORD]; /* (see WxCover) */
   sInt4 validIndex;       /* Which index this is, counting only used
                            * valid indexes.  If it is not used it is -1 */
   char *english[NUM_HAZARD_WORD]; /* The english translation of ugly string. */
   int SimpleCode;         /* Simple weather code for this ugly string. */
} HazardStringType;

typedef struct {
   char **data;              /* Array of text strings */
   uChar *f_valid;           /* 1 valid may be not-used, 2 valid and used,
                              * 0 invalid may be not-used,
                              * temporarily 3 (invalid and used). */
   uInt4 dataLen;            /* number of text strings in data. */
   int maxLen;               /* Max Length of all of the "ugly strings"
                              * It includes 1 for the \0 character. */
   HazardStringType *haz;    /* The parsed Ugly string. */
   int maxEng[NUM_HAZARD_WORD]; /* Max length of english phrases for all ugly
                               * word number X. */
} sect2_HazardType;

enum { GS2_NONE, GS2_WXTYPE, GS2_UNKNOWN, GS2_HAZARD };

typedef struct {
/*   void *ptr;  */              /* Pointer to section 2 data. */
   sect2_WxType wx;          /* wx data. */
   sect2_UnknownType unknown; /* unknown type of section 2 data. */
   sect2_HazardType hazard;  /* hazard data */
   uChar ptrType;            /* Which structure is valid.
                                GS2_WXTYPE => wx
                                GS2_UNKNOWN => unknown
                                GS2_HAZARD => hazard */
} sect2_type;

enum { CAT_TEMP, CAT_MOIST, CAT_MOMENT, CAT_MASS, CAT_SW_RAD,
       CAT_LW_RAD, CAT_CLOUD, CAT_THERMO_INDEX, CAT_KINEMATIC_INDEX,
       CAT_TEMP_PROB, CAT_MOISTURE_PROB, CAT_MOMENT_PROB, CAT_MASS_PROB,
       CAT_AEROSOL, CAT_TRACE, CAT_RADAR, CAT_RADAR_IMAGERY, CAT_ELECTRO,
       CAT_NUCLEAR, CAT_PHYS_ATMOS };
enum { TEMP_TEMP, TEMP_VIRT, TEMP_POTENTIAL, TEMP_PSEUDO_POTENTIAL,
       TEMP_MAXT, TEMP_MINT, TEMP_DEW_TEMP, TEMP_DEW_DEPRESS,
       TEMP_LAPSE, TEMP_ANOMALY, TEMP_LATENT_FLUX, TEMP_SENSIBLE_FLUX,
       TEMP_HEAT, TEMP_WINDCHILL, TEMP_MIN_DEW_DEPRESS, TEMP_VIRT_POTENTIAL };
enum { CLOUD_ICE, CLOUD_COVER, CLOUD_CONVECT_COVER, CLOUD_LOW,
       CLOUD_MEDIUM, CLOUD_HIGH, CLOUD_WATER, CLOUD_AMNT, CLOUD_TYPE,
       CLOUD_THUDER_MAX, CLOUD_THUNDER_COVER, CLOUD_BASE, CLOUD_TOP,
       CLOUD_CEIL };
enum { MOMENT_WINDDIR, MOMENT_WINDSPD, MOMENT_U_WIND, MOMENT_V_WIND,
       MOMENT_STREAM, MOMENT_VEL_POTENT, MOMENT_MONT_STREAM,
       MOMENT_SIGMA_VERTVEL, MOMENT_VERTVEL_PRESS, MOMENT_VERTVEL_GEOMETRIC,
       MOMENT_ABS_VORT, MOMENT_ABS_DIV, MOMENT_REL_VORT, MOMENT_REL_DIV,
       MOMENT_POT_VORT, MOMENT_VERT_U_SHEAR, MOMENT_VERT_V_SHEAR,
       MOMENT_U_FLUX, MOMENT_V_FLUX, MOMENT_MIX_ENERGY,
       MOMENT_BOUNDARY_DISSPATE, MOMENT_MAX_WINDSPD, MOMENT_GUSTSPD,
       MOMENT_U_GUSTSPD, MOMENT_V_GUSTSPD };
enum { MOIST_SPEC_HUMID, MOIST_REL_HUMID, MOIST_HUMID_MIX, MOIST_PRECIP_WATER,
       MOIST_VAPOR_PRESS, MOIST_SAT_DEFICIT, MOIST_EVAP, MOIST_PRECIP_RATE,
       MOIST_PRECIP_TOT, MOIST_LARGE_SCALE, MOIST_CONVECT_PRECIP,
       MOIST_SNOWAMT, MOIST_SNOWRATE_WATER, MOIST_SNOWAMT_WATER,
       MOIST_CONVECT_SNOW, MOIST_LARGE_SCALE_SNOW, MOIST_SNOWMELT,
       MOIST_SNOWAGE, MOIST_ABS_HUMID, MOIST_PRECIP_TYPE,
       MOIST_INTEGRATE_WATER, MOIST_CONDENSATE, MOIST_CLOUDMIX_RATIO,
       MOIST_ICEMIX_RATIO, MOIST_RAINMIX_RATIO, MOIST_SNOWMIX_RATIO,
       MOIST_HORIZ_CONVERGE, MOIST_MAXREL_HUMID, MOIST_MAXABS_HUMID,
       MOIST_TOT_SNOW, MOIST_PRECIP_WATER_CAT, MOIST_HAIL, MOIST_GRAUPEL };
enum { OCEAN_CAT_WAVES, OCEAN_CAT_CURRENT, OCEAN_CAT_ICE,
       OCEAN_CAT_SURF, OCEAN_CAT_SUBSURF };
enum { OCEAN_WAVE_SPECTRA1, OCEAN_WAVE_SPECTRA2, OCEAN_WAVE_SPECTRA3,
       OCEAN_WAVE_SIG_HT_WV_SWELL, OCEAN_WAVE_DIR_WV,
       OCEAN_WAVE_SIG_HT_WV, OCEAN_WAVE_PD_WV, OCEAN_WAVE_DIR_SWELL,
       OCEAN_WAVE_SIG_HT_SWELL, OCEAN_WAVE_PD_SWELL, OCEAN_WAVE_PRIM_DIR,
       OCEAN_WAVE_PRIM_PD, OCEAN_WAVE_SEC_DIR, OCEAN_WAVE_SEC_PD };

typedef struct {
   uChar processID;          /* Statistical process method used. */
   uChar incrType;           /* Type of time increment between intervals */
   uChar timeRangeUnit;      /* Time range unit. */
   sInt4 lenTime;         /* Range or length of time interval. */
   uChar incrUnit;           /* Unit of time increment. */
   sInt4 timeIncr;        /* Time increment between intervals. */
} sect4_IntervalType;

typedef struct {             /* Used to store data where unpacked value =
                                value / 10**factor */
   sInt4 value;           /* scale value in equation */
   sChar factor;             /* scale factor in equation. */
} ScaleType;

typedef struct {  /* See Template 4.30. */
   unsigned short int series; /* Satellite series of band. */
   unsigned short int numbers; /* Satellite numbers of band. */
   uChar instType;           /* Instrument type of band */
   ScaleType centWaveNum;    /* scaled value for central wave number of
                                band. */
} sect4_BandType;

enum { GS4_ANALYSIS, GS4_ENSEMBLE, GS4_DERIVED, GS4_PROBABIL_PNT = 5,
       GS4_PERCENT_PNT = 6, GS4_ERROR = 7, GS4_STATISTIC = 8,
       GS4_PROBABIL_TIME = 9, GS4_PERCENT_TIME = 10, GS4_ENSEMBLE_STAT = 11,
       GS4_DERIVED_INTERVAL = 12,
       GS4_STATISTIC_SPATIAL_AREA = 15, // TODO; partially supported. Should fetch specific fields in metaparse.cpp
       GS4_RADAR = 20,
       GS4_SATELLITE = 30,
       GS4_SATELLITE_SYNTHETIC = 32,
       GS4_ANALYSIS_CHEMICAL = 40
};

typedef struct {
   uShort2 templat;          /* The section 4 template number. */
   uChar cat;                /* General category of Meteo Product. */
   uChar subcat;             /* Specific subcategory of Meteo Product. */
   uChar genProcess;         /* What type of generate process (Analysis,
                                Forecast, Probability Forecast, etc). */
   uChar bgGenID;            /* Background generating process id. */
   uChar genID;              /* Analysis/Forecast generating process id. */
   uChar f_validCutOff;      /* 1 if cutOff is valid, 0 if cutoff missing. */
   sInt4 cutOff;             /* Data Cutoff in seconds after reference time
                                (see sect 1 for reference time). */
   double foreSec;           /* forecast "projection time" in seconds. */
   uChar foreUnit;           /* Original unit for foreSec.  See 4.4 */
   uChar fstSurfType;        /* Type of the first fixed surface. */
   double fstSurfValue;      /* Value of first fixed surface. */
   sChar fstSurfScale;       /* Scale factor used when storing value. */
   uChar sndSurfType;        /* Type of the second fixed surface. */
   double sndSurfValue;      /* Value of second fixed surface. */
   sChar sndSurfScale;       /* Scale factor used when storing value. */
   double validTime;         /* The ending time, or valid time, in seconds
                                UTC.  This is specified in template 4.8, 4.9,
                                for the others it is refTime + foreSec. */
        /* The following are somewhat template specific. */
   uChar typeEnsemble;       /* The type of Ensemble forecast
                                Template 4.1, (Code Table 4.5) */
   uChar perturbNum;         /* Perturbation number, Template 4.1 */
   uChar numberFcsts;        /* Number of forecasts in Ensemble.
                                Template, 4.1, 4.2 */
   uChar derivedFcst;        /* Derived Forecast (from Ensemble).
                                Template 4.2 */
   uChar numInterval;        /* Number of time intervals. Template 4.8,4.9 */
   sInt4 numMissing;         /* Number of missing values. Template 4.8,4.9 */
   sect4_IntervalType *Interval; /* Stores the array of time intervals.
                                Template 4.8,4.9 */
   uChar numBands;           /* Number of Spectral Bands. Template 4.30 */
   sect4_BandType *bands;    /* Holds info about each Band Template 4.30 */
   uChar percentile;         /* Which percentile this forecast is for */
   uChar foreProbNum;        /* Forecast Probability number (Template 4.9) */
   uChar numForeProbs;       /* Total # of forecast probabilities (4.9) */
   uChar probType;           /* Type of probability range. (Template 4.9)
                                0 below lower limit. 1 above upper limit.
                                2 between lower (inclusive) and
                                  upper limit (exclusive)
                                3 above lower limit. 4 below upper limit. */
   ScaleType lowerLimit;     /* Lower Limit probability field. Template 4.9*/
   ScaleType upperLimit;     /* Upper Limit probability field. Template 4.9*/
} sect4_type;

/* This is the structure needed for the PDS (Product Definition Section)
 * for GRIB2.  I hope to combine stuff, but there is a lot more to GRIB2
 * than GRIB1
 */
typedef struct {
   /* Section 0 Data */
   uChar prodType;           /* 0 is meteo product, 1 is hydro, 2 is land
                                3 is space, 10 is oceanographic. */
   /* Section 1 Data */
   uChar mstrVersion;        /* Master table version. */
   uChar lclVersion;         /* Local table version. */
   uChar sigTime;            /* Significance of reference time */
   double refTime;           /* Reference time in seconds UTC */
   uChar operStatus;         /* What is operational status of data. */
   uChar dataType;           /* What type of data. (analysis, forecast,
                                analysis & forecast, ...) */

   /* Rest of PDS. */
   uChar f_sect2;            /* 1 if sect2 exists, 0 otherwise. */
   sInt4 sect2NumGroups;  /* total number of groups in section 2 data. */
   sect2_type sect2;         /* sect 2 info */
   sect4_type sect4;         /* sect 4 info */
} pdsG2Type;

/* see: http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */
typedef struct {
   uChar BitFlag;            /* Octet 29 */
   uChar Application;        /* Octet 41 (should be 1) */
   uChar Type;               /* Octet 42 */
   uChar Number;             /* Octet 43 */
   uChar ProdID;             /* Octet 44 */
   uChar Smooth;             /* Octet 45 */
} pdsG1EnsType;

/* see: http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */
typedef struct {
   uChar Cat;                /* swapped octet 46 for octet 9.
                              * This holds 191,192,193 (octet 9)
                              * pdsMeta->cat holds category (octet 46) */
   uChar Type;               /* Octet 47 */
   double lower;             /* 48-51 */
   double upper;             /* 52-55 */
} pdsG1ProbType;

/* see: http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */
typedef struct {
   uChar ensSize;            /* Octet 61 */
   uChar clusterSize;        /* Octet 62 */
   uChar Num;                /* Octet 63 */
   uChar Method;             /* Octet 64 */
   double NorLat;            /* Octet 65-67 / 1000 (deg) */
   double SouLat;            /* Octet 68-70 / 1000 (deg) */
   double EasLon;            /* Octet 71-73 / 1000 (deg) */
   double WesLon;            /* Octet 74-76 / 1000 (deg) */
   char Member[11];          /* Octet 77-86 */
} pdsG1ClusterType;

/* This is the structure needed for the PDS (Product Definition Section)
 * for GRIB1
 */
typedef struct {
   uChar mstrVersion;        /* Parameter Table Version #. */
/*   uChar center, subcenter; */ /* Who produced it. */
   uChar genProcess;         /* Generating Process ID. ?Sect 4? */
   uChar cat;                /* General category of Meteo Product. */
   uChar gridID;             /* The Grid Defin ID number (GRIB1 specific) */
   uChar levelType;          /* Type of level. ?sect 4 fstSurf? */
   int levelVal;             /* Value of level. */
   double refTime;           /* Reference time in seconds UTC */
   double P1;                /* Period of time1 ?sect 4 valid time? */
   double P2;                /* Period of time2 */
   double validTime;         /* Valid time in seconds UTC */
   uChar timeRange;          /* Time Range Indicator. */
   /* Specific for averages or accumulations. (determined by timeRange) */
   int Average;              /* number included in average. */
   uChar numberMissing;      /* number missing from averages. */
   uChar f_hasEns;           /* 1 = has ens type, 0 = doesn't */
   pdsG1EnsType ens;         /* Ensemble information */
   uChar f_hasProb;          /* 1 = has prob type, 0 = doesn't */
   pdsG1ProbType prob;       /* Probability information */
   uChar f_hasCluster;       /* 1 = has cluster type, 0 = doesn't */
   pdsG1ClusterType cluster; /* Cluster information */
} pdsG1Type;

/* This is the structure needed for the PDS (Product Definition Section)
 * for TDLP
 */
typedef struct {
   double refTime;           /* Reference time in seconds UTC */
   /* CCCFFF :: is the class and subclass (variable type). */
   /* B :: is the binary indicator. */
   /* DD :: is the Data source (NGM/ETA/AVN/ etc). */
   sInt4 ID1;             /* First word of ID (CCCFFFBDD) */
   int CCC, FFF, B, DD;
   /* V :: is the type of surface processing */
   /* LLL :: is the lower level. */
   /* UUU :: is the upper level. */
   sInt4 ID2;             /* Second word of ID (VLLLLUUUU) */
   int V, LLLL, UUUU;
   /* T :: indicates non-linear transformation of the data. */
   /* RR :: is the run time offset in hours. Example RR=24 with model field
    * means the data from the model run 24 hours ago. */
   /* O :: time operator... 2 fields are involved in time computation.
    * 1-4 indicate both times were for run of NDATE-RR, but projections of
    * var1 = ttt, var2 = ttt + HH. */
   /* 5-8 indicate var1 has run of NDATE-RR, while var2 has run of NDATE,
    * with projections of var1 = ttt + HH, var2 = ttt. */
   sInt4 ID3;             /* Third word of ID (TRROHHttt) */
   int T, RR, Oper, HH, ttt;
   /* WXXXXYY :: Threshold (W=0 positive, =1 negative) (xxxx is fraction)
    * (YY > 50 is negative) (YY is the exponent applied to xxxx). */
   /* I is type of interpolation. */
   /* S is smoothing indicator. */
   /* G is reserved. */
   sInt4 ID4;             /* Forth word of ID (WXXXXYY ISG) */
   double thresh;
   int I, S, G;
   sInt4 project;         /* Projection in seconds */
   uChar procNum;            /* model or process number (see DD in ID1) */
   uChar seqNum;             /* sequence number within the model number */
   char Descriptor[33];      /* Plain language Descriptor. */
} pdsTDLPType;

/* This is the structure and enumerated types needed for the GDS (Grid
 * Definition Section. In GRIB2 the GDS was in section 3, in GRIB1 it was in
 * section 2.
 */
enum { GS3_LATLON = 0, GS3_MERCATOR = 10, GS3_TRANSVERSE_MERCATOR = 12, GS3_POLAR = 20,
       GS3_LAMBERT = 30, GS3_ALBERS_EQUAL_AREA = 31, GS3_GAUSSIAN_LATLON = 40, GS3_ORTHOGRAPHIC = 90,
       GS3_ROTATED_LATLON = 100, GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120, GS3_LAMBERT_AZIMUTHAL = 140};

/* Note: It appears that compilers break up a struct based on the largest
   element type.  In this case a double.  So to avoid wasted memory, we need
   things to stop at 8 byte borders. sInt4, double, sInt4 is larger than
   sInt4, sInt4, double. */
typedef struct {
   uInt4 numPts;             /* Number of data points. Typically Nx * Ny, but
                                for some exotic grids they don't have Nx,Ny */
   uChar projType;           /* Projection type / template type. Valid choices
                                0(lat/lon), 10(mercator), 20(Polar Stereo),
                                30(Lambert Conformal),
                                in future maybe 90, 110, 120. */
/* Shape of Earth */
   uChar f_sphere;           /* 1 is a sphere, => majEarth == minEarth */
   double majEarth, minEarth; /* semi major and minor axis of earth in km. */
/* Projection info. */
   uInt4 Nx, Ny;             /* Dimensions of grid. */
   double lat1, lon1;        /* lat,lon position of first grid point. */
   /* resFlag: moved to save memory. */
   double orientLon;         /* Where up is North. (0 for lat/lon grids) */
   double Dx, Dy;            /* Mesh delta x,y in degrees or meters. */
   double meshLat;           /* Where the mesh size is defined
                                (0 for lat/lon grids.) */
   uChar resFlag;            /* Table 7 GRIB1 : Section 2 */
   uChar center;             /* For lambert and polar stereographic, answers:
                                (south/north?) and (bi-polar?) */
   uChar scan;               /* describes how the grid was traversed when it
                                was stored. (i.e. top/down left/right etc.)
                                Internally we use 0100. (start lower left) */
   double lat2, lon2;        /* lat,lon position of last grid point.
                                (0 if unused) */
/* Specific to Lambert Conformal grids. */
   double scaleLat1, scaleLat2; /* The tangent latitude.  If different:
                                then the latitude where the scale should be
                                equal, which allows one to compute the correct
                                tangent latitude.  (0 for lat/lon and mercator,
                                90 for north polar stereographic). */
   double southLat, southLon; /* Not needed. 0 except lambert.
                                 (and rotated lat/lon) */
   /* Following is for stretched Lat/Lon grids. */
   double poleLat, poleLon;   /* Pole of stretching. */
   double stretchFactor;      /* Factor of stretching. */
   int f_typeLatLon;          /* 0 regular, 1 stretch, 2 stretch / rotate, 3 rotate. */
   double angleRotate;        /* Rotation angle. */
/* Specific to transverse mercator */
   double latitude_of_origin;
   double central_meridian;
   double x0;
   double y0;
   double x1;
   double y1;
   double x2;
   double y2;
/* following is just to track the datum. */
   uChar hdatum;              /* horizontal datum to use.  0=undefined (use
                               * grid dataum) 1=WGS84 */
} gdsType;

typedef struct {
   uChar projType;           /* Projection type / template type. Valid choices
                                0(lat/lon), 10(mercator), 20(Polar Stereo),
                                30(Lambert Conformal),
                                in future maybe 90, 110, 120. */
   double majEarth, minEarth; /* semi major and minor axis of earth in km. */
} gdsType2;

enum { GS5_SIMPLE = 0, GS5_CMPLX = 2, GS5_CMPLXSEC = 3, GS5_IEEE = 4, GS5_JPEG2000 = 40,
       GS5_PNG = 41, GS5_SPECTRAL = 50, GS5_HARMONIC = 51,
       GS5_JPEG2000_ORG = 40000, GS5_PNG_ORG = 40010
};
typedef struct {
   sInt4 packType;           /* What kind of packing was used. */
   float refVal;             /* The reference value for the grid, also the
                              * minimum value? */
   short int ESF;            /* Power of 2 scaling factor. */
   short int DSF;            /* Decimal Scale Factor */
   uChar fieldType;          /* 0 data was float, 1 if int. */
   uChar f_maxmin;           /* boolean, if min/max are valid*/
   double min, max;          /* Min / Max values in data. */
   uChar f_miss;             /* Missing value management
                                0 none, 1 primary, 2 primary & secondary. */
   double missPri, missSec;  /* primary and secondary missing values. */
   sInt4 numMiss;            /* Number of missing values detected,
                                both primary and secondary. */
} gridAttribType;

typedef struct {
   sChar GribVersion;        /* 1 if GRIB1, 2 if GRIB2, -1 if TDLP */
   pdsTDLPType pdsTdlp;      /* Product Definition section for TDLP. */
   pdsG1Type pds1;           /* Product Definition section for GRIB1. */
   pdsG2Type pds2;           /* Product Definition section for GRIB2. */
   gdsType gds;              /* Grid Definition section (sect3 for GRIB2) */
   gridAttribType gridAttrib; /* Attributes of the Grid (min/max values etc) */
   char *element;            /* A short char look up of variable type. */
   char *comment;            /* A more descriptive look up of variable type. */
   char *unitName;           /* Unit if not the one specified in the GRIB2
                              * document, otherwise NULL. */
   int convert;              /* Enum type of unit conversions (meta.h),
                                Conversion method for this variable's unit. */
   char *shortFstLevel;      /* Short description of the level of this data
                                (above ground) (500 mb), etc */
   char *longFstLevel;       /* Long description of the level of this data
                                (above ground) (500 mb), etc */
   uShort2 center, subcenter; /* Who produced it. */
   char refTime[20];         /* When forecast was issued. */
   char validTime[20];       /* When forecast is valid. */
   sInt4 deltTime;           /* validTime - refTime in seconds. */

/*  int *size_wx; */         /* (idat[0] + 2) * sizeof (int) */
/*  char **release_datetime; *//* pds2.refTime + pds2.cutOffHour */
} grib_MetaData;

typedef enum {
   Prt_D, Prt_DS, Prt_DSS, Prt_S,
   Prt_F, Prt_FS, Prt_E, Prt_ES, Prt_G, Prt_GS, Prt_SS, Prt_NULL
} Prt_TYPE;

/* Note that fmt was Prt_TYPE, but there is no way to tell C89/C++03 to
   use an integer sized enum, so it must be an int to avoid undefined
   behavior.

   http://stackoverflow.com/questions/9853633/how-to-change-the-integer-type-used-by-an-enum-c
 */
char *Print(const char *label, const char *varName, int fmt, ...);

void MetaInit (grib_MetaData *meta);

void MetaSect2Free (grib_MetaData * meta);

void MetaFree (grib_MetaData *meta);

int ParseTime (double * AnsTime, int year, uChar mon, uChar day, uChar hour,
               uChar min, uChar sec);

int ParseSect4Time2secV1 (sInt4 time, int unit, double *ans);

int ParseSect4Time2sec (double refTime, sInt4 delt, int unit, double *ans);

/* Possible error messages left in errSprintf() */
int MetaParse (grib_MetaData * meta, sInt4 *is0, sInt4 ns0,
               sInt4 *is1, sInt4 ns1, sInt4 *is2, sInt4 ns2,
               float *rdat, sInt4 nrdat, sInt4 *idat, sInt4 nidat,
               sInt4 *is3, sInt4 ns3, sInt4 *is4, sInt4 ns4,
               sInt4 *is5, sInt4 ns5, sInt4 grib_len,
               float xmissp, float xmisss, int simpVer, int simpWWA);

#ifdef __cplusplus

void ParseGrid (VSILFILE *fp, gridAttribType * attrib, double **Grib_Data,
                uInt4 *grib_DataLen, uInt4 Nx, uInt4 Ny, int scan,
                sInt4 nd2x3, sInt4 *iain, sInt4 ibitmap, sInt4 *ib, double unitM,
                double unitB, uChar f_txtType, uInt4 txt_dataLen,
                uChar *txt_f_valid,
                uChar f_subGrid, int startX, int startY, int stopX, int stopY);

#endif

void FreqPrint (char **ans, double *Data, sInt4 DataLen, sInt4 Nx,
                sInt4 Ny, sChar decimal, char *comment);

#if 0  // Unused with GDAL
/* Possible error messages left in errSprintf() */
int MetaPrintGDS (gdsType * gds, int version, char **ans);

/* Possible error messages left in errSprintf() */
int MetaPrint (grib_MetaData *meta, char **ans, sChar decimal, sChar f_unit);
#endif  // Unused with GDAL.

sInt4 sbit_2Comp_fourByte(sInt4 data);

sChar sbit_2Comp_oneByte(sChar data);
 
#ifdef __cplusplus
}
#endif  /* __cplusplus */

#endif /* META_H */
