/* -*- 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 <queue>
#include <mia/mesh/filter/selectbig.hh>
#include <mia/mesh/triangle_neighbourhood.hh>

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


CSelectBigMeshFilter::CSelectBigMeshFilter()
{
}


PTriangleMesh CSelectBigMeshFilter::do_filter(const CTriangleMesh& mesh) const
{
       CTrianglesWithAdjacentList tri_neigh(mesh);
       vector<bool> taken(mesh.triangle_size(), false);
       vector<bool> added(mesh.triangle_size(), false);
       size_t remaining = mesh.triangle_size();
       vector<unsigned> largest_section;

       while (remaining) {
              vector<unsigned> section;
              unsigned int idx = 0;

              while (taken[idx])
                     ++idx;

              cvdebug() << "Start section at " << idx << "\n";
              queue<unsigned> next_triangle;
              next_triangle.push(idx);
              added[idx] = true;

              while (!next_triangle.empty()) {
                     unsigned t = next_triangle.front();
                     next_triangle.pop();

                     if (!taken[t]) {
                            cvdebug() << "Add " << t << "\n";
                            section.push_back(t);
                            taken[t] = true;
                            --remaining;
                     }

                     auto& ajd =  tri_neigh[t];

                     for ( auto a : ajd) {
                            if (!taken[a] && !added[a]) {
                                   next_triangle.push(a);
                                   added[a] = true;
                            }
                     }
              }

              if (section.size() > largest_section.size()) {
                     swap(largest_section, section);
                     cvdebug() << "got section of size " << largest_section.size() << "\n";
              }

              if (largest_section.size() > remaining) {
                     cvdebug() << "Only remain " << remaining << " triangles, stop processing\n";
                     break;
              }
       }

       return get_sub_mesh(mesh, largest_section);
}

CSelectBigMeshFilterPlugin::CSelectBigMeshFilterPlugin():
       CMeshFilterPlugin("selectbig")
{
}

mia::CMeshFilter *CSelectBigMeshFilterPlugin::do_create()const
{
       return new CSelectBigMeshFilter();
}

const std::string CSelectBigMeshFilterPlugin::do_get_descr()const
{
       return "This filter selects the component with the highest number of triangles from "
              "a mesh that is actually composed of disconnected components. Components are "
              "considered to be disconnected if they don't share a common edge (they might "
              "share a common vertex though";
}

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

NS_END
