// 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/v5.5.2/Skin_surface_3/include/CGAL/Compute_anchor_3.h $
// $Id: Compute_anchor_3.h 254d60f 2019-10-19T15:23:19+02:00 Sébastien Loriot
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Nico Kruithof <Nico@cs.rug.nl>

#ifndef CGAL_COMPUTE_ANCHOR_3_H
#define CGAL_COMPUTE_ANCHOR_3_H

#include <CGAL/license/Skin_surface_3.h>


#include <CGAL/Regular_triangulation_3.h>
#include <CGAL/Triangulation_simplex_3.h>

namespace CGAL {

template < class RegularTriangulation_3>
class Compute_anchor_3
{
public:
  typedef RegularTriangulation_3                       Regular_triangulation;
  typedef typename Regular_triangulation::Geom_traits  Geom_traits;
  typedef typename Geom_traits::Weighted_point_3       Weighted_point;

  typedef typename RegularTriangulation_3::Vertex_handle Vertex_handle;
  typedef typename RegularTriangulation_3::Cell_handle   Cell_handle;
  typedef typename RegularTriangulation_3::Facet         Facet;
  typedef typename RegularTriangulation_3::Edge          Edge;

  typedef typename RegularTriangulation_3::Finite_vertices_iterator Finite_vertices_iterator;
  typedef typename RegularTriangulation_3::Finite_edges_iterator    Finite_edges_iterator;
  typedef typename RegularTriangulation_3::Finite_facets_iterator   Finite_facets_iterator;
  typedef typename RegularTriangulation_3::Finite_cells_iterator    Finite_cells_iterator;
  typedef typename RegularTriangulation_3::Facet_circulator         Facet_circulator;
  typedef Triangulation_simplex_3<RegularTriangulation_3>           Simplex;

  typedef typename std::list<Simplex>::iterator                     Simplex_iterator;

  Compute_anchor_3(const RegularTriangulation_3 &reg) : reg(reg) { }

  Simplex anchor_del( const Vertex_handle v )
  {
    equiv_anchors.clear();
    return v;
  }

  Simplex anchor_del( const Edge &e ) {
    return compute_anchor_del(e);
  }

  Simplex anchor_del( const Facet &f ) {
    return compute_anchor_del(f);
  }

  Simplex anchor_del( const Cell_handle ch ) {
    return compute_anchor_del(ch);
  }

  Simplex anchor_del( const Simplex &s )
  {
    int dim = s.dimension();
    if (dim == 0) {
      Vertex_handle vh = s;
      return anchor_del(vh);
    } else if (dim == 1) {
      Edge e = s;
      return anchor_del(e);
    } else if (dim == 2) {
      Facet f = s;
      return anchor_del(f);
    } else if (dim == 3) {
      Cell_handle ch = s;
      return anchor_del(ch);
    }
    CGAL_error();
    return Simplex();
  }

  Simplex anchor_vor( const Vertex_handle v ) {
    return compute_anchor_vor(v);
  }

  Simplex anchor_vor( const Edge &e ) {
    return compute_anchor_vor(e);
  }

  Simplex anchor_vor( const Facet &f ) {
    return compute_anchor_vor(f);
  }

  Simplex anchor_vor( const Cell_handle ch )
  {
    equiv_anchors.clear();
    for (int i=0; i<4; i++) {
      Cell_handle ch2 = ch->neighbor(i);
      if (!reg.is_infinite(ch2)) {
        Sign side = test_anchor(ch,ch2);
        CGAL_assertion(test_anchor(ch2,ch)==side);
        if (side==ZERO) {
          equiv_anchors.push_back(ch2);
        } else {
          CGAL_assertion(side==POSITIVE);
        }
      }
    }
    return ch;
  }

  Simplex anchor_vor( const Simplex &s )
  {
    int dim = s.dimension();
    if (dim == 0) {
      return anchor_vor(Vertex_handle(s));
    } else if (dim == 1) {
      return anchor_vor(Edge(s));
    } else if (dim == 2) {
      return anchor_vor(Facet(s));
    } else if (dim == 3) {
      return anchor_vor(Cell_handle(s));
    }
    return Simplex();
  }

