/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2018 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
*/
#ifndef _AFTERBURNER_H
#define _AFTERBURNER_H

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* =============================================== */
/* These include files should be standard on all   */
/* UNIX systems.                                   */
/* =============================================== */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#ifndef _ERROR_H
#include "error.h"
#endif
#ifndef _DMEMORY_H
#include "dmemory.h"
#endif

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#define MaxLevel 1024

#define MaxCodes 277

#define S_ECHAM5 1

struct Date
{
  int yr;
  int mo;
  int dy;
  int hr;
  int mn;
};

struct Control
{
  int Verbose;

  int Mean;
  int MeanCount0;
  int MeanCount;
  int Multi;
  int Nfiles;
  int TermCount;

  int OutputInterval;
  int EndOfInterval;

  int AnalysisData; /* 0 = ECHAM Data, 1 = ECMWF Spectral Analyses */
  int DayIn;        /* day increment of infiles if Multi = TRUE    */
  int Debug;
  int Extrapolate;
  int Szip;

  int istreamID;
  int ostreamID;
  int ostreamID2;
  int ivlistID;
  int ovlistID;
  int ovlistID2;
  int taxisID;
  int taxisID2;

  struct Date NextDate;
  struct Date NewDate;
  struct Date OldDate;
  struct Date StartDate;

  int nvct;
  double *vct;

  int *vert_index;
  size_t *pnmiss;
  double *Orography;
  double *p_of_height;

  int Type;
  int unitsel;

  int Fouriers;
  int Latitudes;
  int Longitudes;
  int HalfLevels;
  int Gaussian;
  int Spectral;

  int Truncation;
  int Waves;

  int Dim3FC, Dim3SP, Dim3GP;
  int DimFC, DimGP, DimSP;
  int DimSP_half;

  double *poli;
  double *pold;
  double *pdev;
  double *pol2;
  double *pol3;

  double *dv2uv_f1;
  double *dv2uv_f2;

  int NumCodesRequest;

  int NumLevel;
  int NumLevelFound;
  int NumLevelRequest;
  double LevelRequest[MaxLevel];

  double *rcoslat;
  double *coslat;
  double *DerivationFactor;
  double *Field;
};

struct Variable
{
  int needed0;  /* var needed for process  */
  int needed;   /* var needed for process  */
  int selected; /* var selected for output */
  int detected; /* var detected in input   */
  int comp;     /* compute var if selected and not detected */
  int sfit;
  int hlev;
  int plev;
  int ivarID;
  int ovarID;  /* 1st variable ID */
  int ovarID2; /* 2nd variable ID used for variance */
  int tableID;
  int igridID;
  int ogridID;
  int izaxisID;
  int ozaxisID;
  size_t nmiss0;
  size_t nmiss;
  double missval;
  double *spectral;
  double *spectral0;
  double *fourier;
  double *hybrid;
  double *hybrid0;
  double *height;
  double *grid;
  double *grid0;
  double *mean;
  double *variance;
  int *samp;
};

/* FFT */
void fft_set(double *trigs, long *ifax, long n);
void fc2gp(double *restrict trig, long *restrict ifax, double *restrict fc, double *restrict gp, long nlat, long nlon,
           long nlev, long nfc);
void gp2fc(double *trig, long *ifax, const double *restrict gp, double *restrict fc, long nlat, long nlon, long nlev, long nfc);

/* Convert Spectral Array to new resolution */
void sp2sp(double *arrayIn, int truncIn, double *arrayOut, int truncOut);
void sp2fc(const double *sa, double *fa, const double *poli, long nlev, long nlat, long nfc, long nt);
void fc2sp(double *fa, double *sa, double *poli, int klev, int nlat, int nfc, int nt);

/* Physc */
void dv2ps(const double *restrict div, double *restrict pot, long nlev, long ntr);
void dv2uv(double *d, double *o, double *u, double *v, double *f, double *g, int nt, int nsp, int nlev);
void scaluv(double *fu, double rclat[], int nlat, int lot);
void uv2dv(double *fu, double *fv, double *sd, double *sv, double *pol2, double *pol3, int klev, int nlat, int nt);
void geninx(long ntr, double *f, double *g);

/* clang-format off */
#define    LOW_CLOUD   34
#define    MID_CLOUD   35
#define    HIH_CLOUD   36
#define    LOW_WATER   37  /* not used ?   */
#define    MID_WATER   38  /* not used ?   */
#define    HIH_WATER   39  /* not used ?   */
#define    ALL_WATER   40  /* not used ?   */

#define GEOPOTENTIAL  129
#define  TEMPERATURE  130
#define       U_WIND  131
#define       V_WIND  132
#define     HUMIDITY  133
#define           PS  134
#define        OMEGA  135
#define    VORTICITY  138
#define           TS  139
#define       STREAM  148
#define      VELOPOT  149
#define          SLP  151
#define         LNPS  152
#define   DIVERGENCE  155
#define GEOPOTHEIGHT  156
#define    RHUMIDITY  157

#define   SW_BOT_CLF  189  /* not used ?   */
#define   LW_BOT_CLF  190  /* not used ?   */
#define   SW_TOP_CLF  191  /* not used ?   */
#define   LW_TOP_CLF  192  /* not used ?   */
#define  NET_TOP_CLF  193  /* not computed */

#define    WINDSPEED  259
#define       PRECIP  260
#define      NET_TOP  261
#define      NET_BOT  262
#define     NET_HEAT  263
#define    NET_WATER  264
#define       SW_CLF  265
#define       LW_CLF  266
#define      NET_CLF  267
#define       SW_ATM  268
#define       LW_ATM  269
#define      NET_ATM  270
#define  SURF_RUNOFF  271
#define        DPSDX  273
#define        DPSDY  274
#define  FRESH_WATER  275
#define      PS_PROG  276  /* PS for prognostic timestep */
#define   HALF_PRESS  277
#define   FULL_PRESS  278
#define       THETAH  279
#define       THETAF  280
/* clang-format on */

void after_read_vct(const char *vctfile, double **vct, int *nvct);

void after_gp2sp(struct Control *globs, struct Variable *vars, int ccode);
void after_GP2FC(double *gp, double *fc, long nlat, long nlon, long nlev, long nfc);
void after_FC2GP(double *fc, double *gp, long nlat, long nlon, long nlev, long nfc);
void after_FCrh2FCsh(struct Control *globs, struct Variable *vars);
void after_SPuv2SPdv(struct Control *globs, struct Variable *vars);
void after_FCsh2FCrh(struct Control *globs, struct Variable *vars);

void after_EchamCompGP(struct Control *globs, struct Variable *vars);
void after_processPL(struct Control *globs, struct Variable *vars);
void after_processML(struct Control *globs, struct Variable *vars);

void after_AnalysisAddRecord(struct Control *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID,
                             size_t nmiss);
double *after_get_dataptr(struct Variable *vars, int code, int gridID, int zaxisID, int levelID);
void after_EchamAddRecord(struct Control *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID,
                          size_t nmiss);

void after_AnalysisDependencies(struct Variable *vars, int ncodes);
void after_EchamDependencies(struct Variable *vars, int ncodes, int type, int source);

void after_legini_setup(struct Control *globs, struct Variable *vars);

#endif /*  afterburner.h  */
