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

  Author: Uwe Schulzweida

*/

#ifndef POINTSEARCH_FULL_H
#define POINTSEARCH_FULL_H

#include "pointsearch_unstruct.h"
#include "pointsearch_utils.h"
#include "cdo_omp.h"
#include "cdo_math.h"
#include "cdo_options.h"
#include "varray.h"
#include "grid_convert.h"
#include "kdtreelib/kdtree.h"

class PointsearchFull : public PointsearchStrategy
{
public:
  PointsearchFull(const Varray<double> &lons, const Varray<double> &lats, const PointsearchParams &params) : m_params{ params }
  {
    create(lons, lats);
  }
  ~PointsearchFull()
  {
    if (m_pointsXYZ) delete[] m_pointsXYZ;
  }

  size_t
  search_nearest(const PointLonLat &pointLL, size_t *index, double *dist) override
  {
    if (m_pointsXYZ == nullptr) return 0;

    auto sqrDistMax = cdo::sqr(m_params.searchRadius);

    double tgtPoint[3];
    gcLLtoXYZ(pointLL.get_lon(), pointLL.get_lat(), tgtPoint);

    auto closestPoint = m_n;
    double sqrDist = FLT_MAX;
    for (size_t i = 0; i < m_n; ++i)
      {
        double d = (float) cdo::sqr_distance(tgtPoint, m_pointsXYZ[i]);
        if (closestPoint >= m_n || d < sqrDist || (d <= sqrDist && i < closestPoint))
          {
            sqrDist = d;
            closestPoint = i;
          }
      }

    if (closestPoint < m_n && sqrDist < sqrDistMax)
      {
        *index = closestPoint;
        *dist = std::sqrt(sqrDist);
        return 1;
      }

    return 0;
  }

  size_t
  search_qnearest(const PointLonLat &pointLL, size_t nnn, size_t *indices, double *dist) override
  {
    (void) pointLL;
    (void) nnn;
    (void) indices;
    (void) dist;

    static auto warning{ true };
    if (warning)
      {
        warning = false;
        fprintf(stderr, "PointsearchFull::search_qnearest() not implemented\n");
      }

    size_t numIndices = 0;

    if (m_pointsXYZ == nullptr) return numIndices;

    return numIndices;
  }

private:
  size_t m_n{ 0 };
  double (*m_pointsXYZ)[3]{ nullptr };
  const PointsearchParams &m_params;

  void
  create(const Varray<double> &lons, const Varray<double> &lats)
  {
    auto n = lons.size();
    m_pointsXYZ = new double[n][3];

#ifdef HAVE_OPENMP4
#pragma omp simd
#endif
    for (size_t i = 0; i < n; ++i) { gcLLtoXYZ(lons[i], lats[i], m_pointsXYZ[i]); }

    m_n = n;
  }
};

#endif
