/* -*- 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/core/typedescr.hh>
#include <mia/core/errormacro.hh>
#include <mia/2d/filter/sortlabel.hh>

NS_BEGIN(sort_label_2dimage_filter)

NS_MIA_USE; 
using namespace std; 

template <typename T>
struct CSortLabel_dispatch {
	static P2DImage apply(const T2DImage<T>& /*image*/) {
		throw create_exception<invalid_argument>("Unsupported input label type ", __type_descr<T>::value);  
	}
}; 


template <typename T>
struct LabelSort {
	LabelSort():label(0), n(0){}
	T label; 
	size_t n; 
}; 

template <typename T>
bool LabelSort_predicate (const LabelSort<T>& lhs, const LabelSort<T>& rhs) 
{
	return (lhs.n > rhs.n) || (lhs.n == rhs.n && lhs.label < rhs.label); 
}

template <typename T>
P2DImage CSortLabel_run(const T2DImage<T>& image)
{
	vector<LabelSort<T> > counter(numeric_limits<T>::max()); 
	for (size_t i = 0; i < counter.size(); i++) 
		counter[i].label = i; 
	
	for_each(image.begin(), image.end(), [&counter](T value){ ++counter[value].n;}); 
	sort(counter.begin()+1, counter.end(), LabelSort_predicate<T>);
	
	vector<T> mapper(numeric_limits<T>::max()); 
	for(T i = 1; i <  numeric_limits<T>::max(); ++i) {
		mapper[counter[i].label] = i; 
	}
	T2DImage<T> *result = new T2DImage<T>(image); 
	transform(image.begin(), image.end(), result->begin(), 
		  [&mapper](T value){return mapper[value];}); 
	return P2DImage(result); 
}

template <>
struct CSortLabel_dispatch<unsigned char> {
	static P2DImage apply(const C2DUBImage& image) {
		return CSortLabel_run(image); 
	}
}; 

template <>
struct CSortLabel_dispatch<unsigned short> {
	static P2DImage apply(const C2DUSImage& image) {
		return CSortLabel_run(image); 
	}
}; 


template <typename T> 
P2DImage CSortLabel::operator() (const mia::T2DImage<T>& image) const
{
	return CSortLabel_dispatch<T>::apply(image); 
}



CSortLabel::result_type CSortLabel::do_filter(const C2DImage& image) const
{
	return mia::filter(*this, image); 
}

CSortLabelFilterPlugin::CSortLabelFilterPlugin():
	C2DFilterPlugin("sort-label")
{
}


C2DFilter *CSortLabelFilterPlugin::do_create()const
{
	return new CSortLabel(); 
}

const std::string CSortLabelFilterPlugin::do_get_descr()const
{
	return "This plug-in sorts the labels of a gray-scale image so that the lowest label "
		"value corresponts to the lable with themost pixels. The background (0) is not touched"; 
	
}

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


NS_END
