// Copyright (c) 2005 Rijksuniversiteit Groningen (Netherlands)
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.2/Skin_surface_3/include/CGAL/marching_tetrahedra_3.h $
// $Id: include/CGAL/marching_tetrahedra_3.h e13ef800cb7 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Nico Kruithof <Nico@cs.rug.nl>

#ifndef CGAL_MARCHING_TETRAHEDRA_H
#define CGAL_MARCHING_TETRAHEDRA_H

#include <CGAL/license/Skin_surface_3.h>


#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Modifier_base.h>
#include <CGAL/Cartesian_converter.h>
#include <CGAL/Marching_tetrahedra_observer_default_3.h>

namespace CGAL {

// If TriangulationDataStructure_3 only gets a Cell_handle range
// it is not possible to derive the Vertex_handle type.
template <class Vertex_iterator,
          class Cell_iterator,
          class HalfedgeDS_3,
          class MarchingTetrahedraTraits_3,
          class MarchingTetrahedraObserver_3 >
class Marching_tetrahedra_builder : public Modifier_base<HalfedgeDS_3> {
public:
  typedef HalfedgeDS_3                          HDS;
  typedef MarchingTetrahedraTraits_3            Traits;
  typedef MarchingTetrahedraObserver_3          Observer;

private:
  typedef Vertex_iterator                               T_Vertex_iterator;
  typedef Cell_iterator                                 T_Cell_iterator;
  typedef typename T_Cell_iterator::value_type          T_Cell;
  typedef typename T_Vertex_iterator::value_type        T_Vertex;

  typedef typename T_Vertex::Cell_handle                T_Cell_handle;
  typedef typename T_Cell::Vertex_handle                T_Vertex_handle;

  typedef typename T_Vertex_iterator::value_type *      T_Vertex_pointer;
  typedef typename T_Cell_iterator::value_type *        T_Cell_pointer;

  typedef typename HDS::Face_handle                     HDS_face_handle;
  typedef typename HDS::Vertex_handle                   HDS_vertex_handle;

  typedef std::map<T_Vertex_handle,bool>                T_vertex_map;
  typedef typename T_vertex_map::iterator               T_vertex_map_it;

  // First vertex lies inside the surface, the second vertex outside
  typedef std::pair<T_Vertex_handle,T_Vertex_handle>    T_edge;
  typedef std::map<T_edge,int>                          T_edge_map;
  typedef typename T_edge_map::iterator                 T_edge_map_it;

  typedef Polyhedron_incremental_builder_3<HDS>         Polyh_incr_builder;
public:

  Marching_tetrahedra_builder(
    T_Vertex_iterator vertices_begin,
    T_Vertex_iterator vertices_end,
    T_Cell_iterator cells_begin,
    T_Cell_iterator cells_end,
    const Traits &traits,
    Observer &observer)
    : vertices_begin(vertices_begin),
      vertices_end(vertices_end),
      cells_begin(cells_begin),
      cells_end(cells_end),
      traits(traits), observer(observer), nVertices(0) {
  }

  void operator()( HDS& hds) {
    // sortedV is an array of vertex indices.
    // The first nIn vertices lie inside the surface, the last 4-nIn outside.
    int sortedV[4], cellV[4], nIn;
    T_edge edge;

    Polyh_incr_builder B( hds, true);
    B.begin_surface(0,0,0);

    for (T_Cell_iterator cit = cells_begin; cit != cells_end; ++cit) {
      // Compute signs on vertices and sort them:
      nIn = 0;
      for (int i=0; i<4; i++) {
        if (is_inside(cit,i)) {
          sortedV[nIn] = i; nIn++;
        } else {
          sortedV[3-i+nIn] = i;
        }
      }

      // Process edges whose vertices lie on different sides of the surface
      int edgeN=0;
      for (int i=0; i<nIn; i++) {
        for (int j=nIn; j<4; j++) {
          cellV[edgeN++] = process_edge(B, cit,sortedV[i],sortedV[j]);
        }
      }

      // Construct triangles:
      CGAL_assertion(!B.error());
      if (nIn==1) {
        process_cell(B, cellV, (sortedV[0]%2)==1, cit);
        CGAL_assertion(!B.error());
      } else if (nIn==2) {
        bool change_orientation =
          (((sortedV[0] == 0) && (sortedV[1]==1)) ||
            ((sortedV[0] == 0) && (sortedV[1]==3)) ||
            ((sortedV[0] == 1) && (sortedV[1]==2)) ||
            ((sortedV[0] == 2) && (sortedV[1]==3)));

        process_cell(B, cellV, !change_orientation, cit);
         process_cell(B, cellV+1, change_orientation, cit);
        CGAL_assertion(!B.error());
      } else if (nIn==3) {
        process_cell(B, cellV, (sortedV[3]%2) == 1, cit);
        CGAL_assertion(!B.error());
      }

    }

    CGAL_assertion(!B.error());
    B.end_surface();
  }


