/* -*- 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/internal/autotest.hh>
#include <mia/3d/filter/crop.hh>

NS_MIA_USE
using namespace std;
using namespace ::boost::unit_test;
using namespace crop_3d_filter;

struct CropFixture {
	CropFixture();

	C3DBounds size;
	C3DSIImage src;

	void check_result(C3DCrop& f, const C3DBounds& start, C3DBounds rsize);

};

CropFixture::CropFixture():
	size(10,12,13),
	src(size)
{
	C3DSIImage::iterator i = src.begin();
	for (size_t z = 0; z < size.z; ++z)
		for (size_t y = 0; y < size.y; ++y)
			for (size_t x = 0; x < size.x; ++x, ++i) {
				*i = (x+1 ) * (y +1) * (z+1);
			}
	src.set_voxel_size(C3DFVector(2,3,4)); 
}

void CropFixture::check_result(C3DCrop& f, const C3DBounds& start, C3DBounds rsize)
{
	P3DImage result_wrap = f.filter(src);
	const C3DSIImage *result = dynamic_cast<const C3DSIImage *>(result_wrap.get());
	BOOST_REQUIRE(result);
	BOOST_REQUIRE(result->get_size() == rsize);

	C3DSIImage::const_iterator i = result->begin();
	for (size_t z = 0; z < rsize.z; ++z)
		for (size_t y = 0; y < rsize.y; ++y)
			for (size_t x = 0; x < rsize.x; ++x, ++i) {
				BOOST_CHECK_EQUAL(static_cast<size_t>(*i), 
						  (x+1+start.x ) * (y +1+start.y) * (z+1+start.z));
			}
	BOOST_CHECK_EQUAL(result->get_voxel_size(), src.get_voxel_size()); 

	BOOST_CHECK_EQUAL(result->get_origin(), C3DFVector(start) * src.get_voxel_size()); 
}

BOOST_FIXTURE_TEST_CASE( test_crop_inside, CropFixture )
{
	C3DBounds start(2,3,4);
	C3DBounds end(10,9,11);

	C3DCrop f(start, end);
	check_result(f, start, end-start);
}

BOOST_FIXTURE_TEST_CASE( test_crop_x_outside, CropFixture )
{
	C3DBounds start(10,3,4);
	C3DBounds end(10,9,11);

	C3DCrop f(start, end);
	BOOST_CHECK_THROW(f.filter(src), std::invalid_argument);
}

BOOST_FIXTURE_TEST_CASE( test_crop_y_outside, CropFixture )
{
	C3DBounds start(3,20,4);
	C3DBounds end(10,9,11);

	C3DCrop f(start, end);
	BOOST_CHECK_THROW(f.filter(src), std::invalid_argument);
}

BOOST_FIXTURE_TEST_CASE( test_crop_z_outside, CropFixture )
{
	C3DBounds start(3,4,40);
	C3DBounds end(10,9,11);

	C3DCrop f(start, end);
	BOOST_CHECK_THROW(f.filter(src), std::invalid_argument);
}

BOOST_FIXTURE_TEST_CASE( test_crop_limit_x, CropFixture )
{
	C3DBounds start(2,3,4);
	C3DBounds end(-1,9,11);

	C3DCrop f(start, end);
	check_result(f, start, C3DBounds(size.x-start.x,end.y-start.y,end.z-start.z));
}

BOOST_FIXTURE_TEST_CASE( test_crop_limit_y, CropFixture )
{
	C3DBounds start(2,3,4);
	C3DBounds end(10,-1,11);

	C3DCrop f(start, end);
	check_result(f, start, C3DBounds(end.x-start.x,size.y-start.y,end.z-start.z));
}

BOOST_FIXTURE_TEST_CASE( test_crop_limit_z, CropFixture )
{
	C3DBounds start(2,3,4);
	C3DBounds end(10,9,-1);

	C3DCrop f(start, end);
	check_result(f, start, C3DBounds(end.x-start.x,end.y-start.y,size.z-start.z));
}

BOOST_FIXTURE_TEST_CASE( test_crop_range_error_x, CropFixture )
{
	C3DBounds start(6,3,4);
	C3DBounds end(5,9,11);

	C3DCrop f(start, end);
	BOOST_CHECK_THROW(f.filter(src), std::invalid_argument);
}

BOOST_FIXTURE_TEST_CASE( test_crop_range_error_y, CropFixture )
{
	C3DBounds start(6,7,4);
	C3DBounds end(8,2,11);

	C3DCrop f(start, end);
	BOOST_CHECK_THROW(f.filter(src), std::invalid_argument);
}

BOOST_FIXTURE_TEST_CASE( test_crop_range_error_z, CropFixture )
{
	C3DBounds start(6,2,4);
	C3DBounds end(8,7,1);

	C3DCrop f(start, end);
	BOOST_CHECK_THROW(f.filter(src), std::invalid_argument);
}
