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

  Copyright (C) 2003-2020 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_POINT_SEARCH_H
#define GRID_POINT_SEARCH_H

#include <cstddef>
#include "knn_weights.h"
#include "array.h"

#define GPS_NOT_FOUND SIZE_MAX

constexpr double
square(const double x) noexcept
{
  return x * x;
}

constexpr double
squareDistance(const double *a, const double *b) noexcept
{
  return square(a[0] - b[0]) + square(a[1] - b[1]) + square(a[2] - b[2]);
}

enum class PointSearchMethod
{
  full,
  nanoflann,
  kdtree,
  spherepart,
  latbins
};

struct GridPointSearch
{
  bool in_use = false;
  bool extrapolate = false;
  bool is_cyclic = false;
  bool is_reg2d = false;
  bool is_curve = false;
  PointSearchMethod method = PointSearchMethod::full;
  size_t n = 0;
  size_t dims[2] = { 0 };

  void *search_container = nullptr;
  double searchRadius = 0;

  // reg2d search
  Varray<double> reg2d_center_lon, reg2d_center_lat;
  Varray<double> coslat, sinlat;  // cosine, sine of grid lats (for distance)
  Varray<double> coslon, sinlon;  // cosine, sine of grid lons (for distance)

  const double *plons = nullptr, *plats = nullptr;

  double lonmin = 0, lonmax = 0, latmin = 0, latmax = 0;
  float min[3] = { 0 }, max[3] = { 0 };
  void *pointcloud = nullptr;

  double (*coordinates_xyz)[3];
};

void gridSearchPoint(GridPointSearch &gps, double plon, double plat, knnWeightsType &knnWeights);
void gridSearchPointSmooth(GridPointSearch &gps, double plon, double plat, knnWeightsType &knnWeights);

void gridPointSearchCreateReg2d(GridPointSearch &gps, bool xIsCyclic, size_t dims[2], const Varray<double> &lons, const Varray<double> &lats);
void gridPointSearchCreate(GridPointSearch &gps, bool xIsCyclic, size_t dims[2], size_t n, const Varray<double> &lons, const Varray<double> &lats);
void gridPointSearchDelete(GridPointSearch &gps);
size_t gridPointSearchNearest(GridPointSearch &gps, double lon, double lat, size_t *addr, double *dist);
size_t gridPointSearchQnearest(GridPointSearch &gps, double lon, double lat, size_t nnn, size_t *adds, double *dist);
void gridPointSearchExtrapolate(GridPointSearch &gps);

#endif
