/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis 
 * Copyright (c) Leipzig, Madrid 1999-2015 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/internal/plugintester.hh> 
#include <mia/3d/fullcost/label.hh>
#include <mia/3d/transformfactory.hh>

NS_MIA_USE


struct TransformInitFixture {
	TransformInitFixture(); 

	P3DTransformationFactory tff; 
}; 


BOOST_FIXTURE_TEST_CASE ( test_labeldistance, TransformInitFixture ) 
{
        // create two images and do the thing 
        

        const unsigned char mov_data[75] = {
                1, 1, 2, 2, 2, 
		1, 1, 2, 2, 2, 
                1, 1, 1, 2, 2, 
                1, 1, 5, 2, 2, 
                5, 5, 5, 5, 2, 
		
		1, 1, 3, 2, 2, 
                1, 1, 2, 2, 2, 
                1, 1, 1, 2, 2, 
                1, 5, 5, 2, 2, 
                5, 5, 5, 5, 2, 

		1, 1, 2, 2, 2, 
                1, 1, 2, 2, 2, 
                1, 1, 2, 2, 2, 
                1, 1, 1, 2, 2, 
                5, 5, 5, 5, 2, 

	}; 

        const unsigned char ref_data[75] = {
                1, 1, 1, 2, 2,  1, 1, 1, 2, 2,  1, 1, 1, 2, 2,  5, 5, 5, 2, 2,  5, 5, 2, 2, 2, 
		1, 1, 3, 2, 2,  1, 1, 1, 2, 2,  1, 1, 1, 2, 2,  5, 5, 5, 2, 2,  5, 5, 2, 2, 2, 
		1, 1, 1, 2, 2,  1, 1, 1, 2, 2,  1, 1, 1, 2, 2,  5, 5, 2, 2, 2,  5, 5, 2, 2, 2, 
	}; 

#if 0 
        const float distances [75] = {
		0, 0, 1, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0, 0, 0, 
                1, 1, 0, 0, 0, 
                0, 0, 1, sqrtf(2.0f), 0, 
		
		0, 0, 0, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0, 0, 0, 
                1, 0, 0, 0, 0, 
                0, 0, 1, sqrtf(2.0f), 0, 

		0, 0, 1, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 1, 0, 0, 
                1, 1, 1, 0, 0, 
                0, 0, 1, sqrtf(3.0f), 0

        }; 

#endif 
        const float gradz [75] = {
                0, 0, 0, 0, 0,   0, 0, 0, 0, 0,   0, 0, 0, 0, 0,   0, 0,  0, 0, 0,   0, 0, 0, 0, 0, 
                0, 0, 0, 0, 0,   0, 0, 0, 0, 0,   0, 0, 0, 0, 0,   0, 0,  0, 0, 0,   0, 0, 0, 0.5f * (sqrtf(2.0f) - sqrtf(3.0f)) , 0, 
                0, 0, 0, 0, 0,   0, 0, 0, 0, 0,   0, 0, 0, 0, 0,   0, 0,  0, 0, 0,   0, 0, 0, 0.5f * (sqrtf(2.0f) - sqrtf(3.0f)), 0 

        }; 

        const float grady [75] = {
                0, 0,  0, 0, 0, 
                0, 0,  0, 0, 0, 
		0, 0,  0, 0, 0, 
                -1, -1,  0, 0, 0, 
		0, 0,-.5f, -.5f * (sqrtf(2.0f) - 1.0f), 0, 

                0, 0,  0, 0, 0, 
                0, 0,  0, 0, 0, 
		0, 0,  0, 0, 0, 
                -1,0,  0, 0, 0, 
		0, 0,-.5f, -.5f * (sqrtf(2.0f) - 1.0f), 0, 

                0, 0,  0, 0, 0, 
                0, 0,  0, 0, 0, 
		0, 0,  0.5f, 0, 0, 
                -1, -1, -1, 0, 0, 
		0, 0, 0, -.5f * (sqrtf(3.0f) - sqrtf(2.0f)), 0 

        }; 
        
	const float gradx [75] = {
		0, 0, 1, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, -0.5f * sqrtf(2.0f), -0.5f * (sqrtf(5.0f) - 1.0f), 0, 

		0, 0, 0, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, -0.5f * sqrtf(2.0f), -0.5f * (sqrtf(5.0f) - 1.0f), 0,

		0, 0, 1, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0.5f * sqrtf(2.0f), 0, 0, 
                0, 0, -.5f * (sqrtf(2.0f) - 1.0f) , 0, 0, 
                0, 0, -0.5f * sqrtf(3.0f), -0.5f * (sqrtf(6.0f) - 1.0f), 0 

	}; 




        const C3DBounds size(5,5,3);
        
        
        C3DUBImage *mov_image = new C3DUBImage(size, mov_data );
        C3DUBImage *ref_image = new C3DUBImage(size, ref_data );

        P3DImage mov(mov_image); 
        P3DImage ref(ref_image);
        
        auto cost = BOOST_TEST_create_from_plugin<C3DLabelFullCostPlugin>("labelimage:maxlabel=7"); 
        
        auto t = tff->create(size); 
	auto params = t->get_parameters(); 
	std::fill(params.begin(), params.end(), 0.0); 
	t->set_parameters(params); 
        
        save_image("src.@", mov); 
        save_image("ref.@", ref); 

        cost->reinit();
        cost->set_size(size); 
        
        BOOST_CHECK_CLOSE(cost->cost_value(*t), 15 + 2 * sqrt(2.0) + sqrt(3.0), 0.01); 
        BOOST_CHECK_CLOSE(cost->cost_value(), 15 + 2 * sqrt(2.0) + sqrt(3.0), 0.01); 
        

	CDoubleVector gradient(t->degrees_of_freedom()); 
	double cost_value = cost->evaluate(*t, gradient);
        BOOST_CHECK_CLOSE(cost_value, 15 + 2 * sqrt(2.0) + sqrt(3.0), 0.01); 

	for(int i = 0; i < 75; ++i) {
		cvdebug() << "[" << i << "]: (" << gradient[3*i] << ", " << gradient[3*i+1] << ", " << gradient[3*i+2]
			  << ") expect ("<< gradx[i] << ", " << grady[i] << ", " << gradz[i] << ")\n"; 

		if (gradx[i] != 0.0) 
			BOOST_CHECK_CLOSE(gradient[3*i], gradx[i], 0.1); 
		else 
			BOOST_CHECK_SMALL(gradient[3*i], 1e-10); 

		if (grady[i] != 0.0) 
			BOOST_CHECK_CLOSE(gradient[3*i+1], grady[i], 0.1);
		else 
			BOOST_CHECK_SMALL(gradient[3*i+1], 1e-10); 

		if (gradz[i] != 0.0) 
			BOOST_CHECK_CLOSE(gradient[3*i+2], gradz[i], 0.1);
		else 
			BOOST_CHECK_SMALL(gradient[3*i+2], 1e-10); 


	}

}




TransformInitFixture::TransformInitFixture():
        tff(C3DTransformCreatorHandler::instance().produce("vf:imgkernel=[bspline:d=0],imgboundary=zero"))
{
        
}