  bool is_inside(T_Cell_iterator ch, int i) {
    CGAL_assertion(ch.operator->() != nullptr);
    //return (traits.sign(ch,i) == POSITIVE);
    T_vertex_map_it it = triang_vertex_signs.find((ch->vertex(i)));

    if (it == triang_vertex_signs.end()) {
      bool side = (traits.sign(ch,i) == POSITIVE);
      CGAL_assertion(triang_vertex_signs.find((ch->vertex(i))) ==
                     triang_vertex_signs.end());
      CGAL_assertion(ch.operator->() != nullptr);
      triang_vertex_signs[(ch->vertex(i))] = side;
      CGAL_assertion(triang_vertex_signs.find((ch->vertex(i))) !=
                     triang_vertex_signs.end());
      CGAL_assertion(triang_vertex_signs[(ch->vertex(i))] == side);
      return side;
    } else {
      return it->second;
    }
  }
  int process_edge(Polyh_incr_builder &B,
    T_Cell_iterator ch, int i, int j) {
    CGAL_assertion(is_inside(ch, i));
    CGAL_assertion(!is_inside(ch, j));

    T_edge edge = T_edge(ch->vertex(i),ch->vertex(j));
    T_edge_map_it edge_it = polyh_vert.find(edge);

    if (edge_it == polyh_vert.end()) {
      HDS_vertex_handle vh = B.add_vertex(traits.intersection(ch, i, j));
      polyh_vert[edge] = nVertices;
      nVertices ++;
      observer.after_vertex_insertion(ch, i, j, vh);
      return nVertices-1;
    } else {
      return edge_it->second;
    }
  }

  // Orientation is right
  void process_cell(
    Polyh_incr_builder &B,
    int *vs,
    bool change_orientation,
    T_Cell_iterator ch)
  {
    CGAL_assertion((vs[0]!=vs[1]) && (vs[0]!=vs[2]) && (vs[1]!=vs[2]));
    HDS_face_handle f = B.begin_facet();
    if (change_orientation) {
      B.add_vertex_to_facet( vs[0] );
      B.add_vertex_to_facet( vs[2] );
      B.add_vertex_to_facet( vs[1] );
    } else {
      B.add_vertex_to_facet( vs[0] );
      B.add_vertex_to_facet( vs[1] );
      B.add_vertex_to_facet( vs[2] );
    }
    B.end_facet();
    observer.after_facet_insertion(ch, f);
  }

private:
  T_Vertex_iterator vertices_begin, vertices_end;
  T_Cell_iterator cells_begin, cells_end;
  const Traits &traits;
  Observer &observer;
  T_edge_map polyh_vert;
  T_vertex_map triang_vertex_signs;
  int nVertices;
};


template <class Triangulation_3,
          class Polyhedron_3,
          class MarchingTetrahedraTraits_3>
void marching_tetrahedra_3(
  const Triangulation_3 &triangulation,
  Polyhedron_3 &polyhedron,
  const MarchingTetrahedraTraits_3 &traits) {

  typedef Marching_tetrahedra_observer_default_3
    <typename Triangulation_3::Finite_vertices_iterator,
    typename Triangulation_3::Finite_cells_iterator,
    Polyhedron_3>
                                                                Observer;

  marching_tetrahedra_3(triangulation.finite_vertices_begin(),
                        triangulation.finite_vertices_end(),
                        triangulation.finite_cells_begin(),
                        triangulation.finite_cells_end(),
                        polyhedron,
                        traits,
                        Observer());
}

template <class Triangulation_3,
          class Polyhedron_3,
          class MarchingTetrahedraTraits_3,
          class MarchingTetrahedraObserver_3>
void marching_tetrahedra_3(
  const Triangulation_3 &triangulation,
  Polyhedron_3 &polyhedron,
  const MarchingTetrahedraTraits_3 &traits,
  MarchingTetrahedraObserver_3 &observer) {

  marching_tetrahedra_3(triangulation.finite_vertices_begin(),
                        triangulation.finite_vertices_end(),
                        triangulation.finite_cells_begin(),
                        triangulation.finite_cells_end(),
                        polyhedron,
                        traits,
                        observer);
}

template <class Vertex_iterator,
          class Cell_iterator,
          class Polyhedron_3,
          class MarchingTetrahedraTraits_3,
          class MarchingTetrahedraObserver_3>
void marching_tetrahedra_3(
  Vertex_iterator finite_vertices_begin,
  Vertex_iterator finite_vertices_end,
  Cell_iterator finite_cells_begin,
  Cell_iterator finite_cells_end,
  Polyhedron_3 &polyhedron,
  const MarchingTetrahedraTraits_3 &traits,
  MarchingTetrahedraObserver_3 &observer) {

  typedef typename Polyhedron_3::HalfedgeDS                   HDS;
  typedef Marching_tetrahedra_builder<
    Vertex_iterator, Cell_iterator, HDS,
    MarchingTetrahedraTraits_3, MarchingTetrahedraObserver_3> Builder;

  Builder builder(finite_vertices_begin, finite_vertices_end,
                  finite_cells_begin, finite_cells_end,
                  traits, observer);
  polyhedron.delegate(builder);
}

} //namespace CGAL

#endif // CGAL_MARCHING_TETRAHEDRA_H
