/*!

  \brief This module provides efficient methods for the fitting and analysis of point-clouds in arbitrary dimensions.
    
 */
namespace Grenaille
{ 
  /*!
    \brief Concepts used in Grenaille
    
    The Grenaille module is structured around five concepts:
    - one defining the structure a sample (PointConcept);
    - two defining how neighbor samples should be weighted (WeightKernelConcept and WeightFuncConcept);
    - two defining how to perform fitting and what to extract from it (FittingProcedureConcept and FittingExtensionConcept).

    In practice, one must first determine which concept implementations they need, and then gather them in what we call a Basket.    
    \code
    typedef 
    Basket <PointImpl,              // Implementation of PointConcept
    
            WeightFuncImpl,         // Implementation of WeightFuncConcept (could use WeightKernelConcept)
            
            FittingProcedureImpl,   // Implementation of FittingProcedureConcept
            
            FittingExtensionImpl1,  // 
            FittingExtensionImpl2,  // Implementations of FittingExtensionConcept
            ... ,                   //
            FittingExtensionImpln   //
            
            > myFit;                // Final structure to fit and extend a primitive over weighted samples
    \endcode

    \see The \ref grenaille_user_manual_page demonstrates step by step how to use a Basket for a simple application.

    \see For more details about each concept and examples of how to use them, browse through the pages below.
  */
  namespace Concept{
    /*!
      \brief Describes the procedure to fit a primitive to point samples.
      
      A typical example use would go like this:
      \code    
        typedef Basket<MyPointStructure,MyWeightingFunction,MyFittingProcedure> Fit;
        MyWeightingFunction w ( some_parameters );

        // Create a fit object      
        Fit fit;
        
        // init the internal state with respect to the reference position
        fit.init( referencePosition );
        
        // set the weighting function. Has no influence of the other internals
        fit.setWeightFunc( w );
        
        foreach neighbors of referencePosition
          fit.addNeighbor(neighbor);
          
        fit.finalize();
        
    if(fit.isStable())
    {
      // use the fit
      // ...
    }      
      \endcode
         
     */
    template < class DataPoint, class _WFunctor, typename T = void >
    class FittingProcedureConcept {
      public:
      /**************************************************************************/
      /* Initialization                                                         */
      /**************************************************************************/
     /*! 
        \brief Init the WeightFunc, without changing the other internal states
        \warning Must be called be for any computation 
      */
      void setWeightFunc (const WFunctor& w){};
      
      /*!
        \brief Set the evaluation position and reset the internal states. 
        \warning Must be called be for any computation
      */
      void init (const VectorType& evalPos){};
      

      /**************************************************************************/
      /* Processing                                                             */
      /**************************************************************************/
      /*!
        \brief Add a neighbor to perform the fit 
        \return false if param nei is not a valid neighbour (weight = 0)
      */
      bool addNeighbor(const DataPoint &nei){};
      
      /*!
        \brief Finalize the fitting procedure
        \return the corresponding state of the fitting
        \warning Must be called be for any use of the fitting output
      */
      FIT_RESULT finalize (){};    
    };
    
    /*!
      \brief Describes how to add features to an existing fitting procedure.
      
      A typical example use would go like this:
      \code
        typedef Basket<MyPointStructure,MyWeightingFunction,MyFittingProcedure, MyExtension1, MyExtension2, ...> ExtendedFit;
        MyWeightingFunction w ( some_parameters );

        // Create a fit object      
        ExtendedFit extFit;
        
        // init the internal state with respect to the reference position
        extFit.init( referencePosition );
        
        // set the weighting function. Has no influence of the other internals
        extFit.setWeightFunc( w );
        
        foreach neighbors of referencePosition
          extFit.addNeighbor(neighbor);
          
        extFit.finalize();
        
        if(extFit.isStable())
        {
          // use the result of the fit and its extensions
          // ... 
        }
      \endcode
      
     */
    template < class DataPoint, class _WFunctor, typename T = void >
    class FittingExtensionConcept {
      public:
      /**************************************************************************/
      /* Initialization                                                         */
      /**************************************************************************/
      /*! \see FittingProcedureConcept::setWeightFunc */
      void setWeightFunc (const WFunctor& w){};
      
