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

int
gribVersion(unsigned char *is, size_t buffersize)
{
  if (buffersize < 8) Error("Buffer too small (current size %d)!", (int) buffersize);

  return GRIB_EDITION(is);
}

static double
GET_Real(unsigned char *grib)
{
  int iexp = GET_UINT1(grib[0]);
  int imant = (int) (GET_UINT3(grib[1], grib[2], grib[3]));

  return decfp2(iexp, imant);
}

static size_t
decodeIS(unsigned char *is, int *isec0, int *iret)
{
  // Octets 1 - 4 : The letters G R I B. Four 8 bit fields.

  // Check letters -> GRIB, BUDG or TIDE.

  // Check that 'GRIB' is found where expected.
  bool lgrib = GRIB_START(is);

  // ECMWF pseudo-grib data uses 'BUDG' and 'TIDE'.
  bool lbudg = BUDG_START(is);
  bool ltide = TIDE_START(is);

  // Data is not GRIB or pseudo-grib.
  if (lgrib == false && lbudg == false && ltide == false)
    {
      *iret = 305;
      gprintf(__func__, "Input data is not GRIB or pseudo-grib.");
      gprintf(__func__, "Return code = %d", *iret);
    }
  if (lbudg || ltide)
    {
      *iret = 305;
      gprintf(__func__, "Pseudo-grib data unsupported.");
      gprintf(__func__, "Return code = %d", *iret);
    }

  // Octets 5 - 7 : Length of message. One 24 bit field.
  ISEC0_GRIB_Len = (int) (GRIB1_SECLEN(is));

  // Octet 8 : GRIB Edition Number. One 8 bit field.
  ISEC0_GRIB_Version = GRIB_EDITION(is);

  if (ISEC0_GRIB_Version > 1) Error("GRIB version %d unsupported!", ISEC0_GRIB_Version);

  int grib1offset = ISEC0_GRIB_Version * 4;

  size_t isLen = 4 + (size_t) grib1offset;

  return isLen;
}

static void
decodePDS_ECMWF_local_Extension_1(unsigned char *pds, int *isec1)
{
  isec1[36] = GET_UINT1(pds[40]);                  /* extension identifier       */
  isec1[37] = GET_UINT1(pds[41]);                  /* Class                      */
  isec1[38] = GET_UINT1(pds[42]);                  /* Type                       */
  isec1[39] = (int) (GET_UINT2(pds[43], pds[44])); /* Stream                     */
  /* isec1[40] = GET_UINT4(pds[45],pds[46],pds[47],pds[48]); */
  memcpy((char *) &isec1[40], &pds[45], 4);
  isec1[41] = GET_UINT1(pds[49]); /* Forecast number            */
  isec1[42] = GET_UINT1(pds[50]); /* Total number of forecasts  */
}

static void
decodePDS_DWD_local_Extension_254(unsigned char *pds, int *isec1)
{
  isec1[36] = GET_UINT1(pds[40]); /* extension identifier */
  for (int i = 0; i < 11; ++i) isec1[37 + i] = GET_UINT1(pds[41 + i]);

  int isvn = (int) (GET_UINT2(pds[52], pds[53]));

  isec1[48] = isvn % 0x8000; /* DWD experiment identifier            */
  isec1[49] = isvn >> 15;    /* DWD run type (0=main, 2=ass, 3=test) */
}

