/* emu.h
 *          *
 *        Emilio            4/16/2001
 *          Added constant LONGLINELEN, for handling long char lines.
 *          *
 * Header file for all the C programs written to analyze binary and NetCDF
 * input and output for modelling using the Environmental Modeling Utilities,
 * or "EMU" package.
 ******************************************************************************/

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "unitconv.h"

#ifdef PC
  #include <float.h>
#endif
#ifdef UNIX
  #include <ieeefp.h>
#endif
#ifdef NONETCDF
  #include "nonetcdf.h"
#else
  #include <netcdf.h>
#endif


#define   TRUE         1
#define   FALSE        0
#define   NONE        -1

#define   TOL          0.0001
#define   FILENAMELEN  120
#define   NAMELEN      60
#define   LINELEN      300
#define   LONGLINELEN 5000

#define   FMIN   1.0e-37F  /* actually these go to 38, but for cross system  */
#define   FMAX   1.0e+37F  /* compatibility we'll limit it to this.          */
#define   SMIN  -32768
#define   SMAX   32767
#define   FSMAX  32767.0F

typedef int                 BOOL;     /* Hungarian b   */
typedef unsigned char       BYTE;     /* Hungarian by  */
typedef short int           SHORT;    /* Hungarian s, n is for straight int,
                                       * sz is for char * string             */
typedef unsigned short int  WORD;     /* Hungarian w   */
typedef long int            LONG;     /* Hungarian l   */
typedef unsigned long       DWORD;    /* Hungarian dw  */

BOOL bProcess;
BOOL bVerbose;
BOOL bQuiet;
BOOL bInteractive;
BOOL bDebug;
BOOL bNoMask;
BOOL bOutBinary;
char *pszOutputFile, *pszNCGenFile, *pszNCtitle, *pszNChist;
int   errnc;
char *pszOutputFilePath;

/* ====== Generic constant names used in several applications ====== */

#define   CELLCNT   0
#define   COUNT     1
#define   AREA      1
#define   SUM       0
#define   SUM2      1
#define   SUM2XY    2
#define   MEAN      0
#define   SD        1
#define   MIN       2
#define   MAX       3

/* ============= OPTION ARGUMENTS FOR Initialize() FUNCTION =============== */

#define   NOMASK    0
#define   DOMASK    1

/* ============== CUSTOMAZIBLE, GLOBAL PARAMETER ARRAYS =================== */

int     nParams;       /* total number of parameters */
float   *P;            /* parameter array            */
int     nStrParams;    /* total number of string parameters               */
char    *szP[10];      /* array of string parameters (10 max)             */


/* ======= GLOBAL VARIABLES USED TO HOLD GEOGRAPHIC-SPATIAL INFORMATION === */

int     nYBlocks, nBlockRows;
int     nRows, nCols;                          /* size of image              */
float   fLatTop, fLatBtm, fLonRight, fLonLeft; /* Geographic, lat-lon frame  */
float   fVScale, fHScale;                  /* size of each pixel, in degrees */
int     nOutRows, nOutCols;                    /* size of image              */

/* ===== GLOBAL VARIABLES USED TO HOLD TIME AND TIME AXIS INFORMATION ====== */

#define   NOTIME   -1
#define   MONTHLY   0
#define   MONMEAN   1
#define   ANNUAL    2

     /* FirstMonth & LastMonth are NOT base 0 (ie, january = 1, not 0) */
int  YEARORIGIN;
int  nFirstYear;               /* FIRST YEAR                                 */
int  nFirstMonth;              /* FIRST MONTH                                */
int  nLastYear;                /* LAST YEAR                                  */
int  nLastMonth;               /* LAST MONTH                                 */
int  nTotalYears;              /* TOTAL NUMBER OF YEARS                      */
int  nTimeSteps;               /* TOTAL NUMBER OF MONTHS (TIME STEPS)        */
int  nTotalMonths[YEAR2MONTH]; /* TOTAL NUMBER FOR EACH MONTH                */

/* ======= GLOBAL VARIABLES USED FOR HANDLING OF FILE PATHS & NAMES ======== */

#define   PATH      0
#define   PARAM     1

/* =======  DEFINITION OF INFORMATION FOR A VARIABLE ======================= */
typedef enum
{
    INTEL =    1,
    DEC =      1,
    MOTOROLA = 2,
} byteorder_type;