      /*! \see FittingProcedureConcept::init */
      void init (const VectorType& evalPos){};
      

      /**************************************************************************/
      /* Processing                                                             */
      /**************************************************************************/
      /*! \see FittingProcedureConcept::addNeighbor */
      bool addNeighbor(const DataPoint &nei){};
      
      /*! \see FittingProcedureConcept::finalize */
      FIRESULT finalize (){};
    };
    
    
    /*!
      \brief A 1D weighting function and its derivatives.
    */
    template <typename _Scalar>
    class WeightKernelConcept{
    public:
      typedef _Scalar Scalar;

      //! \brief Apply the weighting kernel to the scalar value \f$f(x)\f$
      MULTIARCH inline Scalar f  (const Scalar& x) const {}
      //! \brief Apply the first derivative of the weighting kernel to the scalar value \f$f'(x)\f$
      MULTIARCH inline Scalar df (const Scalar& x) const {}
      //! \brief Apply the second derivative of the weighting kernel to the scalar value \f$f''(x)\f$
      MULTIARCH inline Scalar ddf(const Scalar& x) const {}
    };// class WeightKernelConcept
    
    
    /*!
      \brief Applies a Concept::WeightKernelConcept to a Concept::PointConcept query.
    */
    template <class Point>
    class WeightFuncConcept {

    public:        
      typedef typename DataPoint::Scalar Scalar;
      typedef typename DataPoint::VectorType VectorType;
        
      /*! \brief Apply the weight function to a query. */
      MULTIARCH inline Scalar w(const VectorType& relativeQuery, 
              const DataPoint&  attributes) const {}   
         
      /*! \brief Apply the weight function differenciated in space to a query. */
      MULTIARCH inline VectorType spacedw(const VectorType& relativeQuery, 
              const DataPoint&  attributes) const {}   
         
      /*! \brief Apply the weight function differenciated in scale to a query. */
      MULTIARCH inline Scalar scaledw(const VectorType& relativeQuery, 
              const DataPoint&  attributes) const {}

      /*! \brief Read access to the evaluation scale */
      MULTIARCH inline Scalar evalScale() const {}   
    };// class WeightFuncConcept
    
    /*!
      \brief Definition of a point sample.
      
      All fitting methods in Grenaille rely on the definition of a Point type. Specific features might be required depending on the content of the Basket: for instance, an OrientedSphereFit will require points equipped with normals as illustrated in the \ref grenaille_user_manual_page.
      
      Grenaille does not provide an implementation for this concept so that users may adapt it to their own data structure. However, the following implementation can be used as a starting point for most 3D applications:
      \code
      class MyPoint{
      public:
        enum {Dim = 3};
        typedef float Scalar;
        typedef Eigen::Matrix<Scalar, Dim, 1> VectorType;

        MULTIARCH inline MyPoint(const VectorType &pos = VectorType::Zero())
          : _pos(pos) {}
          
        MULTIARCH inline const VectorType& pos() const { return _pos; }  
        MULTIARCH inline       VectorType& pos()       { return _pos; }  

      private:
        VectorType _pos;
      };
      \endcode
     */
    class PointConcept{
      public:
        /*! \brief Defines the ambient space dimension, 3 in this example */
        enum {Dim = 3}; 
        /*! \brief Defines the type used ton encode scalar values */
        typedef float Scalar;
        /*! \brief Defines type used ton encode vector values */
        typedef Eigen::Matrix<Scalar, Dim, 1> VectorType;

        /*! \brief Default constructor */
        MULTIARCH inline PointConcept(){}
          
        /*! \brief Read access to the position property */
        MULTIARCH inline const VectorType& pos() const {}  
        /*! \brief Write access to the position property */
        MULTIARCH inline       VectorType& pos()       {}  
      }; //class PointConcept
  
  } // End namespace Concept
} // End namespace Grenaille
