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

  Author: Uwe Schulzweida

*/
#ifndef REMAP_H
#define REMAP_H

#include <cstdint>
#include <cmath>

#include "varray.h"
#include "remap_vars.h"
#include "remap_grid.h"
#include "grid_cellsearch.h"
#include "grid_pointsearch.h"
#include "point.h"
#include "mpim_grid/grid_healpix.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif

constexpr double PI = M_PI;
constexpr double PI2 = (2.0 * PI);
constexpr double PIH = (0.5 * PI);
constexpr float PI_f = PI;
constexpr float PI2_f = PI2;
constexpr float PIH_f = PIH;

constexpr double TINY = 1.e-14;

#define REMAP_GRID_BASIS_SRC 1
#define REMAP_GRID_BASIS_TGT 2

// clang-format off
struct  // RemapSearch
#ifdef WARN_UNUSED
[[gnu::warn_unused]]
#endif
RemapSearch
// clang-format on
{
  RemapGrid *srcGrid;
  RemapGrid *tgtGrid;

  GridPointsearch gps;
  GridCellsearch gcs;
};

// clang-format off
struct  // RemapType
#ifdef WARN_UNUSED
[[gnu::warn_unused]]
#endif
RemapType
// clang-format on
{
  int nused{ 0 };
  int gridID{ -1 };
  size_t gridsize{ 0 };
  size_t numMissVals{ 0 };
  RemapGrid srcGrid;
  RemapGrid tgtGrid;
  RemapVars vars;
  RemapSearch search;
};

#define REMAP_WRITE_REMAP 2
#define REMAP_MAX_ITER 3
#define REMAP_GENWEIGHTS 5

int remap_check_mask_indices(const size_t (&indices)[4], const Vmask &mask);

void remap_set_int(int remapvar, int value);

void remap_init_grids(RemapMethod mapType, bool doExtrapolate, int gridID1, RemapGrid &srcGrid, int gridID2, RemapGrid &tgtGrid);

void remap_grid_free(RemapGrid &grid, bool removeMask = true);
void remap_grid_alloc(RemapMethod mapType, RemapGrid &grid);
void remap_search_init(RemapMethod mapType, RemapSearch &search, RemapGrid &srcGrid, RemapGrid &tgtGrid);

void remap_search_points(RemapSearch &rsearch, const PointLonLat &pointLL, KnnData &knnData);
int remap_search_square(RemapSearch &rsearch, const PointLonLat &pointLL, SquareCorners &squareCorners);
size_t remap_search_cells(RemapSearch &rsearch, bool isReg2dCell, GridCell &gridCell, Varray<size_t> &searchIndices);

void remap_bilinear_weights(RemapSearch &rsearch, RemapVars &rv);
void remap_bicubic_weights(RemapSearch &rsearch, RemapVars &rv);
void remap_knn_weights(const KnnParams &knnParams, RemapSearch &rsearch, RemapVars &rv);
void remap_conserv_weights(RemapSearch &rsearch, RemapVars &rv);

void remap_bilinear(RemapSearch &rsearch, const Field &field1, Field &field2);
void remap_bicubic(RemapSearch &rsearch, const Field &field1, Field &field2);
void remap_knn(const KnnParams &knnParams, RemapSearch &rsearch, const Field &field1, Field &field2);
void remap_conserv(NormOpt normOpt, RemapSearch &rsearch, const Field &field1, Field &field2);

namespace remap
{

template <typename T>
void gradients(const Varray<T> &array, RemapGrid &grid, const Vmask &mask, RemapGradients &gradients);
void gradients(const Field &field, RemapGrid &grid, RemapGradients &gradients);

void stat(int remapOrder, RemapGrid &srcGrid, RemapGrid &tgtGrid, RemapVars &rv, const Field &field1, const Field &field2);

};  // namespace remap

void remap_write_data_scrip(const std::string &weightsfile, const RemapSwitches &remapSwitches, RemapGrid &srcGrid,
                            RemapGrid &tgtGrid, RemapVars &rv);
RemapSwitches remap_read_data_scrip(const std::string &weightsfile, int gridID1, int gridID2, RemapGrid &srcGrid,
                                    RemapGrid &tgtGrid, RemapVars &rv);

int grid_search_square_reg2d_NN(size_t nx, size_t ny, size_t *nbrIndices, double *nbrDistance, double plat, double plon,
                                const Varray<double> &src_center_lat, const Varray<double> &src_center_lon);

int grid_search_square_reg2d(RemapGrid *srcGrid, SquareCorners &squareCorners, double plat, double plon);

std::pair<double, double> remap_find_weights(const PointLonLat &pointLL, const SquareCorners &squareCorners);

PointLonLat remapgrid_get_lonlat(const RemapGrid *grid, size_t index);

void remap_check_area(size_t grid_size, const Varray<double> &cell_area, const char *name);

template <typename T>
void remap_set_mask(const Varray<T> &v, size_t gridsize, size_t numMissVals, double mv, Vmask &mask);

void remap_set_mask(const Field &field1, size_t gridsize, size_t numMissVals, double missval, Vmask &imask);

#endif /* REMAP_H */
