/* -*- 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_core_spacial_kernel_hh
#define mia_core_spacial_kernel_hh


#include <vector>

// MIA specific
#include <mia/core/defines.hh>
#include <mia/core/factory.hh>

NS_MIA_BEGIN

struct EXPORT_CORE spacial_kernel_data {
	static const char *data_descr;
};


struct EXPORT_CORE kernel_plugin_type {
	static const char *type_descr;
};

/**
   \ingroup filters
   \brief base class for 1D filter kernels 

   prototype for a 1D spacial convolution kernel
   \todo put the convolution into this class, problem: it must work for all
         data types but should also be virtual
*/

class EXPORT_CORE C1DFilterKernel : public CProductBase {
protected:
	
	/** constructs the kernel
	    \param fsize width parameter of the kernel
	*/
	C1DFilterKernel(unsigned fsize);

	virtual ~C1DFilterKernel();
public:
	/// define the plugin search path, data part  
	typedef spacial_kernel_data plugin_data; 
	/// define the plugin search path, type part 
	typedef kernel_plugin_type  plugin_type; 

	/// returns the filter width definition parameter
	int get_fsize() const;

	/// returns the width of the kernel
	size_t size() const;

	/** run the filter in-place 
	    @param data 
	 */
	void apply_inplace(std::vector<double>& data) const;

	/**
	   run the filter out-of-place 
	   @param data input data 
	   @returns the filtered output 
	 */
	std::vector<double> apply(const std::vector<double>& data) const;

private:
	/// returns the width of the kernel
	virtual size_t do_size() const = 0;

	virtual std::vector<double> do_apply(const std::vector<double>& data) const = 0;

	unsigned m_fsize;

};

/**
   \ingroup filters
   \brief Base class for folding kernal types.
 */

class EXPORT_CORE C1DFoldingKernel : public C1DFilterKernel {
public:

	
	/// Data type of the kernel coefficient vector 
	typedef std::vector<double> vec_mask;

	/// iterator over the kernel 
	typedef vec_mask::const_iterator const_iterator;

	/**
	   Kernel constructor of the given size 
	   @param fsize 
	 */
	C1DFoldingKernel(int fsize);


	/// returns a constant iterator at the begin of the filter kernel
	const_iterator begin()const;

	/// returns a constant iterator at the end of the filter kernel
	const_iterator end()const;

	/// returns a constant iterator at the begin of the derivative of the filter kernel
	const_iterator dbegin()const;

	/// returns a constant iterator at the end of the derivative of the filter kernel
	const_iterator dend()const;

	/// standard access operator, rw version 
	double& operator[](int i) {
		return m_mask[i];
	}
	
	/// standard access operator, ro version 
	double operator[](int i)const {
		return m_mask[i];
	}
protected:
	/// kernel iterator 
        typedef vec_mask::iterator iterator;

	/// returns an iterator at the begin of the kernel
	iterator begin();

	/// returns an iterator at the end of the kernel
	iterator end();

	/// returns an iterator at the begin if the derivative of the  kernel
	iterator dbegin();

	/// returns an iterator at the end if the derivative of the  kernel
	iterator dend();

private:
	/// returns the width of the kernel
	virtual size_t do_size() const;

	vec_mask m_mask;
	vec_mask m_derivative;

};

/// base class for filters kernels working in the spacial domain 
typedef TFactory<C1DFoldingKernel> C1DSpacialKernelPlugin;

typedef std::shared_ptr<C1DFoldingKernel> P1DSpacialKernel; 

template<>  const char * const 
TPluginHandler<TFactory<C1DFoldingKernel>>::m_help; 

/// plugin handler for spaciel filter kernels 
typedef THandlerSingleton<TFactoryPluginHandler<C1DSpacialKernelPlugin> > C1DSpacialKernelPluginHandler;


inline P1DSpacialKernel produce_spacial_kernel(const std::string& descr) 
{
	return C1DSpacialKernelPluginHandler::instance().produce(descr); 
}

/// @cond NEVER
FACTORY_TRAIT(C1DSpacialKernelPluginHandler); 
/// @endcond

NS_MIA_END

#endif