  bool is_degenerate() {
    return !equiv_anchors.empty();
  }

  Simplex_iterator equivalent_anchors_begin() {
    return equiv_anchors.begin();
  }

  Simplex_iterator equivalent_anchors_end() {
    return equiv_anchors.end();
  }

private:
  ///////////////////////////////
  // Anchor functions
  ///////////////////////////////
  Simplex compute_anchor_del( Edge const &e );
  Simplex compute_anchor_del( Facet const &f );
  Simplex compute_anchor_del( Cell_handle const ch );

  Simplex compute_anchor_vor( Vertex_handle const v );
  Simplex compute_anchor_vor( Edge const &e );
  Simplex compute_anchor_vor( Facet const &f );

  // Test whether the anchor of edge (wp1,wp2) and wp2 are equal
  Sign test_anchor(Weighted_point &wp1, Weighted_point &wp2)
  {
    return enum_cast<Sign>(
      -reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(wp1, wp2));
  }

  Sign test_anchor(Weighted_point const& wp1, Weighted_point const& wp2,
                   Weighted_point const& wp3)
  {
    return enum_cast<Sign>(
      - reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(wp1, wp2, wp3));
  }

  Sign test_anchor(Weighted_point const& wp1, Weighted_point const& wp2,
                   Weighted_point const& wp3, Weighted_point const& wp4)
  {
    return enum_cast<Sign>(
      -reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(wp1, wp2, wp3, wp4));
  }

  // Test whether the anchor of e and anchor of e.first->vertex(i) are equal
  Sign test_anchor(Edge e, int i)
  {
    CGAL_assertion(!reg.is_infinite(e));
    CGAL_assertion(e.second == i || e.third == i);
    Weighted_point wp1, wp2;
    Cell_handle ch = e.first;
    return test_anchor(
      ch->vertex(e.second+e.third-i)->point(),
      ch->vertex(i)->point());
  }

  // Test whether the anchor of f and anchor of the edge f - f.first->vertex(i)
  // are equal
  Sign test_anchor(Facet f, int i)
  {
    CGAL_assertion(!reg.is_infinite(f));
    CGAL_assertion(f.second != i);
    CGAL_assertion(0<=f.second && f.second<4);
    CGAL_assertion(0<=i && i<4);

    Weighted_point wp1, wp2, wp3;
    Cell_handle ch = f.first;
    wp3 = ch->vertex(i)->point();
    switch ((4+i-f.second)&3) {
      case 1: CGAL_assertion (((f.second+1)&3) == i);
        wp1 = ch->vertex((f.second+2)&3)->point();
        wp2 = ch->vertex((f.second+3)&3)->point();
        break;
      case 2: CGAL_assertion (((f.second+2)&3) == i);
        wp1 = ch->vertex((f.second+1)&3)->point();
        wp2 = ch->vertex((f.second+3)&3)->point();
        break;
      case 3: CGAL_assertion (((f.second+3)&3) == i);
        wp1 = ch->vertex((f.second+1)&3)->point();
        wp2 = ch->vertex((f.second+2)&3)->point();
        break;
      default:
        CGAL_error();
    }
    return enum_cast<Sign>(
      -reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(wp1, wp2, wp3));
  }

  // Test whether the anchor of ch and anchor of the facet (ch,i) are equal
  Sign test_anchor(Cell_handle ch, int i)
  {
    CGAL_assertion(!reg.is_infinite(ch));

    return enum_cast<Sign>(
      - reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(
      ch->vertex((i+1)&3)->point(),
      ch->vertex((i+2)&3)->point(),
      ch->vertex((i+3)&3)->point(),
      ch->vertex(i)->point()));
  }

  Sign test_anchor(Cell_handle ch, Cell_handle ch2)
  {
    CGAL_assertion(!reg.is_infinite(ch));
    CGAL_assertion(!reg.is_infinite(ch2));

    int index = ch2->index(ch);
    return enum_cast<Sign>(
             -reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(
      ch->vertex(0)->point(),
      ch->vertex(1)->point(),
      ch->vertex(2)->point(),
      ch->vertex(3)->point(),
      ch2->vertex(index)->point()));
  }