byteorder_type tByteOrder;

typedef enum
{
    EMU_BYTE   =  1,     /* unsigned 1 byte integer */
    EMU_CHAR   =  2,     /* ISO/ASCII character */
    EMU_SHORT  =  3,     /* signed 2 byte integer */
    EMU_INT    =  4,     /* signed 4 byte integer */
    EMU_FLOAT  =  5,     /* single precision floating point number */
    EMU_DOUBLE =  6,     /* double precision floating point number */
    EMU_USHORT =  7,     /* Unsigned 2 byte integer */
    EMU_WORD   =  7,     /* Unsigned 2 byte integer */
    EMU_LONG   =  4,     /* Signed 4 byte integer */
    EMU_ULONG  =  8,     /* Unsigned 4 byte integer */
    EMU_DWORD  =  8,     /* Unsigned 4 byte integer */
} emu_type;

typedef enum
{
    BADTYPE = 0,
    FSQ =   1,
    BSQ =   2,
    BIL =   3,
    BIP =   4,
} dataorg_type;

typedef struct _dynfilelist
{
  char  szName[FILENAMELEN];        /* Name of associated dyn file element  */
  char  szFormat[FILENAMELEN];      /* Name of associated dyn file element  */
  int   nElements;                  /* number of elemetns                   */
  char  * * ppszElement;
} DYNFILE;


typedef struct _var
{
/* ===== General Characteristics ===== */
  char     name[20];           /* Variable name                             */
  char     filetype[15];       /* File type: bin|intel|motorola, ascii, nc  */
  char     path[FILENAMELEN];  /* File Path                                 */
  char     param[FILENAMELEN]; /* File Name                                 */
  int      nDyn;               /* number of dynamic file elements           */
  DYNFILE *pstDyn;             /* array of dynamic file elements            */
  BOOL     bIsMetaData;        /* Variable is a place holder for metadata   */

/* ===== Optional, Variable-specific parameters ===== */
  int      nParams;      /* total number of numeric float-type parameters   */
  float   *P;            /* array of numeric float-type parameters          */
  int      nStrParams;   /* total number of string parameters               */
  char    *szP[10];      /* array of string parameters (10 max)             */

/* ===== Spatial (X-Y) Characteristics ===== */
  int      nCols;        /* Number of columns                               */
  int      nRows;        /* Number of rows                                  */
  char     YDir[10];     /* Orientation of Y axis: "normal" or "rev"        */

/* ===== Time Axis Characteristics ======= */
  short    tTimeType;    /* Mean-Axis type: MONTHLY, MONMEAN, ANNUAL        */
  double   t1;           /* value in months of 1st time step in time coord  */
  char     szUnit[NAMELEN];   /* Time axis units (month, hour, day)         */
  short    YearOrigin;   /* Origin Year for this variable (mostly nc vars)  */

/* ===== Data Type, Compression, Nodata Value And Type ===== */
  emu_type tEmuType;     /* EMU Data Type                                   */
  float    scale_factor; /* scale factor (gain), if present                 */
  float    add_offset;   /* offset factor (offset), if present              */
  short    nFillValue;
  float    fFillValue;

/* ===== Image Data Elements ===== */
  dataorg_type tDataOrg; /* Data Organization Type (bil, fsq, bsq, etc)     */
  int          nBands;   /* number of bands, for multi-band images          */

/* ===== Binary-Specific Elements ===== */
  BOOL     bByteSwap;    /* Byte Swap (with respect to the default)         */

/* ===== ASCII-Specific Elements ===== */
  char     delimeter;    /* Delimeter character used as field-separator     */

/* ===== Netcdf-Specific Elements ===== */
  int      ncid;         /* netCDF file id, if file is netCDF               */
  int      var_id;       /* id of variable if file is netCDF                */
} VAR;

VAR * pstVars;
VAR * pstOutput;
VAR * pstInput;
int nVars;

/* == GLOBAL VARIABLES RELATED TO MASKS, MASK IDS, AND CELLCOUNTS PER MASK ID */

#define  SITESMAX       500        /* max number of unique IDs from mask file */

char  szMaskType[15];  /* rangeposval OR singleposval OR rangefileIDs   *
                        * OR rangefileID1                               */
                       /* (ALL MASK VALUES MUST BE POSITIVE)            */
