/* -*- 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/>.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <queue>
#include <iomanip>
#include <limits>

#include <boost/type_traits.hpp>

NS_MIA_BEGIN;

using namespace std; 

NAMESPACE_BEGIN(rgg_2dstack_filter)

struct PvSeeder {
	PvSeeder(const CProbabilityVector& p, float seed, int cls); 
	
	template <typename T> 
	bool operator()(T x) const; 


}; 

struct C2DRGGStackFilterImpl {
	C2DRGGStackFilterImpl(float seed, const CProbabilityVector& pv, 
			      size_t depth, float gt); 
	void do_initialize(::boost::call_traits<mia::P2DImage>::param_type x); 
	void do_push(::boost::call_traits<mia::P2DImage>::param_type x); 
	P2DImage do_filter(); 
	void post_finalize(); 
	void shift_buffer(); 

	template <typename T> 
	int operator () ( const T2DImage<T>& x); 
private: 
	
	float m_seed; 
	const CProbabilityVector pv; 
	size_t m_depth; 
	float m_gt; 
	
	vector<C3DUBImage>  m_region_buffer;  
	vector<P2DImage>    m_image_buffer; 
}; 

void C2DRGGStackFilterImpl::shift_buffer()
{
	copy_backward(m_region_buffer->begin(), 
		      m_region_buffer->end() - m_region_buffer->get_size().x * m_region_buffer->get_size().y , 
		      m_region_buffer->end());
	copy_backward(m_image_buffer->begin(), 
		      m_image_buffer->end() - m_image_buffer->get_size().x * m_image_buffer->get_size().y , 
		      m_image_buffer->end());
}


C2DRGGStackFilterImpl::C2DRGGStackFilterImpl(float seed, const CProbabilityVector& pv, 
					     size_t depth, float gt):
	C2DImageFifoFilter(1, depth, 0)
{
}

void C2DRGGStackFilterImpl::do_initialize(::boost::call_traits<mia::P2DImage>::param_type x)
{
}

template <typename T> 
int C2DRGGStackFilterImpl::operator () ( const T2DImage<T>& x)
{
	C2DUBImage mask(x.get_size(), x.get_attribute_list()); 
	transform(x.begin(), x.end(), mask.begin(), m_pvseed); 
}


void C2DRGGStackFilterImpl::do_push(::boost::call_traits<mia::P2DImage>::param_type x)
{
	m_image_buffer.push_back(x); 
	if (m_image_buffer.size() > depth) 
		m_image_buffer.pop_font(); 
	
	// run the region growing 
	mia::accumulate(*this, x); 
}

P2DImage C2DRGGStackFilterImpl::do_filter()
{
}

void C2DRGGStackFilterImpl::post_finalize()
{
}

C2DRGGStackFilter::C2DRGGStackFilter(float seed, const CProbabilityVector& pv, 
				     size_t depth, float gt):
	m_impl(new C2DRGGStackFilterImpl(seed, pv, depth, gt)
{
}


void C2DRGGStackFilter::do_initialize(::boost::call_traits<mia::P2DImage>::param_type x)
{
	m_impl->do_initialize(x); 
}

void C2DRGGStackFilter::do_push(::boost::call_traits<mia::P2DImage>::param_type x)
{
	m_impl->do_push(x);
}

P2DImage C2DRGGStackFilter::do_filter()
{
	return m_impl->do_filter(); 
}

void C2DRGGStackFilter::post_finalize()
{
	m_impl->post_finalize(); 
}

class C2DRGGStackFilterFactory : public C2DFifoFilterPlugin {
public: 
	C2DRGGStackFilterFactory(); 
private: 
	
	virtual const string do_get_descr() const;
	virtual C2DImageFifoFilter *do_create()const;

	string m_seed_map; 
	float  m_seed_thresh; 
	int    m_depth; 
	float  m_gradient_thresh; 
}; 


C2DRGGStackFilterFactory::C2DRGGStackFilterFactory():
	C2DFifoFilterPlugin("regiongrow"), 
	m_seed_thresh(0.9), 
	m_depth(5), 
	m_gradient_thresh(4.0)
{
	add_parameter("map", new CStringParameter(m_seed_map, CCmdOptionFlags::required_input, "class probability map"));
	add_parameter("st", new CFloatParameter(m_seed_thresh, 0.0, 1.0, 
					      false, "seed probability threshold"));
	add_parameter("depth", new CIntParameter(m_depth, 1, 30, 
					      false, "number of slices to keep during processing"));
	add_parameter("gt", new CFloatParameter(m_gradient_thresh, 4.0, 
						numeric_limits<float>::max(), 
						false, "gradient threshold"));
}
 

const string C2DRGGStackFilterFactory::do_get_descr() const
{
	return "Gradiend based region growing 2D fifo filter"; 
}
C2DImageFifoFilter *C2DRGGStackFilterFactory::do_create()const
{
	CProbabilityVector pv(m_seed_map);
	return C2DFifoFilterPlugin::ProductPtr(
             new C2DRGGStackFilter(m_seed_thresh, pv, 
				   m_depth, m_gradient_thresh)); 
}

extern "C" EXPORT CPluginBase *get_plugin_interface()
{
	return new C2DRGGStackFilterFactory(); 
}

NAMESPACE_END // end namespace regiongrow_2dstack_filter
