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

// very wired thing, this should work automatically ...
#ifdef G_HAVE_INLINE
#define G_CAN_INLINE 1
#endif

#include <gts.h>

// MONA specific
#include <mia/mesh/triangularMesh.hh>

using namespace std;
using namespace mia;

typedef struct  {
       CTriangleMesh::vertex_iterator vi;
       guint32  c_index;
} vertex_data_t;


typedef struct {
       CTriangleMesh::triangle_iterator ti;
       guint32 c_index;
} triangle_data_t;

static void copy_vertices(GtsPoint *p, vertex_data_t *v)
{
       auto& vec = *v->vi;
       vec.x = p->x;
       vec.y = p->y;
       vec.z = p->z;
       GTS_OBJECT(p)->reserved = GUINT_TO_POINTER(v->c_index++);
       ++v->vi;
}

static void copy_triangles(GtsTriangle *tri, CTriangleMesh::triangle_iterator *ti)
{
       GtsVertex *v1;
       GtsVertex *v2;
       GtsVertex *v3;
       gts_triangle_vertices(tri, &v1, &v2, &v3);
       (**ti).x = GPOINTER_TO_UINT( GTS_OBJECT(v1)->reserved);
       (**ti).y = GPOINTER_TO_UINT( GTS_OBJECT(v2)->reserved);
       (**ti).z = GPOINTER_TO_UINT( GTS_OBJECT(v3)->reserved);
       ++(*ti);
}


EXPORT  CTriangleMesh *gts_to_mona_mesh(GtsSurface *surface, bool reverse)
{
       int n_vertices = gts_surface_vertex_number(surface);
       int n_faces = gts_surface_face_number(surface);
       CTriangleMesh::CVertexfield vinitial(n_vertices);
       vertex_data_t vdata = {vinitial.begin(), 0};
       gts_surface_foreach_vertex(surface, (GtsFunc)copy_vertices, &vdata);
       CTriangleMesh::CTrianglefield initriangles(n_faces);
       auto ti = initriangles.begin();
       gts_surface_foreach_face(surface, (GtsFunc)copy_triangles, &ti);
       auto vertices  = CTriangleMesh::PVertexfield(new  CTriangleMesh::CVertexfield);
       vertices->reserve(n_vertices);
       vector<int> remap(n_vertices);
       auto r = remap.begin();

       for (auto v = vinitial.begin(); v != vinitial.end(); ++v, ++r) {
              auto ov = find(vertices->begin(), vertices->end(), *v);

              if (ov != vertices->end()) {
                     cvdebug() << "found duplicate vertex\n";
                     *r = distance(vertices->begin(), ov);
              } else {
                     *r = vertices->size();
                     vertices->push_back(*v);
              }
       }

       auto triangles = CTriangleMesh::PTrianglefield(new  CTriangleMesh::CTrianglefield);
       triangles->reserve(n_faces);
       for_each(initriangles.begin(), initriangles.end(),
       [&remap, &triangles](CTriangleMesh::triangle_type & t) -> void {
              t.x = remap[t.x];
              t.y = remap[t.y];
              t.z = remap[t.z];

              if (t.x != t.y && t.x != t.z && t.y != t.z)
                     triangles->push_back(t);
       });

       if (reverse) {
              cvmsg() << "revert triangles\n";
              transform(triangles->begin(), triangles->end(), triangles->begin(),
              [](const CTriangleMesh::triangle_type & t) {
                     return CTriangleMesh::triangle_type(t.x, t.z, t.y);
              });
       }

       return new CTriangleMesh(triangles, vertices, NULL, NULL, NULL);
}