  Sign test_anchor(Weighted_point const& wp1, Weighted_point const& wp2,
                   Weighted_point const& wp3, Weighted_point const& wp4,
                   Weighted_point const& wp5)
  {
    return enum_cast<Sign>(
      -reg.geom_traits().power_side_of_bounded_power_sphere_3_object()(wp1, wp2, wp3, wp4, wp5));
  }

  const Regular_triangulation &reg;
  std::list<Simplex> equiv_anchors;
};

// compute_anchor_del

template < class RegularTriangulation3 >
typename Compute_anchor_3<RegularTriangulation3>::Simplex
Compute_anchor_3<RegularTriangulation3>::
compute_anchor_del(Edge const &e)
{
  CGAL_assertion(!reg.is_infinite(e));
  equiv_anchors.clear();
  Sign result = test_anchor(e, e.second);
  if (result==NEGATIVE) {
    return Simplex(e.first->vertex(e.third));
  } else if (result==ZERO) {
    equiv_anchors.push_back(Simplex(e.first->vertex(e.third)));
  }
  result = test_anchor(e, e.third);
  if (result==NEGATIVE) {
    equiv_anchors.clear();
    return Simplex(e.first->vertex(e.second));
  } else if (result==ZERO) {
    equiv_anchors.push_back(Simplex(e.first->vertex(e.third)));
  }
  return Simplex(e);
}

template < class RegularTriangulation3 >
typename Compute_anchor_3<RegularTriangulation3>::Simplex
Compute_anchor_3<RegularTriangulation3>::
compute_anchor_del(Facet const &f)
{
  CGAL_assertion(!reg.is_infinite(f));
  equiv_anchors.clear();

  int i;
  Sign result;
  bool contains_center = true;
  for (i=1; (i<4) && contains_center; i++) {
    result = test_anchor(f, (f.second+i)&3);
    contains_center = (result != NEGATIVE);
    if (result == ZERO) {
      //equiv_anchors.push_back(Edge(f.first, f.second,(f.second+i)&3));
      equiv_anchors.push_back(
        Edge(f.first, (f.second+(i==1?2:1))&3, (f.second+(i==3?2:3))&3));
    }
  }

  if (contains_center) {
    return Simplex(f);
  } else {
    i--;
    Edge e; e.first = f.first;
    if (i==1) e.second = ((f.second+2)&3);
    else e.second = ((f.second+1)&3);
    if (i==3) e.third = ((f.second+2)&3);
    else e.third = ((f.second+3)&3);

    Simplex s = anchor_del(e);
    if (s.dimension() == 1) {
      equiv_anchors.clear();
      return s;
    } else {
      // The anchor is the anchor of the other edge adjacent to tmp
      CGAL_assertion(s.dimension() == 0);
      Vertex_handle vh=s;
      e.second = e.first->index(vh);
      e.third = (f.second+i)&3;

      s = anchor_del(e);
      equiv_anchors.clear();
      return s;
    }
  }
}

template < class RegularTriangulation3 >
typename Compute_anchor_3<RegularTriangulation3>::Simplex
Compute_anchor_3<RegularTriangulation3>::
compute_anchor_del(Cell_handle const ch)
{
  CGAL_assertion(!reg.is_infinite(ch));
  equiv_anchors.clear();

  Simplex s;
  bool contains_center = true;
  Sign result;
  for (int i=0; (i<4) && contains_center; i++) {
    result = test_anchor(ch, i);
    if (result == NEGATIVE) {
      contains_center = false;
      s = anchor_del(Facet(ch,i));
    } else if (result == ZERO) {
      equiv_anchors.push_back(Facet(ch,i));
    }
  }

  if (contains_center) {
    return Simplex(ch);
  } else {
    Simplex tmp;

    bool found=true;
    while (true) {
      if (s.dimension() == 1) {
        // Test two adjacent facets
        Edge e=s;
        int ind1 = ch->index(e.first->vertex(e.second));
        int ind2 = ch->index(e.first->vertex(e.third));
        for (int i=0; (i<4) && found; i++) {
          if ((i != ind1) && (i != ind2)) {
            tmp = anchor_del(Facet(ch, i));
            found = (s == tmp);
          }
        }
      } else if (s.dimension() == 0) {
        // Test adjacent edges
        Vertex_handle vh=s;
        int index = ch->index(vh);
        for (int i=0; (i<4) && found; i++) {
          if (i != index) {
            tmp = anchor_del(Edge(ch, index, i));
            found = (s == tmp);
          }
        }
      } else {
        CGAL_assertion(s.dimension() == 2);
      }
      if (found) {
        equiv_anchors.clear();
        return s;
      }
      found = true;
      s = tmp;
    }
  }
}

template < class RegularTriangulation3 >
typename Compute_anchor_3<RegularTriangulation3>::Simplex
Compute_anchor_3<RegularTriangulation3>::
compute_anchor_vor (Vertex_handle const v)
{
  CGAL_assertion(!reg.is_infinite(v));
  CGAL_assertion(reg.is_vertex(v));

  equiv_anchors.clear();

  Sign side;
  bool contains_center=true;
  Simplex s;

  std::list<Vertex_handle> adj_vertices;
  typename std::list<Vertex_handle>::iterator adj_vertex;
  reg.incident_vertices(v, std::back_inserter(adj_vertices));

  for (adj_vertex = adj_vertices.begin();
       (adj_vertex != adj_vertices.end()) && contains_center;
       adj_vertex++) {
    if (!reg.is_infinite(*adj_vertex)) {
      side = test_anchor(v->point(),(*adj_vertex)->point());
      if (side == NEGATIVE) {
        contains_center = false;
      } else if (side == ZERO) {
        Edge e;
        if (!reg.is_edge (v, *adj_vertex, e.first, e.second, e.third)) {
          CGAL_error();
        }
        equiv_anchors.push_back(Simplex(e));
      }
    }
  }

  if (contains_center) {
    return Simplex(v);
  } else {
    adj_vertex--;
    Edge e;
    if (!reg.is_edge(v, *adj_vertex, e.first, e.second, e.third)) {
      CGAL_error();
    }
    s = anchor_vor(e);
    Simplex tmp;
    while (true) {
      bool found = true;
      if (s.dimension() == 2) {
        // s lies on a Voronoi edge
        Facet f=s;
        int index = f.first->index(v);
        for (int i=1; (i<4) && found; i++) {
          if (((f.second+i)&3) != index) {
            tmp = anchor_vor(Edge(f.first, index, (f.second+i)&3));
            found = (tmp == s);
          }
        }
      } else if (s.dimension() == 3) {
        //  s lies on a Voronoi vertex
        Cell_handle ch=s;
        CGAL_assertion(ch != Cell_handle());
        int index = ch->index(v);
        for (int i=1; (i<4) && (found); i++) {
          tmp = anchor_vor(Facet(ch, (index+i)&3));
          found = (tmp == s);
        }
      } else {
        CGAL_assertion(s.dimension() == 1);
      }
      if (found) {
        equiv_anchors.clear();
        if (s.dimension() == 1) {
          Edge e = s;
          Vertex_handle v_other = e.first->vertex(e.second+e.third-e.first->index(v));
          for (adj_vertex = adj_vertices.begin();
               adj_vertex != adj_vertices.end();
               adj_vertex++) {
            if ((v_other != (*adj_vertex)) && (!reg.is_infinite(*adj_vertex))) {
              CGAL_assertion(!reg.is_infinite(v));
              CGAL_assertion(!reg.is_infinite(v_other));
              CGAL_assertion(!reg.is_infinite(*adj_vertex));
              side = test_anchor(v->point(), v_other->point(),
                                 (*adj_vertex)->point());
              if (side==ZERO) {
                Edge e2;
                if (!reg.is_edge(v, *adj_vertex, e2.first, e2.second, e2.third)) {
                  CGAL_error();
                }
                equiv_anchors.push_back(e2);
              }
            }
          }
        }
        return s;
      }
      s = tmp;
    }
  }
}

template < class RegularTriangulation3 >
typename Compute_anchor_3<RegularTriangulation3>::Simplex
Compute_anchor_3<RegularTriangulation3>::compute_anchor_vor (Edge const &e)
{
  CGAL_assertion(!reg.is_infinite(e));

  equiv_anchors.clear();

  Vertex_handle v0 = e.first->vertex(e.second);
  Vertex_handle v1 = e.first->vertex(e.third);

  bool contains_center = true;
  Sign side;
  Simplex s;

  Facet_circulator fcir, fstart;
  fstart = fcir = reg.incident_facets(e);

  do {
    if (!reg.is_infinite(*fcir)) {
      int i = 6 - (*fcir).second -
              (*fcir).first->index(v0) - (*fcir).first->index(v1);
      side = test_anchor(*fcir, i);
      if (side == NEGATIVE) {
        contains_center = false;
        s = anchor_vor(Facet(*fcir));
      } else if (side == ZERO) {
        equiv_anchors.push_back(Facet(*fcir));
      }
    }
  } while (++fcir != fstart);

  if (contains_center) {
    s = Simplex(e);
    return s;
  } else {
    Simplex tmp;
    while (true) {
      bool found = true;
      if (s.dimension() == 3) {
        CGAL_assertion(s.dimension() == 3);
        Cell_handle ch=s;
        int index0 = ch->index(v0);
        int index1 = ch->index(v1);
        for (int i=0; (i<4) && found; i++) {
          if ((i != index0) && (i != index1)) {
            if (!reg.is_infinite(Facet(ch,i))) {
              side = test_anchor(ch,6-index0-index1-i);
              if (side != POSITIVE) {
                tmp = anchor_vor(Facet(ch,i));
                found = (s==tmp);
              }
            }
          }
        }
      } else {
        CGAL_assertion(s.dimension() == 2);
      }
      if (found) {
        equiv_anchors.clear();
        if (s.dimension() == 2) {
          // Check whether facet is degenerate (a line segment):
          Facet f = s;
          int index = 6 - f.second - f.first->index(v0) - f.first->index(v1);
          fstart = fcir = reg.incident_facets(e);
          do {
            if (!reg.is_infinite(*fcir)) {
              int index2 = 6 - (*fcir).second
                             - (*fcir).first->index(v0)
                             - (*fcir).first->index(v1);
              if (!(f.first->vertex(index) == (*fcir).first->vertex(index2))) {
                side = test_anchor(v0->point(), v1->point(),
                                   f.first->vertex(index)->point(),
                                   (*fcir).first->vertex(index2)->point());
                if (side == ZERO) {
                  equiv_anchors.push_back(Facet(*fcir));
                }
              }
            }
          } while (++fcir != fstart);
        }
        return s;
      }
      s = tmp;
    }
  }
}

template < class RegularTriangulation3 >
typename Compute_anchor_3<RegularTriangulation3>::Simplex
Compute_anchor_3<RegularTriangulation3>::compute_anchor_vor (Facet const &f)
{
  CGAL_assertion(!reg.is_infinite(f));
  equiv_anchors.clear();

  Sign side;

  CGAL_assertion(f.first != Cell_handle());
  if (!reg.is_infinite(f.first)) {
    side = test_anchor(f.first, f.second);
    if (side==NEGATIVE) {
      return Simplex(f.first);
    } else if (side == ZERO) {
      equiv_anchors.push_back(f.first);
    }
  }

  Cell_handle neighbor = f.first->neighbor(f.second);
  CGAL_assertion(neighbor != Cell_handle());
  if (!reg.is_infinite(neighbor)) {
    int n_index = neighbor->index(f.first);
    side = test_anchor(neighbor, n_index);
    if (side==NEGATIVE) {
      CGAL_assertion(equiv_anchors.empty());
      return Simplex(neighbor);
    } else if (side == ZERO) {
      equiv_anchors.push_back(neighbor);
    }
  }

  return Simplex(f);
}

} //namespace CGAL

#endif // CGAL_COMPUTE_ANCHOR_3_H
