/* -*- 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_scale1d_hh
#define mia_core_scale1d_hh

#include <cassert>
#include <vector>
#include <memory>

#include <mia/core/vector.hh>
#include <mia/core/msgstream.hh>
#include <mia/core/interpolator1d.hh>
#include <mia/core/spacial_kernel.hh>

NS_MIA_BEGIN

/**
   \ingroup interpol
   \brief A class for general scaling of one-dimensional arrays. 

   Class for general scaling of one-dimensional arrays. kernels are the separable B-spline 
   functions.  For upscaling, an interpolator is created by using the provided interpolator factory.
   Downscaling is done solving an overdetermined linear system of equations Ax = y. At construction 
   time the matrix A is QR decomposed, and later the downscaling is done by solving QRx=y
   \todo see if scaling can be expressed by a filter 
   \todo seems like downscaling isn't done properly 
*/

class EXPORT_CORE C1DScalar {
public:
	typedef std::vector<double> std_double_vector; 

	/**
	    Create the scaler prividing the given interpolator kernel.
	    \param kernel interpolation kernel 
	    \param in_size
	    \param out_size the input will be scaled to the given size, the scale is set accordingly 
	 */

	C1DScalar(const CSplineKernel& kernel, size_t in_size, size_t out_size);

	/**
	    Create the scaler providing the given interpolator kernel.
	    \param kernel interpolation kernel 
	    \param in_size
	    \param scale factor by which tho scale the input size, the interpolation will be 
	    exact with respect to the scale 
	 */

	C1DScalar(const CSplineKernel& kernel, size_t in_size, double scale);

	/**
	   Scaling operator.
	   \param input input data
	   \param[out] output when calling the function, the size of this vector must be set to the requested
	   size. The path for down or upscaling is automatically selected.
	 */
	void operator () (const std_double_vector& input, std_double_vector& output) const;

	/**
	   Alternate interface for scaling. Here the input data has to be copied into 
	   the pre-acclocated memory by using the \a input_begin() and \a input_end() iterators, 
	   and the result can be read using the \a output_begin(), \a output_end() iterators. 
	 */
	void run();

	/// \returns begin iterator of the input buffer 
	std_double_vector::iterator input_begin();  
	/// \returns end iterator of the input buffer 
	std_double_vector::iterator input_end();  

	/// \returns begin iterator of the output buffer 
	std_double_vector::iterator output_begin();  
	
	/// \returns end iterator of the output buffer 
	std_double_vector::iterator output_end();  

	/// \returns the size of the output vector
	size_t get_output_size() const; 
private:
	void initialize(const CSplineKernel& kernel); 
	void upscale(const std_double_vector& input, std_double_vector& output) const; 
	void downscale(const std_double_vector& input, std_double_vector& output) const; 

	enum EStrategy {
		scs_fill_output, 
		scs_upscale, 
		scs_copy, 
		scs_downscale, 
		scs_unknown
	}; 

	size_t m_in_size; 
	size_t m_support; 
	double m_scale; 
	std::vector<double> m_poles; 
	EStrategy m_strategy; 
	PSplineBoundaryCondition m_bc; 

	std_double_vector m_input_buffer; 
	
	size_t m_out_size; 
	std_double_vector m_output_buffer; 
	std::vector<CSplineKernel::VWeight> m_weights; 
	std::vector<CSplineKernel::VIndex> m_indices; 

	P1DSpacialKernel m_gauss; 
};



NS_MIA_END


#endif