LONG  * pMask;         /* pointer that will be allocated space          *
                        * to contain mask array                         */
int   nRegionID;       /* selected single mask ID                       */

int     FlMskSitesN;       /* Total number of unique IDs > 0 in the mask file  */
int    *FlMskSitesIDs;     /* Array containing all unique IDs in the mask file */
int    *FlMskSitesCellcount; /* Array of cellcount per unique ID in mask file  */
int     FlMskTotCellcount; /* Total cellcount for all unique IDs in mask file  */
double *FlMskSitesArea;    /* Array of area (m2) per unique ID in mask file    */
double  FlMskTotArea;      /* Total area (m2) for all unique IDs in mask file  */

int     MskSitesN;         /* Total number of IDs that will be printed out     */
int    *MskSitesIDs;
int     MskTotCellcount;   /* Total cellcount for selected IDs                 */
double  MskTotArea;        /* Total area (m2) for selected IDs                 */
   /* With rangefileIDs, MskTotCellcount is used mainly when a single          *
    * aggregate ID is requested; with rangeposval it will be set to be         *
    * equal to FlMskTotCellcount                                               */

typedef struct _masks
{
    int     ID;          /* Aggregate ID                         */
    int     cellcount;   /* Total number of cells within the     *
                          * Aggregate ID zone                    */
    double  area;        /* Total area (in m^2) encompassed by   *
                          * Aggregate ID zone                    */
    int     nMaskids;    /* Number of IDs in Aggregate ID        */
    int    *maskids;     /* Array containing all the IDs in the  *
                          * Aggregate ID                         */
} MASK;

MASK *Masks;


/* ==== GLOBAL STRUCTURE USED TO CREATE & HANDLE A "Z" NETCDF DIMENSION ===== */

typedef enum
{
    XYZT = 1111,
    XYZ  = 1110,
    XYT  = 1101,
    XY   = 1100,
    XZT  = 1011,
    ZT   = 11,
    Z    = 10,
    T    = 1,
} AXES_TYPE;

AXES_TYPE AxesType;

typedef struct _axis
{
  BOOL   active;               /* 1 = yes, 0 = no  (TRUE/FALSE)      */
  char   name[20];
  char   lname[30];
  char   units[30];
  short  point_spacing_even;   /* 1 = yes, 0 = no                    */
  float  valid_range[2];       /* 1st value is min, 2nd value is max */
  int    length;               /* number of values                   */
  double *values;              /* array containing the actual values */

  /* fields specific to z/depth/elev axes */
  char   positiveOrient[5];    /* "up" or "down"                     */

  /* fields specific to time axes */
  char  time_origin[30];
  BOOL  monmean;
  char  time_modulo[2];
} AXIS;

AXIS stAxes[4];

/* === STRUCTS FOR HANDLING BITMAP (.bmp) IMAGE FILES === */

typedef struct _BITMAPFILEHEADER
{
  short  bfType;
  long   bfSize;
  short  bfRes1;
  short  bfRes2;
  long   bfOffBits;
} BITMAPFILEHEADER;

typedef struct _BITMAPINFOHEADER
{
  long      biSize;
  long      biWidth;
  long      biHeight;
  short int biPlanes;
  short     biBitCount;
  long      biCompression;
  long      biSizeImage;
  long      biXPelsPerMeter;
  long      biYPelsPerMeter;
  long      biClrUsed;
  long      biClrImportant;
} BITMAPINFOHEADER;

/* ===== GENERAL MACROS  ===== */

static float  sqrarg;
static double dsqrarg;
unsigned short tempr;
#define   SQR(a)    ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
#define   DSQR(a)   ((dsqrarg=(a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg)
#define   SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr;
#define   GMAX(a,b) (((a) > (b)) ? (a) : (b));
#define   GMIN(a,b) (((a) < (b)) ? (a) : (b));

/* ================ FUNCTIONS defined within each application =============== */

void     ProcessCommandLineArgs(int * pArgc, char * argv[]);
void     LocalCleanUp(void);

/* =================== FUNCTIONS FOUND IN function.c =================== */

void     Initialize(int * argc, char * argv[], short option);
short    GetVarNo(char *varname);
void     CopyVar(VAR *varto, VAR *varfrom);
nc_type  EMUtoNCType(emu_type EmuType);
emu_type NCtoEMUType(nc_type NCType);
size_t   EmuTypeSize(emu_type EmuType);
void     CleanUp(void);
void     GlobalCleanUp(void);

