/* -*- 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/mesh/filter/vtxsort.hh>

NS_BEGIN(mia_meshfilter_vtxsort) 
using namespace mia; 
using namespace std; 


CVtxSortMeshFilter::CVtxSortMeshFilter(const C3DFVector& sort_direction ):
	m_sort_direction(sort_direction) 
{
}
	

PTriangleMesh CVtxSortMeshFilter::do_filter(const CTriangleMesh& mesh) const
{
	struct collector {
		float product; 
		C3DFVector v; 
		long index;
	}; 
	
	vector<collector> help; 
	help.reserve(mesh.vertices_size()); 
	
	collector c; 
	c.index = 0; 
	
	for(auto k = mesh.vertices_begin(); k != mesh.vertices_end(); ++k, ++c.index) {
		c.product = dot(*k, m_sort_direction); 
		c.v = *k; 
		help.push_back(c); 
	}

	sort(help.begin(), help.end(), 
	     [](const collector& lhs, const collector& rhs) {
		     return lhs.product < rhs.product; 
	     }); 

	PTriangleMesh result(new CTriangleMesh(mesh.triangle_size(), mesh.vertices_size())); 
	
	vector<unsigned int> reindex(mesh.vertices_size()); 

	// copy the sorted vertices
	auto out_vert = result->vertices_begin(); 
	unsigned int pos = 0; 
	for (auto h = help.begin(); h != help.end(); ++h, ++out_vert, ++pos) {
		*out_vert = h->v; 
		reindex[h->index] = pos; 
	}

	// copy the triangle connectivity
	transform(mesh.triangles_begin(), mesh.triangles_end(), result->triangles_begin(), 
		  [&reindex](const CTriangleMesh::triangle_type& t) {
			  return CTriangleMesh::triangle_type(reindex[t.x], 
							      reindex[t.y], 
							      reindex[t.z]); 
		  }); 
		  
	// if existent move the color scales and 
	if (mesh.get_available_data() & CTriangleMesh::ed_scale) {
		transform( help.begin(), help.end(), result->scale_begin(), 
			   [&mesh](const collector& c){
				   return mesh.scale_at(c.index); }); 
	}

	if (mesh.get_available_data() & CTriangleMesh::ed_color) {
		transform( help.begin(), help.end(), result->color_begin(), 
			   [&mesh](const collector& c){
				   return mesh.color_at(c.index);});
	}
	if (mesh.get_available_data() & CTriangleMesh::ed_normal) {
		transform( help.begin(), help.end(), result->normals_begin(), 
			   [&mesh](const collector& c){
				   return mesh.normal_at(c.index);});
	}
	return result; 
}

CVtxSortMeshFilterPlugin::CVtxSortMeshFilterPlugin():
	CMeshFilterPlugin("vtxsort"), 
	m_sort_direction(0,0,1)
{
	add_parameter("dir", make_param(m_sort_direction, false, 
					      "Sorting direction,"));
}

mia::CMeshFilter *CVtxSortMeshFilterPlugin::do_create()const
{
	return new CVtxSortMeshFilter(m_sort_direction);
}

const std::string CVtxSortMeshFilterPlugin::do_get_descr()const
{
	return "This filter sorts the vertices of a mesh according to the given direction. "
		"Specifically, the dot product between the given direction and the vertices "
		"will be ordered ascending"; 
}

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

NS_END
