/* -*- 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/export_handler.hh>
#include <mia/2d/splinetransformpenalty.hh>
#include <mia/core/plugin_base.cxx>
#include <mia/core/handler.cxx>


NS_MIA_BEGIN
using namespace std; 

const char *C2DSplineTransformPenalty::data_descr = "2dtransform";
const char *C2DSplineTransformPenalty::type_descr = "splinepenalty"; 

C2DSplineTransformPenalty::C2DSplineTransformPenalty(double weight, bool normalize):
        m_weight(weight), 
	m_normalize(normalize)
{
}

C2DSplineTransformPenalty::~C2DSplineTransformPenalty()
{
}

void C2DSplineTransformPenalty::initialize(const C2DBounds& size, const C2DFVector& range, PSplineKernel kernel)
{
	m_size = size;
	m_range = range; 
	m_kernel = kernel; 
	do_initialize(); 
}


double C2DSplineTransformPenalty::value(const C2DFVectorfield&  coefficients) const
{
	assert(coefficients.get_size() == get_size()); 
	const double w = m_normalize ? m_weight / m_range.product() : m_weight; 
	return w * do_value(coefficients); 
}


double C2DSplineTransformPenalty::value_and_gradient(const C2DFVectorfield&  coefficients, CDoubleVector& gradient) const
{
	assert(coefficients.get_size() == get_size()); 
	assert(coefficients.size() * 2 == gradient.size()); 

	const double w = m_normalize ? m_weight / m_range.product() : m_weight; 
	double value =  w * do_value_and_gradient(coefficients, gradient); 
	transform(gradient.begin(), gradient.end(), gradient.begin(), 
		  [w](double x) { return - w * x;}); 
	return value; 
}

const C2DBounds& C2DSplineTransformPenalty::get_size() const
{
	return m_size; 
}

const C2DFVector& C2DSplineTransformPenalty::get_range() const
{
	return m_range; 
}

PSplineKernel C2DSplineTransformPenalty::get_kernel() const
{
	return m_kernel; 
}

double C2DSplineTransformPenalty::get_weight() const
{
	return m_weight; 
}

bool C2DSplineTransformPenalty::get_normalize() const
{
	return m_normalize; 
}


C2DSplineTransformPenalty *C2DSplineTransformPenalty::clone() const
{
	return do_clone(); 
}

C2DSplineTransformPenaltyPluginHandler::ProductPtr produce_2d_spline_transform_penalty(const string& descr)
{
	return C2DSplineTransformPenaltyPluginHandler::instance().produce(descr); 
}


C2DSplineTransformPenaltyPlugin::C2DSplineTransformPenaltyPlugin(char const * const  name):
	TFactory<C2DSplineTransformPenalty>(name), 
	m_weight(1.0), 
	m_normalize(false)
{
	add_parameter("weight", new CFBoundedParameter(m_weight, EParameterBounds::bf_min_open, {0.0f},
						       false, "weight of penalty energy"));
	add_parameter("norm", new CBoolParameter(m_normalize, false, "Set to 1 if the penalty should be normalized " 
						 "with respect to the image size")); 
}

C2DSplineTransformPenaltyPlugin::Product *C2DSplineTransformPenaltyPlugin::do_create() const
{
	return do_create(m_weight, m_normalize); 
}


template<> const  char * const 
TPluginHandler<C2DSplineTransformPenaltyPlugin>::m_help = 
	"This class provides the penalty put on spline based transformations  "
	"when considering image registration. This penalty term ensures "
	"the smoothness of a transformation or the evaluation of a similarity measure "
	"between a transformed study data set and a reference data set.";


EXPLICIT_INSTANCE_DERIVED_FACTORY_HANDLER(C2DSplineTransformPenalty, C2DSplineTransformPenaltyPlugin); 

C2DSplineTransformPenaltyPluginHandlerTest::C2DSplineTransformPenaltyPluginHandlerTest()
{

}

NS_MIA_END
