/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* 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; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/


#ifndef __VCG_TRIMESH_CLOSEST
#define __VCG_TRIMESH_CLOSEST
#include <math.h>

#include <vcg/space/point3.h>
#include <vcg/space/box3.h>
#include <vcg/space/point4.h>
#include <vcg/math/base.h>
#include <vcg/simplex/face/distance.h>
#include <vcg/simplex/vertex/distance.h>
#include <vcg/space/intersection3.h>
#include <vcg/space/index/space_iterators.h>
#include <vcg/complex/complex.h>

namespace vcg {
    namespace tri {

        //**MARKER CLASSES**//
        template <class MESH_TYPE,class OBJ_TYPE>
        class Tmark
        {
            MESH_TYPE *m;
        public:
            Tmark(){}
            Tmark(	MESH_TYPE *m) {SetMesh(m);}
            void UnMarkAll(){ vcg::tri::UnMarkAll(*m);}
            bool IsMarked(OBJ_TYPE* obj){return (vcg::tri::IsMarked(*m,obj));}
            void Mark(OBJ_TYPE* obj){ vcg::tri::Mark(*m,obj);}
            void SetMesh(MESH_TYPE *_m)
            {
              tri::RequirePerFaceMark(*_m);
              m=_m;
            }
        };

        template <class MESH_TYPE>
        class FaceTmark:public Tmark<MESH_TYPE,typename MESH_TYPE::FaceType>
        {
        public:
            FaceTmark(){}
            FaceTmark(MESH_TYPE *m) {this->SetMesh(m);}
        };

        template <class MESH_TYPE>
        class VertTmark
        {
        public:
        typedef typename  MESH_TYPE::VertexType VertexType;
            inline VertTmark(){}
            inline VertTmark(MESH_TYPE *){}
            inline void UnMarkAll() const {}
            inline bool IsMarked(VertexType*) const { return false; }
            inline void Mark(VertexType*) const {}
            inline void SetMesh(void * /*m=0*/) const {}
        };

        //**CLOSEST FUNCTION DEFINITION**//

        /*

        aka MetroCore
        data una mesh m e una ug sulle sue facce trova il punto di m piu' vicino ad
        un punto dato.
        */

        // input: mesh, punto, griglia (gr), distanza limite (mdist)
        // output: normale (interpolata) alla faccia e punto piu' vicino su di essa, e coord baricentriche del punto trovato

        // Nota che il parametro template GRID non ci dovrebbe essere, visto che deve essere
        // UGrid<MESH::FaceContainer >, ma non sono riuscito a definirlo implicitamente

        template <class MESH, class GRID>
            typename MESH::FaceType * GetClosestFaceEP( MESH & mesh, GRID & gr, const typename GRID::CoordType & _p,
                                                        const typename GRID::ScalarType & _maxDist, typename GRID::ScalarType & _minDist,
                                                        typename GRID::CoordType & _closestPt, typename GRID::CoordType & _normf,
                                                        typename GRID::CoordType & _ip)
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef Point3<ScalarType> Point3x;

            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf(&mesh);
            vcg::face::PointDistanceEPFunctor<ScalarType> FDistFunct;
            _minDist=_maxDist;
            typename MESH::FaceType* bestf= gr.GetClosest(FDistFunct, mf, _p, _maxDist, _minDist, _closestPt);

            if(_maxDist> ScalarType(fabs(_minDist)))
            {
                // f=bestf;
                //calcolo normale con interpolazione trilineare
              InterpolationParameters<typename MESH::FaceType,typename MESH::ScalarType>(*bestf,bestf->N(),_closestPt, _ip);
              _normf =  (bestf->V(0)->cN())*_ip[0]+
                  (bestf->V(1)->cN())*_ip[1]+
                  (bestf->V(2)->cN())*_ip[2] ;

              _minDist = fabs(_minDist);
                return(bestf);
            }
            return (0);
        }

