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

NS_MIA_BEGIN

using std::invalid_argument; 

C2DMorphShape::C2DMorphShape():
	m_foreground_mask(new C2DShape()), 
	m_background_mask(new C2DShape())
{
}

C2DMorphShape::C2DMorphShape(P2DShape foreground_mask, P2DShape background_mask):
	m_foreground_mask(foreground_mask), 
	m_background_mask(background_mask)
{
	for (auto f = m_foreground_mask->begin(); f != m_foreground_mask->end(); ++f) {
		if (m_background_mask->has_location(*f)) {
			throw invalid_argument("C2DMorphShape: background and foreground mask overlap"); 
		}
	}
}
 
	
	
void C2DMorphShape::add_pixel(const value_type& pixel, bool foreground)
{
	if (!m_background_mask.unique()) 
		m_background_mask = P2DShape(new C2DShape(*m_background_mask)); 
	if (!m_foreground_mask.unique()) 
		m_foreground_mask = P2DShape(new C2DShape(*m_foreground_mask)); 

	
	if (foreground) {
		if (m_background_mask->has_location(pixel)) {
			throw create_exception<invalid_argument>( "Pixel location ", pixel , 
			      " can't be added to the foreground mask, since it it already "
			      " in the background mask"); 
		}
		m_foreground_mask->insert(pixel); 
	}else{
		if (m_foreground_mask->has_location(pixel)) {
			throw create_exception<invalid_argument>( "Pixel location ", pixel, 
			      " can't be added to the background mask, since it it already "
			      " in the foreground mask"); 
		}
		m_background_mask->insert(pixel); 
	}
}

void C2DMorphShape::add_pixel(int x, int y, bool foreground)
{
	add_pixel(value_type(x,y), foreground);
}

const C2DShape& C2DMorphShape::get_foreground_mask() const 
{
	return *m_foreground_mask; 
}

const C2DShape& C2DMorphShape::get_background_mask() const
{
	return *m_background_mask; 
}


C2DMorphShape C2DMorphShape::rotate_by_90() const
{
	return C2DMorphShape(rotate_90_degree(*m_foreground_mask), 
			     rotate_90_degree(*m_background_mask)); 
}

const char *C2DMorphShape::type_descr = "morphshapes";


/*
   - This could be parallized
   - using a bit-image with the infamous vector<bool> implementation is probably a 
     bad idea. 
 */

size_t morph_hit_and_miss_2d(C2DBitImage& target, const C2DBitImage& source, const C2DMorphShape& shape)
{
	assert(target.get_size() == source.get_size()); 
	
	size_t changed_pixels = 0; 

	const C2DBounds& size = source.get_size();
	auto res_i = target.begin();
	auto src_i = source.begin(); 
	
	
	for (size_t y = 0; y < size.y; ++y)
		for (size_t x = 0; x < size.x; ++x, ++res_i, ++src_i) {
			bool hit = true; 

			auto fgi = shape.get_foreground_mask().begin(); 
			auto fge = shape.get_foreground_mask().end();

			while (hit && fgi != fge) {
				C2DBounds nl(x + fgi->x, y + fgi->y);
				if (nl < size)
					hit &= source(nl); 
				++fgi; 
			}
			
			auto bgi = shape.get_background_mask().begin(); 
			auto bge = shape.get_background_mask().end();
			
			while (hit && bgi != bge) {
				C2DBounds nl(x + bgi->x, y + bgi->y);
				if (nl < size)
					hit &= !source(nl); 
				++bgi; 
			}
			*res_i = hit; 
			if (hit != *src_i) 
				++changed_pixels; 
		}
	return changed_pixels; 
}

/*
   - This could be parallized
   - using a bit-image with the infamous vector<bool> implementation is probably a 
     bad idea. 
 */

size_t morph_thinning_2d(C2DBitImage& target, const C2DBitImage& source, const C2DMorphShape& shape)
{
	assert(target.get_size() == source.get_size()); 
	
	size_t changed_pixels = 0; 

	const C2DBounds& size = source.get_size();
	auto res_i = target.begin();
	auto src_i = source.begin(); 
	
	copy(source.begin(), source.end(), target.begin()); 
	
	for (size_t y = 0; y < size.y; ++y)
		for (size_t x = 0; x < size.x; ++x, ++res_i, ++src_i) {
			bool hit = true; 

			auto fgi = shape.get_foreground_mask().begin(); 
			auto fge = shape.get_foreground_mask().end();

			while (hit && fgi != fge) {
				C2DBounds nl(x + fgi->x, y + fgi->y);
				if (nl < size)
					hit &= source(nl); 
				++fgi; 
			}
			
			auto bgi = shape.get_background_mask().begin(); 
			auto bge = shape.get_background_mask().end();
			
			while (hit && bgi != bge) {
				C2DBounds nl(x + bgi->x, y + bgi->y);
				if (nl < size)
					hit &= !source(nl); 
				++bgi; 
			}
			if (hit) {
				*res_i = false; 
				if (*src_i)
					++changed_pixels; 
			}
		}
	return changed_pixels; 
}

NS_MIA_END
