/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 3 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
  The filter routines for splines and omoms is based on code by
  Philippe Thevenaz http://bigwww.epfl.ch/thevenaz/interpolation/
  see also:

  [1] M. Unser,
 	"Splines: A Perfect Fit for Signal and Image Processing,"
 	IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38,
 	November 1999.
  [2] M. Unser, A. Aldroubi and M. Eden,
 	"B-Spline Signal Processing: Part I--Theory,"
 	IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 821-832,
 	February 1993.
  [3]  M. Unser, A. Aldroubi and M. Eden,
 	"B-Spline Signal Processing: Part II--Efficient Design and Applications,"
 	IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 834-848,
 	February 1993.

*/

#ifndef mia_1d_interpolator_hh
#define mia_1d_interpolator_hh


#include <vector>

#include <mia/core/defines.hh>
#include <mia/core/splinekernel.hh>
#include <mia/core/boundary_conditions.hh>


NS_MIA_BEGIN

/**
   \ingroup interpol
   \brief Basic Interpolator type for 1D Data.

   \remark Why do we need this?
*/

class EXPORT_CORE C1DInterpolator
{
public:
       /** a virtual destructor is neccessary for some of the interpolators */
       virtual  ~C1DInterpolator();
};


/**
   \ingroup interpol

   \brief Interpolator base class providing the full interface

   Basic Interpolator type for 1D Data.
   \tparam T data type to be interpolated over
 */

template <typename T>
class  EXPORT_CORE T1DInterpolator : public  C1DInterpolator
{
public:

       /**
          \param x location of data value to read
          \returns interpolated value at location x
        */
       virtual T operator () (const double& x) const = 0;

       /**
          interface to evaluate the derivative of the spline defined function
          @param x
          @returns interpolated approximate derivative at x
        */
       virtual typename coeff_map<T>::coeff_type derivative_at (const double& x) const = 0;


};

/**
   \ingroup interpol

    \brief Interpolator that uses some kind of spaciel kernel.

    Base type for interpolators that work with some kind of convolution
    \remark currently all interpolators are like this ...
*/
template <class T>
class EXPORT_CORE T1DConvoluteInterpolator: public T1DInterpolator<T>
{
public:
       /**
          Construtor to prefilter the input for proper interpolation
          \param data the data used for interpolation
          \param kernel the spline kernel used for interpolation
          \param boundary_conditions boundary conditions to be applied when interpolating
        */

       T1DConvoluteInterpolator(const std::vector<T>& data, PSplineKernel kernel,
                                const CSplineBoundaryCondition& boundary_conditions);

       ~T1DConvoluteInterpolator();

       /**
          The actual interpolation operator
          \param x location to interpolate at
          \returns the interpolated value
        */
       T  operator () (const double& x) const;

       /**
          The interpolation funtion for the first order derivative
          \param x location to interpolate at
          \returns the interpolated derivative value
        */
       virtual typename coeff_map<T>::coeff_type derivative_at (const double& x) const;

protected:
       /// Type of the coefficients after filtering
       typedef std::vector< typename coeff_map< T >::coeff_type > TCoeff1D;

       /// vector to hold coefficients
       typedef TCoeff1D coeff_vector;
private:

       TCoeff1D m_coeff;
       PSplineKernel m_kernel;
       PSplineBoundaryCondition m_boundary_conditions;
       T m_min;
       T m_max;

       // not thread save!!!
       mutable CSplineKernel::VIndex m_x_index;
       mutable CSplineKernel::VWeight m_x_weight;
};


/**
   \ingroup interpol
   \brief Factory class for 1D interpolators

   Factory to create 1D interpolators of a give data type using the given input data.
*/
class EXPORT_CORE C1DInterpolatorFactory
{
public:

       /** Initialize the factory according B-Spline kernel and a boundary condition
           @param kernel
           @param bc
        */
       C1DInterpolatorFactory(PSplineKernel kernel, const CSplineBoundaryCondition& bc);

       C1DInterpolatorFactory(const std::string& kernel_descr, const std::string& boundary_descr);

       /// Copy constructor
       C1DInterpolatorFactory(const C1DInterpolatorFactory& o);

       /// assignment operator
       C1DInterpolatorFactory& operator = ( const C1DInterpolatorFactory& o);

       virtual ~C1DInterpolatorFactory();

       /**
          Create a 1D interpolator from a set of sampes that
          returns the same values as the original at grid points
          @tparam data type to be interpolated
          @param src input data
          @returns the interpolator
        */
       template <class T>
       T1DInterpolator<T> *create(const std::vector<T>& src) const
       __attribute__ ((warn_unused_result));

       /// @returns the B-spline kernel
       PSplineKernel get_kernel() const;

private:
       PSplineKernel m_kernel;
       PSplineBoundaryCondition m_bc;
};

/**
   \ingroup interpol
    Pointer type for C1DInterpolatorFactory.
 */
typedef std::shared_ptr<const C1DInterpolatorFactory > P1DInterpolatorFactory;

/**
   Create an interpolation factory from a type by also allocating the B-spline kernel if
   neccessary.
   @todo this should become the work of a plug-in handler
 */

// implementation
template <class T>
T1DInterpolator<T> *C1DInterpolatorFactory::create(const std::vector<T>& src) const
{
       return new T1DConvoluteInterpolator<T>(src, m_kernel, *m_bc);
}

/**
   @cond INTERNAL
   @ingroup traits
   \brief a trait to update the minimum and maxmimum values based on some input
*/
template <typename T>
struct __dispatch_min_max {
       static void apply(const T i, T& min, T& max);
};

/**
   @ingroup traits
   \brief a trait to copy some data
*/
template <typename I, typename O>
struct __dispatch_copy {
       static void apply(const I& input, O& output);
};

/// @endcond

NS_MIA_END

#endif
