// Cutmesh - Copyright (C) 2010-2018 T. Mouton, E. Bechet 
//
// See the LICENSE file for license information and contributions.
// bugs and problems to <thibaud.mouton@gmail.com>.

#ifndef _DISCRETESURFACE_CUTMESH_H_
#define _DISCRETESURFACE_CUTMESH_H_

#include "GModel.h"
#include "LVertex.h"

#if defined(HAVE_ANN)
#include "ANN/ANN.h"
#else
class Bucket;
#endif

class DiscreteSurface
{
  private:
    double _bbmin[3];
    double _bbmax[3];
    LVertex *_center;
    GFace *_face;
    double _minx;
    double _maxx;
    double _miny;
    double _maxy;
    double _lx;
    double _ly;
    double _enx;
    double _eny;
    int _id;
    int _size;
    int _discrete;

    std::vector<LVertex*> _pt;
    std::vector<LVertex*> _n;
    std::vector<double> _curv;

    void *_implicit_surf;

#if defined(HAVE_ANN)
    ANNkd_tree *_kdtree;
#else
    Bucket *_bs;
#endif
  public:
    DiscreteSurface ( GFace *face, int id, double tminx, double tmaxx, double tminy, double tmaxy, int max_step );
    DiscreteSurface ( int id, double tminx, double tmaxx, double tminy, double tmaxy, int max_step );
    DiscreteSurface ( int id, void *implicit_surf );
    DiscreteSurface ( FILE* fp );
    ~DiscreteSurface();
    void generate ( /*double longuest_mesh_edge*/ );
    void bucketize ( double longuest_mesh_edge );
    void distance ( LVertex* v, double &simpledist, double &projdist );
    void add_point ( LVertex* p, LVertex* n, double curv )
    {
      assert ( p != NULL );
      assert ( n != NULL );
//     p->setSurf(_id);

      if ( _pt.size() == 0 )
      {
        _bbmin[0] = p->x();
        _bbmin[1] = p->y();
        _bbmin[2] = p->z();
        _bbmax[0] = p->x();
        _bbmax[1] = p->y();
        _bbmax[2] = p->z();
      }
      else
      {
        if ( _bbmin[0] > p->x() )
          _bbmin[0] = p->x();
        if ( _bbmin[1] > p->y() )
          _bbmin[1] = p->y();
        if ( _bbmin[2] > p->z() )
          _bbmin[2] = p->z();
        if ( _bbmax[0] < p->x() )
          _bbmax[0] = p->x();
        if ( _bbmax[1] < p->y() )
          _bbmax[1] = p->y();
        if ( _bbmax[2] < p->z() )
          _bbmax[2] = p->z();
      }
      p->setIndex ( size() );
      n->setIndex ( size() );
//     n->setIndex(size());
      _pt.push_back ( p );
      _n.push_back ( n );
      _curv.push_back ( curv );
    }

    inline void get_bbox ( double *min, double *max )
    {
      min[0] = _bbmin[0];
      min[1] = _bbmin[1];
      min[2] = _bbmin[2];
      max[0] = _bbmax[0];
      max[1] = _bbmax[1];
      max[2] = _bbmax[2];
    }

    inline LVertex *get_center_box ()
    {
      if ( _center == NULL )
        _center = new LVertex ( ( _bbmax[0]+_bbmin[0] ) /2. , ( _bbmax[1]+_bbmin[1] ) /2. , ( _bbmax[2]+_bbmin[2] ) /2. );

      return _center;
    }

    inline int bbox_contain ( LVertex *pt )
    {
      if ( !_discrete )
        return 1;

      if ( pt->x() < _bbmin[0] || pt->x() > _bbmax[0] )
        return 0;
      if ( pt->y() < _bbmin[1] || pt->y() > _bbmax[1] )
        return 0;
      if ( pt->z() < _bbmin[2] || pt->z() > _bbmax[2] )
        return 0;
      return 1;
    }

    inline void get_point ( int i, LVertex **p, LVertex **n, double *c )
    {
      *p = _pt[i];
      *n = _n[i];
      *c = _curv[i];
    }
    inline int size()
    {
      return _pt.size();
    }

    inline int get_id()
    {
      return _id;
    }

    inline double u ( double t )
    {
      return ( _minx-_enx ) +t*_lx;
    }

    inline double v ( double t )
    {
      return ( _miny-_eny ) +t*_ly;
    }

    void point ( double u, double v, LVertex *pt, LVertex *n, double &curv );
    void get_closest ( LVertex *pt, LVertex **cl, LVertex **cln );
    void dump ( int num );
};

#endif