static void
decodePDS_DWD_local_Extension_253(unsigned char *pds, int *isec1)
{
  isec1[36] = GET_UINT1(pds[40]); /* extension identifier */
  for (int i = 0; i < 11; ++i) isec1[37 + i] = GET_UINT1(pds[41 + i]);

  int isvn = (int) (GET_UINT2(pds[52], pds[53]));

  isec1[48] = isvn % 0x8000;                       /* DWD experiment identifier            */
  isec1[49] = isvn >> 15;                          /* DWD run type (0=main, 2=ass, 3=test) */
  isec1[50] = GET_UINT1(pds[54]);                  /* User id, specified by table          */
  isec1[51] = (int) (GET_UINT2(pds[55], pds[56])); /* Experiment identifier                */
  isec1[52] = (int) (GET_UINT2(pds[57], pds[58])); /* Ensemble identification by table     */
  isec1[53] = (int) (GET_UINT2(pds[59], pds[60])); /* Number of ensemble members           */
  isec1[54] = (int) (GET_UINT2(pds[61], pds[62])); /* Actual number of ensemble member     */
  isec1[55] = GET_UINT1(pds[63]);                  /* Model major version number           */
  isec1[56] = GET_UINT1(pds[64]);                  /* Model minor version number           */
}

static void
decodePDS_MPIM_local_Extension_1(unsigned char *pds, int *isec1)
{
  isec1[36] = GET_UINT1(pds[40]);                  /* extension identifier            */
  isec1[37] = GET_UINT1(pds[41]);                  /* type of ensemble forecast       */
  isec1[38] = (int) (GET_UINT2(pds[42], pds[43])); /* individual ensemble member      */
  isec1[39] = (int) (GET_UINT2(pds[44], pds[45])); /* number of forecasts in ensemble */
}

static size_t
decodePDS(unsigned char *pds, int *isec0, int *isec1)
{
  size_t pdsLen = PDS_Len;

  // clang-format off
  ISEC1_CodeTable      = PDS_CodeTable;
  ISEC1_CenterID       = PDS_CenterID;
  ISEC1_ModelID        = PDS_ModelID;
  ISEC1_GridDefinition = PDS_GridDefinition;
  ISEC1_Sec2Or3Flag    = PDS_Sec2Or3Flag;
  ISEC1_Parameter      = PDS_Parameter;
  ISEC1_LevelType      = PDS_LevelType;

  if ( (ISEC1_LevelType !=  20) && 
       (ISEC1_LevelType != GRIB1_LTYPE_99)           && 
       (ISEC1_LevelType != GRIB1_LTYPE_ISOBARIC)     && 
       (ISEC1_LevelType != GRIB1_LTYPE_ISOBARIC_PA)  && 
       (ISEC1_LevelType != GRIB1_LTYPE_ALTITUDE)     && 
       (ISEC1_LevelType != GRIB1_LTYPE_HEIGHT)       && 
       (ISEC1_LevelType != GRIB1_LTYPE_SIGMA)        && 
       (ISEC1_LevelType != GRIB1_LTYPE_HYBRID)       && 
       (ISEC1_LevelType != GRIB1_LTYPE_LANDDEPTH)    && 
       (ISEC1_LevelType != GRIB1_LTYPE_ISENTROPIC)   && 
       (ISEC1_LevelType != 115) && 
       (ISEC1_LevelType != 117) && 
       (ISEC1_LevelType != 125) && 
       (ISEC1_LevelType != 127) && 
       (ISEC1_LevelType != GRIB1_LTYPE_SEADEPTH)     && 
       (ISEC1_LevelType != 210) )
    {
      ISEC1_Level1 = PDS_Level1;
      ISEC1_Level2 = PDS_Level2;
    }
  else
    {
      ISEC1_Level1 = (int)(PDS_Level);
      ISEC1_Level2 = 0;
    }

  /* ISEC1_Year        = PDS_Year; */
  ISEC1_Month          = PDS_Month;
  ISEC1_Day            = PDS_Day;
  ISEC1_Hour           = PDS_Hour;
  ISEC1_Minute         = PDS_Minute;
  ISEC1_TimeUnit       = PDS_TimeUnit;
  ISEC1_TimePeriod1    = PDS_TimePeriod1;
  ISEC1_TimePeriod2    = PDS_TimePeriod2;
  ISEC1_TimeRange      = PDS_TimeRange;
  ISEC1_AvgNum         = (int)(PDS_AvgNum);
  ISEC1_AvgMiss        = PDS_AvgMiss;

  if ( ISEC0_GRIB_Version == 1 )
    {
      ISEC1_Year           = PDS_Year;
      ISEC1_Century        = PDS_Century;
      ISEC1_SubCenterID    = PDS_Subcenter;
      ISEC1_DecScaleFactor = PDS_DecimalScale;
    }
  else
    {
      int year             = GET_UINT1(pds[12]);
      if ( year <= 100 )
	{
	  ISEC1_Year       = year;
	  ISEC1_Century    = 1;
	}
      else
	{
	  ISEC1_Year       = year%100;
	  ISEC1_Century    = 1 + (year-ISEC1_Year)/100;
	}
      ISEC1_SubCenterID    = 0;
      ISEC1_DecScaleFactor = 0;
    }

  if ( ISEC1_Year < 0 )
    {
      ISEC1_Year    = -ISEC1_Year;
      ISEC1_Century = -ISEC1_Century;
    }

  ISEC1_LocalFLag = 0;
  if ( pdsLen > 28 )
    {
      size_t localextlen = pdsLen-28;

      if ( localextlen > 4000 )
	{
	  Warning("PDS larger than 4000 bytes not supported!");
	}
      else
	{
	  ISEC1_LocalFLag = 1;

	  if ( ISEC1_CenterID == 78 || ISEC1_CenterID == 215 || ISEC1_CenterID == 250 )
	    {
	      if ( pds[40] == 254 ) 
                decodePDS_DWD_local_Extension_254(pds, isec1);
	      else if ( pds[40] == 253 )
                decodePDS_DWD_local_Extension_253(pds, isec1);
	    }
	  else if ( (ISEC1_CenterID    == 98 && ISEC1_LocalFLag ==  1) ||
		    (ISEC1_SubCenterID == 98 && ISEC1_LocalFLag ==  1) ||
		    (ISEC1_CenterID    ==  7 && ISEC1_SubCenterID == 98) )
	    {
	      if ( pds[40] == 1 )
		decodePDS_ECMWF_local_Extension_1(pds, isec1);
	    }
	  else if ( ISEC1_CenterID    == 252 && ISEC1_LocalFLag ==  1 )
	    {
	      if ( pds[40] == 1 )
		decodePDS_MPIM_local_Extension_1(pds, isec1);	      
	    }
	  else
	    {
	      for ( size_t i = 0; i < localextlen; i++ )
                isec1[24+i] = pds[28+i];
	    }
	}
    }
  // clang-format on

  return pdsLen;
}

