/* -*- 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/2d/morphshape.hh>
#include <mia/2d/imagetest.hh>

using namespace mia; 
using namespace std; 

BOOST_AUTO_TEST_CASE( test_simple_foreground_shape )
{
	C2DMorphShape shape; 
	C2DMorphShape::value_type pixel(1,0); 

	shape.add_pixel(pixel, true);
	
	BOOST_CHECK_EQUAL(shape.get_foreground_mask().size(), 1u); 
	BOOST_CHECK_EQUAL(shape.get_background_mask().size(), 0u); 

	BOOST_CHECK_EQUAL(*shape.get_foreground_mask().begin(), pixel); 
	
}

BOOST_AUTO_TEST_CASE( test_simple_background_shape )
{
	C2DMorphShape shape; 
	C2DMorphShape::value_type pixel(1,0); 

	shape.add_pixel(pixel, false);
	
	BOOST_CHECK_EQUAL(shape.get_foreground_mask().size(), 0u); 
	BOOST_CHECK_EQUAL(shape.get_background_mask().size(), 1u); 

	BOOST_CHECK_EQUAL(*shape.get_background_mask().begin(), pixel); 
	
}

BOOST_AUTO_TEST_CASE( test_mask_init_constructor_error )
{
	P2DShape shape(new C2DShape()); 
	shape->insert(C2DShape::value_type(1,1)); 
	
	BOOST_CHECK_THROW(C2DMorphShape(shape, shape), invalid_argument); 
}

BOOST_AUTO_TEST_CASE( test_mask_init_add_pixel_errors )
{
	C2DMorphShape shape; 
	C2DMorphShape::value_type pixel1(1,1); 
	C2DMorphShape::value_type pixel2(-1,1); 
	shape.add_pixel(pixel1, false); 
	shape.add_pixel(pixel2, true); 
	
	BOOST_CHECK_THROW(shape.add_pixel(pixel1, true), invalid_argument); 
	BOOST_CHECK_THROW(shape.add_pixel(pixel2, false), invalid_argument);

}

BOOST_AUTO_TEST_CASE( test_mask_check_rotate )
{
	C2DMorphShape shape; 
	C2DMorphShape::value_type pixel1(1,0); 
	C2DMorphShape::value_type pixel2(-1,1); 
	C2DMorphShape::value_type pixel3( 0,1); 

	C2DMorphShape::value_type pixel1r90(0, -1); 
	C2DMorphShape::value_type pixel2r90(1,1); 
	C2DMorphShape::value_type pixel3r90(1,0); 

	shape.add_pixel(1, 0, false); 
	shape.add_pixel(pixel2, true); 
	shape.add_pixel(pixel3, true);
	
	auto rshape = shape.rotate_by_90(); 

	auto fg  = rshape.get_foreground_mask(); 

	BOOST_CHECK_EQUAL(fg.size(), 2u); 
	BOOST_CHECK(fg.has_location(pixel2r90)); 
	BOOST_CHECK(fg.has_location(pixel3r90)); 


	auto bg  = rshape.get_background_mask(); 
	
	BOOST_CHECK_EQUAL(bg.size(), 1u); 	
	BOOST_CHECK(bg.has_location(pixel1r90)); 

}



BOOST_AUTO_TEST_CASE( test_simple_hit_and_miss ) 
{
	const C2DBounds size(9, 7); 
	
	vector<bool> input_image = {
		0, 1, 1, 0, 1, 0, 1, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 1, 1, 0, 0,
		0, 1, 0, 1, 0, 0, 1, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 1, 0,
		0, 0, 0, 0, 1, 0, 0, 1, 0,
		0, 1, 0, 0, 0, 0, 0, 0, 0
	}; 

	vector<bool> test_image = {
		0, 0, 0, 0, 1, 0, 1, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 1, 0, 1, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 1, 0, 0, 0, 0,
		0, 1, 0, 0, 0, 0, 0, 0, 0
	}; 
	
	// the "find singular pixel" shape
	C2DMorphShape shape; 
	shape.add_pixel(C2DMorphShape::value_type(0,0), true); 

	shape.add_pixel(C2DMorphShape::value_type(-1,-1), false); 
	shape.add_pixel(C2DMorphShape::value_type(-1, 0), false); 
	shape.add_pixel(C2DMorphShape::value_type(-1, 1), false); 
	shape.add_pixel(C2DMorphShape::value_type( 0,-1), false); 
	shape.add_pixel(C2DMorphShape::value_type( 0, 1), false); 
	shape.add_pixel(C2DMorphShape::value_type( 1,-1), false); 
	shape.add_pixel(C2DMorphShape::value_type( 1, 0), false); 
	shape.add_pixel(C2DMorphShape::value_type( 1, 1), false); 

	
	C2DBitImage source(size, input_image); 
	C2DBitImage target(size); 
	C2DBitImage expect(size, test_image); 

	BOOST_CHECK_EQUAL(morph_hit_and_miss_2d(target, source, shape), 7u);
	
	test_image_equal(target, expect);


}



BOOST_AUTO_TEST_CASE( test_thinning ) 
{
	const C2DBounds size(14, 16); 
	
	vector<bool> input_image = {
		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, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, // 4
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 9
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, // 9 
		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 5
		0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 6
		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 4
		0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, // 6
		0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 9
		0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, // 9 
		0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 6
		0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 = 68
		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,
	}; 


	vector<bool> test_image_1 = {
		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, 0, 0, 0, 0, 0, 0, 0, // 4
		0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, // 9
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, // 9 
		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 5
		0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 6
		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 4
		0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, // 6
		0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, // 9
		0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, // 9 
		0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 6
		0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 = 68
		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,
	}; 

	vector<bool> test_image_2 = {
		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, 0, 0, 0, 0, 0, 0, 0, // 4
		0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, // 9
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, // 9 
		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 5
		0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 6
		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 4
		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, // 6
		0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, // 9
		0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, // 9 
		0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 6
		0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 = 68
		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,
	}; 


	vector<bool> test_image_3 = {
		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, 0, 0, 0, 0, 0, 0, 0, // 4
		0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, // 9
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, // 9 
		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 5
		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, // 6
		0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 4
		0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, // 6
		0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, // 9
		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, // 9 
		0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 6
		0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 = 68
		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,
	}; 
	
	// the "find singular pixel" shape
	C2DMorphShape shape1; 

	shape1.add_pixel(-1, -1, false); 
	shape1.add_pixel( 0, -1, false); 
	shape1.add_pixel( 1, -1, false); 
	shape1.add_pixel( 0,  0, true); 
	shape1.add_pixel(-1,  1, true); 
	shape1.add_pixel( 0,  1, true); 
	shape1.add_pixel( 1,  1, true); 

	C2DMorphShape shape2; 
	shape2.add_pixel( 0, -1, false); 
	shape2.add_pixel( 1, -1, false); 
	shape2.add_pixel( 1,  0, false); 

	shape2.add_pixel( 0,  0, true); 
	shape2.add_pixel(-1,  0, true); 
	shape2.add_pixel( 0,  1, true); 



	C2DBitImage source(size, input_image); 
	C2DBitImage target(size); 
	C2DBitImage expect1(size, test_image_1); 
	C2DBitImage expect2(size, test_image_2);
	C2DBitImage expect3(size, test_image_3);

	BOOST_CHECK_EQUAL(morph_thinning_2d(target, source, shape1), 9u);
	
	test_image_equal(target, expect1);

	BOOST_CHECK_EQUAL(morph_thinning_2d(source, target, shape2), 4u);
	test_image_equal(source, expect2);

	BOOST_CHECK_EQUAL(morph_thinning_2d(target, source,  shape1.rotate_by_90()), 5u);
	test_image_equal(target, expect3);
	


}