/* === FUNCTIONS FOUND IN function.c, kept for backward compatibility === */

void   SimpleInitialize(int * argc, char * argv[]);
void   ReadVariable(VAR * pstVar, char * pszVarKeyword);
void   ReadInput(void);
void   ReadOutput(void);


/* =================== FUNCTIONS FOUND IN io.c ====================== */
void ReadXY(float *pfData, VAR var, int year, int month);
void ReadT(float *ts4byte, VAR var, long int i);
void ReadTXYbl(float **TsXYblock4byte, VAR var, int nyblock);
void ReadXBData(float * * ppfData, VAR * pVar, int y);
void ReadYXBData(float * * * pppfData, VAR * pVar, int yStart, int yExtent, int xStart, int xExtent);
void WriteXY(float * pfData, VAR var, int nYear, int nMonth);
void WriteXBData(float * * ppfData, VAR * pVar, int y);
void WriteYXBData(float * * * pppfData, VAR * pVar, int yStart, int yExtent, int xStart, int xExtent);
void WriteBMP(VAR * pVar, BYTE * * ppbyData);
void WriteBinaryOutput(FILE * fp, float * pfData, emu_type tEmuType, int nElements);
FILE *OUTPUTfile(char *filename, char *extension);

/* === FUNCTIONS FOUND IN io.c, kept for backward compatibility === */
void ReadXYData(float * pfData, VAR * pVar);
void WriteXYMap(float * pfData, VAR * pVar, int nYear, int nMonth);
void WriteXYData(float * pfData, VAR * pVar);


/* =================== FUNCTIONS FOUND IN masks.c ====================== */

BOOL   masktest(int maskid);
int    siteindex(int *siteids, int maskid);

/* =================== FUNCTIONS FOUND IN ncoutput.c =================== */

void   SetupDefaultAxes(BOOL UseTimeAxis, BOOL monmean);
void   FillAxisProp(char *name, char *lname, char *units, short point_spacing_even,
          float min, float max, int length, double *values, BOOL positiveDown);
void   FillTAxisProp(char *name, char *lname, char *units, short point_spacing_even,
          float min, float max, int length, double *values, char *time_origin, BOOL Tmonmean);
int    newNCsetup(int *tID, char *pszOutputFile, char *title, char *history);
void   CreateCDFFromCDL(char * pszCDL, char * pszCDF, char * pszTitle,
          char * pszHistory, char * timeunits, char * timeorigin, char * pszType);
int    DefNCvar(int ncID, char *name, emu_type EmuType, char *dims, char *longname, char *units);
void   DefNCvarsFromNCs(int ncIDin, char * pszNC, emu_type EmuType);
void   AddNCCompressionAttributes(int ncID, int varID, float scale_factor, float add_offset);
void   AddAttribute(int ncIDin, char *pszNC, int nVarIDin, char *pszVar,
          char *pszAtt, emu_type EmuType, int nLen, void *pvValues);


/* =================== FUNCTIONS FOUND IN TimeUtilities.c =================== */
/* define the date on which the Gregorian calendar began: October 15, 1582 */
#define GREGORIAN_START (15 + 31L * (10 + 12L * 1582))
#define GREGORIAN_CROSSOVER 2299161

/* time function prototypes */

/* a postive year signifies AD; negative, BC.  Remember that the year after 1 BC was */
/* 1 AD.  The Solar day refers to the day of the year (Gregorian year), with */
/* January 1st of each year being Solar day 0. */

void  GregorianToJulianDay(int nMonth, int nDay, int nYear, long *plJulianDay);
void  GregorianToSolarDay(int nMonth, int nDay, int nYear, int *pnSolarDay);
void  JulianToGregorianDay(long lJulianDay, int *pnMonth, int *pnDay, int *pnYear);
void  JulianToSolarDay(long lJulianDay, int *pnSolarDay);
void  SolarToGregorianDay(int nSolarDay, int nYear, int *pnMonth, int *pnDay);
void  SolarToJulianDay(int nSolarDay, int nYear, long *plJulianDay);