static void
gribPrintSec2_double(int *isec0, int *isec2, double *fsec2)
{
  gribPrintSec2DP(isec0, isec2, fsec2);
}
static void
gribPrintSec3_double(int *isec0, int *isec3, double *fsec3)
{
  gribPrintSec3DP(isec0, isec3, fsec3);
}
static void
gribPrintSec4_double(int *isec0, int *isec4, double *fsec4)
{
  gribPrintSec4DP(isec0, isec4, fsec4);
}
static void
gribPrintSec2_float(int *isec0, int *isec2, float *fsec2)
{
  gribPrintSec2SP(isec0, isec2, fsec2);
}
static void
gribPrintSec3_float(int *isec0, int *isec3, float *fsec3)
{
  gribPrintSec3SP(isec0, isec3, fsec3);
}
static void
gribPrintSec4_float(int *isec0, int *isec4, float *fsec4)
{
  gribPrintSec4SP(isec0, isec4, fsec4);
}

// clang-format off
#include "templates.h"

#ifdef T
#undef T
#endif
#define T double
#include "decode_array.tmpl"

#ifdef T
#undef T
#endif
#define T float
#include "decode_array.tmpl"


#ifdef T
#undef T
#endif
#define T double
#include "grib_decode.tmpl"

#ifdef T
#undef T
#endif
#define T float
#include "grib_decode.tmpl"
// clang-format on
