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

#include <mia/core/spacialkernel/gauss.hh>

NS_MIA_USE
using namespace std;
using namespace gauss_1d_folding_kernel;

C1DGaussFilterKernel::C1DGaussFilterKernel(unsigned fsize):
	C1DFoldingKernel(fsize)
{
	size_t n = size();
        assert(n > 0);

        vec_mask tmp(n);

        (*this)[0] = 1.0;
        if (n < 2)
                return;

        (*this)[1] = 1.0;

	for (size_t i = 2; i < n; i++){
		(*this)[i] = 1.0;
                transform(begin(), begin() + i, begin() + 1, tmp.begin() + 1, 
			  [](double a, double b){return a + b;}); 
                copy(tmp.begin() + 1, tmp.begin()+i, begin() +1);
	}

	const double norm = pow(2.0,n-1);
	transform(begin(), end(), begin(), [norm](double x){return x / norm;});

	vec_mask::iterator ider    = dbegin();
	vec_mask::iterator ikern   = begin();
	for (size_t i = 0; i < n; ++i, ++ider, ++ikern) {
		*ider = 2.0 * (fsize - i) * *ikern;
	}
}

C1DSpacialGaussKernelPlugin::C1DSpacialGaussKernelPlugin():
	C1DSpacialKernelPlugin("gauss"),
	m_w(1)
{
	add_parameter("w", make_lc_param(m_w, 0, false, "half filter width"));
}

C1DFoldingKernel *C1DSpacialGaussKernelPlugin::do_create() const
{
	if (m_w > 0)
		return new C1DGaussFilterKernel(m_w);
	else
		return NULL;
}

std::vector<double> C1DGaussFilterKernel::do_apply(const std::vector<double>& data) const
{
	typedef std::vector<double> vec_t;
	vec_t result(data.size(),0.0);

	C1DFoldingKernel::const_iterator  ek = end();

	vec_t::iterator  trgt = result.begin();

	int pos = data.size();

	// filter at left boundary
	for (int i = 0; i < get_fsize() && pos >= 0 ; ++i, --pos, ++trgt) {
		double ht = 0.0;
		C1DFoldingKernel::const_iterator  ik = begin();
		vec_t::const_iterator src = data.begin();

		for (int k = get_fsize() - i; k > 0;  --k, ++ik) {
			ht +=  src[k-1] * *ik;
		}

		while (ik != ek && src != data.end()){
			ht += *src++ * *ik++;
		}
		*trgt = ht;
	}

	// filter inside
	vec_t::const_iterator start = data.begin();
	while (pos-- > get_fsize()) {

		C1DFoldingKernel::const_iterator  ik = begin();

		vec_t::const_iterator help = start;

		double ht = 0.0;
		while (ik != ek){
			ht += *ik++ * *help++;
		}
		*trgt = ht;

		++trgt;
		++start;
	}

	// filter at right boundary
	for (int i = 0; i < get_fsize() && pos >= 0 && trgt != result.end() &&
		     start != data.end();   ++i, --pos, ++trgt, ++start) {
		
		C1DFoldingKernel::const_iterator  ik = begin();
		double  ht = 0.0;

		vec_t::const_iterator src = start;
		vec_t::const_reverse_iterator rsrc = data.rbegin();
		while (src != data.end() ){
			ht += *src++ * *ik++;
		}
		while (ik != end()) {
			ht += *rsrc++ * *ik++;
		}

		*trgt = ht;
	}
	return result;
}

const string C1DSpacialGaussKernelPlugin::do_get_descr()const
{
	return "spacial Gauss filter kernel";
}

extern "C" EXPORT CPluginBase  *get_plugin_interface()
{
	return new C1DSpacialGaussKernelPlugin();
}