        template <class MESH, class GRID>
            typename MESH::FaceType * GetClosestFaceBase( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p,
                                                          const typename GRID::ScalarType _maxDist,typename GRID::ScalarType & _minDist,
                                                          typename GRID::CoordType &_closestPt)
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef Point3<ScalarType> Point3x;
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf;
            mf.SetMesh(&mesh);
            vcg::face::PointDistanceBaseFunctor<ScalarType> PDistFunct;
            _minDist=_maxDist;
            return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt));
        }

        template <class MESH, class GRID>
            typename MESH::FaceType * GetClosestFaceEP( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p,
            const typename GRID::ScalarType _maxDist, typename GRID::ScalarType & _minDist,
            typename GRID::CoordType &_closestPt)
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef Point3<ScalarType> Point3x;
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf;
            mf.SetMesh(&mesh);
            vcg::face::PointDistanceEPFunctor<ScalarType> PDistFunct;
            _minDist=_maxDist;
            return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt));
        }

        template <class MESH, class GRID>
        typename MESH::FaceType * GetClosestFaceNormal(MESH & mesh,GRID & gr,const typename MESH::VertexType & _p,
            const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist,
            typename GRID::CoordType &_closestPt)
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf;
            mf.SetMesh(&mesh);
            typedef vcg::face::PointNormalDistanceFunctor<typename MESH::VertexType> PDistFunct;
            PDistFunct fn;
            _minDist=_maxDist;
            //return (gr.GetClosest(PDistFunct,mf,_p,_maxDist,_minDist,_closestPt.P()));
            return (gr.template GetClosest <PDistFunct,MarkerFace>(fn,mf,_p,_maxDist,_minDist,_closestPt));
        }

        template <class MESH, class GRID>
            typename MESH::VertexType * GetClosestVertex( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p,
            const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist )
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef Point3<ScalarType> Point3x;
            typedef VertTmark<MESH> MarkerVert;
            MarkerVert mv;
            mv.SetMesh(&mesh);
            typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
            VDistFunct fn;
            _minDist=_maxDist;
            Point3x _closestPt;
            return (gr.template GetClosest<VDistFunct,MarkerVert>(fn,mv,_p,_maxDist,_minDist,_closestPt));
        }

        template <class MESH, class GRID>
            typename MESH::VertexType * GetClosestVertexScale( MESH & mesh,GRID & gr,const typename GRID::CoordType & _p,
            const typename MESH::CoordType & center, const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist )
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef Point3<ScalarType> Point3x;
            typedef VertTmark<MESH> MarkerVert;
            MarkerVert mv;
            mv.SetMesh(&mesh);
            typedef vcg::vertex::PointScaledDistanceFunctor<typename MESH::VertexType> VDistFunct;
            VDistFunct fn;
            fn.Cen() = center;
            _minDist=_maxDist;
            Point3x _closestPt;
            return (gr.template GetClosest<VDistFunct,MarkerVert>(fn,mv,_p,_maxDist,_minDist,_closestPt));
        }

        template <class MESH, class GRID>
            typename MESH::VertexType * GetClosestVertexNormal( MESH & mesh,GRID & gr,const typename MESH::VertexType & _p,
            const typename GRID::ScalarType & _maxDist,typename GRID::ScalarType & _minDist )
        {
            typedef typename GRID::ScalarType ScalarType;
            typedef Point3<ScalarType> Point3x;
            typedef VertTmark<MESH> MarkerVert;
            MarkerVert mv;
            mv.SetMesh(&mesh);
            typedef vcg::vertex::PointNormalDistanceFunctor<typename MESH::VertexType> VDistFunct;
            VDistFunct fn;
            _minDist=_maxDist;
            Point3x _closestPt;
            return (gr.template GetClosest <VDistFunct,MarkerVert>(fn,mv,_p,_maxDist,_minDist,_closestPt));
        }

    template <class MESH, class GRID, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
      unsigned int GetKClosestFaceEP(MESH & mesh,GRID & gr, const unsigned int _k,
      const typename GRID::CoordType & _p, const typename GRID::ScalarType & _maxDist,
      OBJPTRCONTAINER & _objectPtrs,DISTCONTAINER & _distances, POINTCONTAINER & _points)
    {
      typedef FaceTmark<MESH> MarkerFace;
      MarkerFace mf;
      mf.SetMesh(&mesh);
      vcg::face::PointDistanceEPFunctor<typename MESH::ScalarType> FDistFunct;
      return (gr.GetKClosest /*<vcg::face::PointDistanceFunctor, MarkerFace,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
        (FDistFunct,mf,_k,_p,_maxDist,_objectPtrs,_distances,_points));
    }

    // This version does not require that the face type has the
    // EdgePlane component and use a less optimized (but more memory efficient) point-triangle distance
    template <class MESH, class GRID, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
      unsigned int GetKClosestFaceBase(MESH & mesh,GRID & gr, const unsigned int _k,
      const typename GRID::CoordType & _p, const typename GRID::ScalarType & _maxDist,
      OBJPTRCONTAINER & _objectPtrs,DISTCONTAINER & _distances, POINTCONTAINER & _points)
    {
      typedef FaceTmark<MESH> MarkerFace;
      MarkerFace mf;
      mf.SetMesh(&mesh);
      vcg::face::PointDistanceBaseFunctor<typename MESH::ScalarType> FDistFunct;
      return (gr.GetKClosest /*<vcg::face::PointDistanceFunctor, MarkerFace,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
        (FDistFunct,mf,_k,_p,_maxDist,_objectPtrs,_distances,_points));
    }

        template <class MESH, class GRID, class OBJPTRCONTAINER,class DISTCONTAINER, class POINTCONTAINER>
            unsigned int GetKClosestVertex(MESH & mesh,GRID & gr, const unsigned int _k,
            const typename GRID::CoordType & _p, const typename GRID::ScalarType & _maxDist,
            OBJPTRCONTAINER & _objectPtrs,DISTCONTAINER & _distances, POINTCONTAINER & _points)
        {
            typedef VertTmark<MESH> MarkerVert;
            MarkerVert mv;
            mv.SetMesh(&mesh);
            typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
            VDistFunct distFunct;
            return (gr.GetKClosest/* <VDistFunct,MarkerVert,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
                (distFunct,mv,_k,_p,_maxDist,_objectPtrs,_distances,_points));
        }

        template <class MESH, class GRID, class OBJPTRCONTAINER, class DISTCONTAINER, class POINTCONTAINER>
            unsigned int GetInSphereFaceBase(MESH & mesh,
            GRID & gr,
            const typename GRID::CoordType & _p,
            const typename GRID::ScalarType & _r,
            OBJPTRCONTAINER & _objectPtrs,
            DISTCONTAINER & _distances,
            POINTCONTAINER & _points)
        {
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf;
            mf.SetMesh(&mesh);
            typedef vcg::face::PointDistanceBaseFunctor<typename MESH::ScalarType> FDistFunct;
            return (gr.GetInSphere/*<FDistFunct,MarkerFace,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
                (FDistFunct(),mf,_p,_r,_objectPtrs,_distances,_points));
        }

        template <class MESH, class GRID, class OBJPTRCONTAINER, class DISTCONTAINER, class POINTCONTAINER>
            unsigned int GetInSphereVertex(MESH & mesh,
            GRID & gr,
            const typename GRID::CoordType & _p,
            const typename GRID::ScalarType & _r,
            OBJPTRCONTAINER & _objectPtrs,
            DISTCONTAINER & _distances,
            POINTCONTAINER & _points)
        {
            typedef VertTmark<MESH> MarkerVert;
            MarkerVert mv;
            mv.SetMesh(&mesh);
            typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
            VDistFunct fn;
            return (gr.GetInSphere/*<VDistFunct,MarkerVert,OBJPTRCONTAINER,DISTCONTAINER,POINTCONTAINER>*/
                (fn, mv,_p,_r,_objectPtrs,_distances,_points));
        }

        template <class MESH, class GRID, class OBJPTRCONTAINER>
            unsigned int GetInBoxFace(MESH & mesh,
            GRID & gr,
            const vcg::Box3<typename GRID::ScalarType> _bbox,
            OBJPTRCONTAINER & _objectPtrs)
        {
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf(&mesh);
            return(gr.GetInBox/*<MarkerFace,OBJPTRCONTAINER>*/(mf,_bbox,_objectPtrs));
        }

        template <class MESH, class GRID, class OBJPTRCONTAINER>
            unsigned int GetInBoxVertex(MESH & mesh,
            GRID & gr,
            const vcg::Box3<typename GRID::ScalarType> _bbox,
            OBJPTRCONTAINER & _objectPtrs)
        {
            typedef VertTmark<MESH> MarkerVert;
            MarkerVert mv;
            mv.SetMesh(&mesh);
            return(gr.GetInBox/*<MarkerVert,OBJPTRCONTAINER>*/(mv,_bbox,_objectPtrs));
        }

        template <class MESH, class GRID>
            typename GRID::ObjPtr DoRay(MESH & mesh,GRID & gr, const Ray3<typename GRID::ScalarType> & _ray,
            const typename GRID::ScalarType & _maxDist, typename GRID::ScalarType & _t)
        {
            typedef typename MESH::FaceType FaceType;
            typedef typename MESH::ScalarType ScalarType;
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf;
            mf.SetMesh(&mesh);
            Ray3<typename GRID::ScalarType> _ray1=_ray;
            _ray1.Normalize();
            typedef vcg::RayTriangleIntersectionFunctor<true> FintFunct;
      FintFunct ff;
      return(gr.DoRay(ff,mf,_ray1,_maxDist,_t));
        }

        template <class MESH, class GRID>
            typename GRID::ObjPtr DoRay(MESH & mesh,GRID & gr, const Ray3<typename GRID::ScalarType> & _ray,
                                        const typename GRID::ScalarType & _maxDist,
                                        typename GRID::ScalarType & _t,
                                        typename GRID::CoordType & _normf)
        {
            typedef typename MESH::FaceType FaceType;
            typedef typename MESH::ScalarType ScalarType;
            typedef FaceTmark<MESH> MarkerFace;
            MarkerFace mf;
            mf.SetMesh(&mesh);
            typedef vcg::RayTriangleIntersectionFunctor<true> FintFunct;
            FintFunct fintfunct;
            Ray3<typename GRID::ScalarType> _ray1=_ray;
            _ray1.Normalize();
            FaceType *f=gr.DoRay(fintfunct,mf,_ray1,_maxDist,_t);
            typename GRID::CoordType dir=_ray.Direction();
            dir.Normalize();
            typename GRID::CoordType int_point=_ray.Origin()+_ray1.Direction()*_t;
            if (f!=NULL)
            {
                Point3<typename GRID::ScalarType> bary;
                InterpolationParameters<FaceType,ScalarType>(*f,f->N(),int_point, bary);

                _normf =  (f->V(0)->cN())*bary.X()+
                                    (f->V(1)->cN())*bary.Y()+
                                    (f->V(2)->cN())*bary.Z() ;
            }
            return f;
        }

        ///Iteratively Do Ray sampling on spherical coordinates
        ///sampling along the two angles
        template <class MESH, class GRID, class OBJPTRCONTAINER, class COORDCONTAINER>
            void RaySpherical(MESH & mesh,GRID & gr, const Ray3<typename GRID::ScalarType> & _ray,
                                               const typename GRID::ScalarType & _maxDist,
                                               const typename GRID::ScalarType & _theta_interval,
                                               const typename GRID::ScalarType & _phi_interval,
                                               const int &n_samples,
                                               OBJPTRCONTAINER & _objectPtrs,
                                               COORDCONTAINER & _pos,
                                                 COORDCONTAINER & _norm)
        {
            typedef typename MESH::FaceType FaceType;
            typedef typename MESH::ScalarType ScalarType;
            ScalarType delta_theta=_theta_interval/(ScalarType)(n_samples*2);
      ScalarType delta_phi  =_phi_interval/(ScalarType)(n_samples*2);
      ScalarType theta_init,phi_init,ro;
      typename GRID::CoordType dir0=_ray.Direction();
      dir0.ToPolarRad(ro,theta_init,phi_init);
            for (int x=-n_samples;x<=n_samples;x++)
                for (int y=-n_samples;y<=n_samples;y++)
                {
                    ScalarType theta=theta_init+x*delta_theta;
          if (theta<0) theta=2.0*M_PI-theta;
          ScalarType phi=phi_init+y*delta_phi;

          typename GRID::CoordType dir;
          dir.FromxPolar(ro,theta,phi);
                    dir.Normalize();
                    Ray3<typename GRID::ScalarType> curr_ray(_ray.Origin(),dir);
          typename GRID::ScalarType _t;
          typename GRID::ObjPtr f=NULL;
                    f=DoRay(mesh,gr,curr_ray,_maxDist,_t);
                    if (f!=NULL)
                    {
            typename GRID::CoordType pos=curr_ray.Origin()+curr_ray.Direction()*_t;
                        _objectPtrs.push_back(f);
                        _pos.push_back(pos);

                        ///find the normal
                        typename GRID::ScalarType alfa,beta,gamma;
                        InterpolationParameters<FaceType,ScalarType>(*f,*f.N(),pos, alfa, beta, gamma);
                        typename GRID::CoordType norm =  (f->V(0)->cN())*alfa+
                                                                                         (f->V(1)->cN())*beta+
                                                                                         (f->V(2)->cN())*gamma ;
                        _norm.push_back(norm);
                    }
                }
            }

        //**ITERATORS DEFINITION**//

        template <class GRID,class MESH>
        class ClosestFaceEPIterator:public vcg::ClosestIterator<GRID,
            vcg::face::PointDistanceEPFunctor<typename MESH::ScalarType>, FaceTmark<MESH> >
        {
        public:
            typedef GRID GridType;
            typedef MESH MeshType;
            typedef FaceTmark<MESH> MarkerFace;
            typedef vcg::face::PointDistanceEPFunctor<typename MESH::ScalarType> PDistFunct;
            typedef vcg::ClosestIterator<GRID,PDistFunct, FaceTmark<MESH> > ClosestBaseType;
            typedef typename MESH::FaceType FaceType;
            typedef typename MESH::ScalarType ScalarType;

            //ClosestFaceIterator(GridType &_Si):ClosestBaseType(_Si,PDistFunct<FaceType,ScalarType>()){}
            ClosestFaceEPIterator(GridType &_Si):ClosestBaseType(_Si,PDistFunct()){}

//    Commented out: it seems unuseful and make gcc complain. p.
      void SetMesh(MeshType *m)
            {this->tm.SetMesh(m);}
        };

        template <class GRID,class MESH>
        class ClosestVertexIterator:public vcg::ClosestIterator<GRID, vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType>, VertTmark<MESH> >
        {
        public:
            typedef GRID GridType;
            typedef MESH MeshType;
            typedef VertTmark<MESH> MarkerVert;
            typedef vcg::vertex::PointDistanceFunctor<typename MESH::ScalarType> VDistFunct;
            typedef vcg::ClosestIterator<GRID, VDistFunct, VertTmark<MESH> > ClosestBaseType;
            VDistFunct fn;
            ClosestVertexIterator(GridType &_Si):ClosestBaseType(_Si,fn){}

//    Commented out: it seems unuseful and make gcc complain. p.
            void SetMesh(MeshType *m)
            {this->tm.SetMesh(m);}
        };

        template <class GRID,class MESH>
        class TriRayIterator:public vcg::RayIterator<GRID,vcg::RayTriangleIntersectionFunctor<true>,FaceTmark<MESH> >
        {
        public:
            typedef GRID GridType;
            typedef MESH MeshType;
            typedef FaceTmark<MESH> MarkerFace;
            typedef vcg::RayTriangleIntersectionFunctor<true> FintFunct;
            typedef vcg::RayIterator<GRID,FintFunct, FaceTmark<MESH> > RayBaseType;
            typedef typename MESH::FaceType FaceType;
            typedef typename MESH::ScalarType ScalarType;

            TriRayIterator(GridType &_Si,const ScalarType &max_d):RayBaseType(_Si,FintFunct(),max_d){}

//    Commented out: it  seems unuseful and make gcc complain. p.
            void SetMesh(MeshType *m)
            {this->tm.SetMesh(m);}

        };

    }	 // end namespace tri
}	 // end namespace vcg

#endif