BOOL  IsLeapYear(int nYear);
short GetDaysInMonth(short month, int nYear);
int   GetDayOfWeekIndex(long lJulianDay);
void  GetDayOfWeekName(long lJulianDay, char *pszDayOfWeek);


/* =================== FUNCTIONS FOUND IN util.c =================== */

void    getyrmonth(int tvar, int *year, int *month);
void    NEXTyrmo(int *yr, int *mo);
long int  getRowRevCol(long int i, int *col, int *row);
double  cellarea(int i);
double  area(double latitude, double vscale, double hscale);
int     roundoff(double value);
float   CorrCoef(float * pf1, float * pf2, int nOffset);
float   Mean(float * * ppfData, int nOffset);
void    SwapBytes(BYTE * pbyData, int nSwapBytes, int nBytes);
int     IsBad(double dTest);

/* string handling functions */
int     istype(char *var, char *type);
void    AssignString(char * * ppszTarget, char * pszSource);
char    *GetQuotedText(int * i, char * argv[]);
void    DropTrailingSpaces(char *pszStr);
void    DropSurroundingQuotes(char *pszStr);
void    ClearString(char *string, int StringLen);
int     strcnt(char * psz, char c);
int     GetLineElements(char **ppszArgs, char *pszLine, char delimeter);
void    GetFixedWidthElements(char *ppszArgs[], int TotElements, char *pszLine, int indices[][2]);
void    FreeLineElements(char **StringArray, int TotElements);

/* file handling functions (esp. text files) */
void    DoesFileExist(char * pszFile);
int     fgetline(char *line, int lim, FILE *fin);
long    GetTotalLines(FILE *fdat);
BOOL    SkipLines(FILE *fid, long SkipSize);

void    PrintTime(void);
void    Warning(char * pszWarningString, char * pszArg);
void    ExitError(char * pszErrorString, char * arg);
void    nrerror(char error_text[], char *errorvar);

/* ----------- allocation/deallocation of 1-D arrays ------------ */
VAR    *alloc1d_VAR(long nl, long nh);
void   VAR">free1d_VAR(VAR *v, long nl, long nh);
MASK   *alloc1d_MASK(long nl, long nh);
void   MASK">free1d_MASK(MASK *v, long nl, long nh);

double *alloc1d_d(long nl, long nh);
void   free1d_d(double *v, long nl, long nh);
float  *alloc1d_f(long nl, long nh);
void   free1d_f(float *v, long nl, long nh);
int    *alloc1d_i(long nl, long nh);
void   free1d_i(int *v, long nl, long nh);
long   *alloc1d_l(long nl, long nh);
void   free1d_l(long *v, long nl, long nh);
short  *alloc1d_s(long nl, long nh);
void   free1d_s(short *v, long nl, long nh);
unsigned short *alloc1d_us(long nl, long nh);
void   free1d_us(unsigned short *v, long nl, long nh);
unsigned char *alloc1d_uc(long nl, long nh);
void   free1d_uc(unsigned char *v, long nl, long nh);

/* ----------- allocation/deallocation of 2-D arrays ------------ */
double **alloc2d_d(long nrl, long nrh, long ncl, long nch);
void   free2d_d(double **m, long nrl, long nrh, long ncl, long nch);
float  **alloc2d_f(long nrl, long nrh, long ncl, long nch);
void   free2d_f(float **m, long nrl, long nrh, long ncl, long nch);
LONG   **alloc2d_l(long nrl, long nrh, long ncl, long nch);
void   free2d_l(LONG **m, long nrl, long nrh, long ncl, long nch);
short  **alloc2d_s(long nrl, long nrh, long ncl, long nch);
void   free2d_s(short **m, long nrl, long nrh, long ncl, long nch);
BYTE   **alloc2d_by(long nrl, long nrh, long ncl, long nch);
void   free2d_by(BYTE **m, long nrl, long nrh, long ncl, long nch);

/* ----------- allocation/deallocation of 3-D arrays ------------ */
short  ***alloc3d_s(long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
void   free3d_s(short ***t, long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
float  ***alloc3d_f(long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
void   free3d_f(float ***t, long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
double ***alloc3d_d(long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
void   free3d_d(double ***t, long nrl, long nrh, long ncl, long nch, long ndl, long ndh);

void   CheckDeallocation (void);
void   FreeVectorToMatrix (float * * ppfMatrix);
float  * * VectorToMatrix (float * pfVector);