/* -*- 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/>.
 *
 */


#ifndef mia_3d_vfregularizerkernel_hh
#define mia_3d_vfregularizerkernel_hh

#include <mia/core/factory.hh>
#include <mia/3d/vectorfield.hh>

NS_MIA_BEGIN

/**
   @ingroup registration

   Kernel for iterative  3D vector field regularizer

   This is the base class for a 3D vector field reguarlizer
   kernels that is typically used in non-linear registration to
   evaluate a velocity field from a force field by applying
   some regularization model on the input data.
*/

class EXPORT_3D C3DFVectorfieldRegularizerKernel : public CProductBase
{
public:
       typedef C3DFVectorfieldRegularizerKernel plugin_type;
       typedef C3DFVectorfield plugin_data;

       class CBuffers
       {
       public:
              virtual ~CBuffers();
       };

       typedef std::unique_ptr<CBuffers> PBuffers;

       static const char *type_descr;

       typedef std::shared_ptr< C3DFVectorfieldRegularizerKernel > Pointer;

       C3DFVectorfieldRegularizerKernel(bool has_pertuberation);

       float evaluate_pertuberation_row(unsigned  y, unsigned  z, CBuffers& buffers) const;

       virtual ~C3DFVectorfieldRegularizerKernel();

       void set_data_fields(C3DFVectorfield  *output, const C3DFVectorfield *input);

       void set_update_fields(const T3DDatafield<unsigned char> *update_flags,
                              T3DDatafield<unsigned char> *set_flags, T3DDatafield<float> *residua,
                              float residual_thresh);

       float evaluate_row(unsigned y, unsigned z, CBuffers& buffers);

       float evaluate_row_sparse(unsigned y, unsigned z, CBuffers& buffers);

       unsigned get_boundary_padding() const;

       PBuffers get_buffers() const;

       void start_slice(unsigned z, CBuffers& buffers) const;

       bool has_pertuberation() const;
protected:
       C3DFVectorfield& get_output_field() const;
       const C3DFVectorfield& get_input_field() const;
       T3DDatafield<float>& get_residua() const;
       const T3DDatafield<unsigned char>& get_update_flags() const;
       T3DDatafield<unsigned char>& get_set_flags() const;
       float get_residual_thresh() const;
private:
       virtual void post_set_data_fields();

       virtual float do_evaluate_row(unsigned y, unsigned z, CBuffers& buffers) = 0;

       virtual float do_evaluate_row_sparse(unsigned y, unsigned z, CBuffers& buffers) = 0;

       virtual unsigned do_get_boundary_padding() const = 0;

       virtual PBuffers do_get_buffers() const;

       virtual void do_start_slice(unsigned z, CBuffers& buffers) const;

       virtual float do_evaluate_pertuberation_row(unsigned  y, unsigned  z, CBuffers& buffers) const;

       C3DFVectorfield *m_output;
       const C3DFVectorfield *m_input;
       T3DDatafield<float> *m_residua;
       const T3DDatafield<unsigned char> *m_update_flags;
       T3DDatafield<unsigned char> *m_set_flags;
       float m_residual_thresh;
       bool m_has_pertuberation;
};

inline
bool C3DFVectorfieldRegularizerKernel::has_pertuberation() const
{
       return m_has_pertuberation;
}

inline
C3DFVectorfield& C3DFVectorfieldRegularizerKernel::get_output_field() const
{
       return *m_output;
}

inline
const C3DFVectorfield& C3DFVectorfieldRegularizerKernel::get_input_field() const
{
       return *m_input;
}

inline
T3DDatafield<float>& C3DFVectorfieldRegularizerKernel::get_residua() const
{
       return *m_residua;
}

inline
const T3DDatafield<unsigned char>& C3DFVectorfieldRegularizerKernel::get_update_flags() const
{
       return *m_update_flags;
}

inline
T3DDatafield<unsigned char>& C3DFVectorfieldRegularizerKernel::get_set_flags() const
{
       return *m_set_flags;
}

inline
float C3DFVectorfieldRegularizerKernel::get_residual_thresh() const
{
       return m_residual_thresh;
}

typedef C3DFVectorfieldRegularizerKernel::Pointer P3DVectorfieldRegularizerKernel;

typedef TFactory<C3DFVectorfieldRegularizerKernel> C3DFVectorfieldRegularizerKernelPlugin;

typedef THandlerSingleton<TFactoryPluginHandler<C3DFVectorfieldRegularizerKernelPlugin>> C3DFVectorfieldRegularizerKernelPluginHandler;


template <> const char   *const TPluginHandler<C3DFVectorfieldRegularizerKernelPlugin>::m_help;
extern template class EXPORT_3D  TFactory<C3DFVectorfieldRegularizerKernel>;
extern template class EXPORT_3D  TFactoryPluginHandler<C3DFVectorfieldRegularizerKernelPlugin>;
extern template class EXPORT_3D  THandlerSingleton<TFactoryPluginHandler<C3DFVectorfieldRegularizerKernelPlugin>>;

/**
   @cond NEVER
   @ingroup traits
   @brief  Trait to make C3DFVectorfieldRegularizerPluginHandler available for creation by command line parsing
*/
FACTORY_TRAIT(C3DFVectorfieldRegularizerKernelPluginHandler);
/// @endcond



NS_MIA_END
#endif
