/*
  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 _GRID_H
#define _GRID_H

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <cmath>
#include <stdbool.h>

extern double grid_missval;

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288 /* pi */
#endif

#ifndef RAD2DEG
#define RAD2DEG (180. / M_PI) /* conversion for rad to deg */
#endif

#ifndef DEG2RAD
#define DEG2RAD (M_PI / 180.) /* conversion for deg to rad */
#endif

int nfc_to_nlat(int nfc, int ntr);
int nlat_to_ntr(int nlat);
int nlat_to_ntr_linear(int nlat);
int ntr_to_nlat(int ntr);
int ntr_to_nlat_linear(int ntr);
int nlat_to_nlon(int nlat);

void grid_copy_attributes(int gridID1, int gridID2);
void grid_copy_mapping(int gridID1, int gridID2);

void grid_def_param_laea(int gridID, double a, double lon0, double lat0);
void grid_def_param_sinu(int gridID);

bool grid_is_distance_generic(int gridID);

void grid_to_radian(const char *units, size_t nvals, double *restrict values, const char *description);
void grid_to_degree(const char *units, size_t nvals, double *restrict values, const char *description);

void grid_gen_corners(size_t n, const double *restrict vals, double *restrict corners);
void grid_gen_bounds(size_t n, const double *restrict vals, double *restrict bounds);
void grid_check_lat_borders(int n, double *ybounds);

void grid_gen_xbounds2D(size_t nx, size_t ny, const double *restrict xbounds, double *restrict xbounds2D);
void grid_gen_ybounds2D(size_t nx, size_t ny, const double *restrict ybounds, double *restrict ybounds2D);

void grid_cell_center_to_bounds_X2D(const char *xunitstr, size_t xsize, size_t ysize, const double *restrict grid_center_lon,
                                    double *restrict grid_corner_lon, double dlon);
void grid_cell_center_to_bounds_Y2D(const char *yunitstr, size_t xsize, size_t ysize, const double *restrict grid_center_lat,
                                    double *restrict grid_corner_lat);

int gridWeights(int gridID, double *weights);
int gridGenArea(int gridID, double *area);

int referenceToGrid(int gridID);
int gridToZonal(int gridID);
int gridToMeridional(int gridID);
int gridToUnstructured(int gridID, int lbounds);
int gridToUnstructuredSelecton(int gridID1, size_t selectionSize, int *selectionIndexList, int nocoords, int nobounds);
int gridToCurvilinear(int gridID, int lbounds);
int gridCurvilinearToRegular(int gridID);
int gridToRegular(int gridID);
void field2regular(int gridID1, int gridID2, double missval, double *array, size_t nmiss, int lnearest);

/* GME grid */
struct cart
{
  double x[3];
};

struct geo
{
  double lon;
  double lat;
};

void correct_sinxvals(int xsize, int ysize, double *xvals);

struct cart gc2cc(struct geo *position);
void factorni(int kni, int *kni2, int *kni3);
void gme_grid_restore(double *p, int ni, int nd);
void gme_grid(int lbounds, size_t gridsize, double *rlon, double *rlat, double *blon, double *blat, int *imask, int ni, int nd,
              int ni2, int ni3);

/* Rotated grid */
double lamrot_to_lam(double phis, double rlas, double polphi, double pollam, double polgam);
double phirot_to_phi(double phis, double rlas, double polphi, double polgam);
void usvs_to_uv(double us, double vs, double phi, double rla, double polphi, double pollam, double *u, double *v);

void cdo_print_grid(int gridID, int opt);

bool grid_has_proj4param(int gridID);

// Define a de-staggered grid for U and V
int cdo_define_destagered_grid(int gridID_u_stag, int gridID_v_stag, double *destagGridOffsets);

// Define a sampled grid of another grid
int cdo_define_sample_grid(int gridID, int sampleFactor);

// Define a sub-grid of another grid
int cdo_define_subgrid_grid(int gridSrcID, int subI0, int subI1, int subJ0, int subJ1);

#endif /* _GRID_H */
