#pragma once

#include <string>
#include <vector>
#include "nutil.h"
#include "ndata.h"
#include <nanoflann.hpp>



struct PointCloud
{
    stlmesh *mesh;
    using coord_t = double;  //!< The type of each coordinate

    // Must return the number of data points
    inline size_t kdtree_get_point_count() const { return mesh->vertices.size(); }

    // Returns the dim'th component of the idx'th point in the class:
    // Since this is inlined and the "dim" argument is typically an immediate
    // value, the
    //  "if/else's" are actually solved at compile time.
    inline double kdtree_get_pt(const size_t idx, const size_t dim) const
    {
      return mesh->vertices[idx].first[dim];
    }

    // Optional bounding-box computation: return false to default to a standard
    // bbox computation loop.
    //   Return true if the BBOX was already computed by the class and returned
    //   in "bb" so it can be avoided to redo it again. Look at bb.size() to
    //   find out the expected dimensionality (e.g. 2 or 3 for point clouds)
    template <class BBOX>
    bool kdtree_get_bbox(BBOX& /* bb */) const
    {
        return false;
    }
};



class stlmesh_nn : public stlmesh
{
public :
    using my_kd_tree_t=nanoflann::KDTreeSingleIndexAdaptor<
        nanoflann::L2_Simple_Adaptor<double, PointCloud>,
        PointCloud, 3 /* dim */
        > ;
    PointCloud ANN_search;
    my_kd_tree_t *kd_tree;

    stlmesh_nn() {ANN_search.mesh=this;kd_tree=NULL;}
    ~stlmesh_nn() {if (kd_tree) delete kd_tree;kd_tree=NULL;}
    void build_kdtree()
    {
      if (kd_tree) delete kd_tree;
      kd_tree=new my_kd_tree_t(3,ANN_search,{10});
    }
    int search(npoint3 pt,int nb,unsigned int tab[],double dist[])
    {
        const size_t                   num_results = nb;
//        size_t                         ret_index;
//        double                          out_dist_sqr;
//       nanoflann::KNNResultSet<double> resultSet(num_results);
//        resultSet.init(&ret_index, &out_dist_sqr);
//        resultSet.init(tab, dist);
        if (kd_tree)
            return kd_tree->knnSearch(pt.array(), num_results, tab, dist);
        else return 0;
//        kd_tree->findNeighbors(resultSet, pt.array(), nanoflann::SearchParams(10));
//        return 0;
//        return ret_index;
    }
    virtual void clear(void)
    {
        stlmesh::clear();
        delete kd_tree;
        kd_tree=NULL;
    }
};

enum SBFileType {SBnative, SBimetric, SBother };

//fonction de chargement du maillage STL
void loadmesh(std::string filename,stlmesh_nn &mesh);

//fonction de chargement des donnees scanbodies (tous formats)
void loadSB(std::string filename, std::vector<std::pair<npoint3,npoint3> >& sb);

// test du format de fichier : natif ou autre.
//SBFileType checkSBFormat(std::string filename);

//fonction de chargement des donnees scanbodies
//void loadSBNative(std::string filename, std::vector<std::pair<npoint3,npoint3> >& sb);

//fonction de chargement des donnees scanbodies (imetric file)
//void loadSBimetric(std::string filename, std::vector<std::pair<npoint3,npoint3> >& sb);

void saveSB(std::string filename, std::vector<std::pair<npoint3,npoint3> >& sb);

void loadPoints(std::string filename, std::vector<std::pair<npoint3,npoint3> >& sb,std::vector<npoint3> &pts,double sbh=0.0);
