#include <Rcpp.h>
// Copyright (c) 2005-2008 Max-Planck-Institute Saarbruecken (Germany).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.1/Convex_decomposition_3/include/CGAL/Convex_decomposition_3/Single_wall_creator3.h $
// $Id: include/CGAL/Convex_decomposition_3/Single_wall_creator3.h 50cfbde3b84 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     :  Peter Hachenberger <hachenberger@mpi-sb.mpg.de>
#ifndef CGAL_CD3_SINGLE_WALL_CREATOR3_H
#define CGAL_CD3_SINGLE_WALL_CREATOR3_H

#include <CGAL/license/Convex_decomposition_3.h>

#include <CGAL/Modifier_base.h>
#include <CGAL/Nef_3/SNC_decorator.h>
#include <CGAL/Nef_3/SNC_point_locator.h>
#include <CGAL/Nef_3/SNC_intersection.h>
#include <CGAL/Nef_S2/Normalizing.h>
#include <CGAL/Convex_decomposition_3/Ray_hit_generator.h>
#include <CGAL/Convex_decomposition_3/SM_walls.h>

#undef CGAL_NEF_DEBUG
#define CGAL_NEF_DEBUG 229
#include <CGAL/Nef_2/debug.h>

namespace CGAL {

template<typename Nef_>
class Single_wall_creator3 : public Modifier_base<typename Nef_::SNC_and_PL> {

  typedef Nef_                                   Nef_polyhedron;
  typedef typename Nef_polyhedron::SNC_and_PL    SNC_and_PL;
  typedef typename Nef_polyhedron::SNC_structure SNC_structure;
  typedef typename SNC_structure::Items          Items;
  typedef CGAL::SNC_decorator<SNC_structure>     Base;
  typedef CGAL::SNC_point_locator<Base>          SNC_point_locator;
  typedef CGAL::SNC_intersection<SNC_structure>  SNC_intersection;
  typedef CGAL::SNC_constructor<Items, SNC_structure>
    SNC_constructor;

  typedef typename SNC_structure::Sphere_map     Sphere_map;
  typedef CGAL::SM_decorator<Sphere_map>         SM_decorator;
  typedef CGAL::SM_point_locator<SM_decorator>   SM_point_locator;
  typedef CGAL::SM_walls<Sphere_map>             SM_walls;
  typedef CGAL::Ray_hit_generator<Nef_polyhedron> Ray_hit;

  typedef typename Base::Segment_3               Segment_3;
  typedef typename Base::Point_3                 Point_3;
  typedef typename Base::Ray_3                   Ray_3;
  typedef typename Base::Vector_3                Vector_3;
  typedef typename Base::Sphere_point            Sphere_point;
  typedef typename Base::Sphere_circle           Sphere_circle;
  typedef typename Base::Sphere_segment          Sphere_segment;
  typedef typename Base::Vertex_handle           Vertex_handle;
  typedef typename Base::Halfedge_handle         Halfedge_handle;
  typedef typename Base::Halffacet_handle        Halffacet_handle;
  typedef typename Base::SVertex_handle          SVertex_handle;
  typedef typename Base::SHalfedge_handle        SHalfedge_handle;
  typedef typename Base::SHalfloop_handle        SHalfloop_handle;
  typedef typename Base::SFace_handle            SFace_handle;
  typedef typename Base::Object_handle           Object_handle;

  typedef typename Base::SVertex_iterator        SVertex_iterator;

  typedef typename Base::SHalfedge_around_svertex_circulator
    SHalfedge_around_svertex_circulator;

  Halfedge_handle ein;
  Sphere_circle c;
#ifndef CGAL_NEF_NO_INDEXED_ITEMS
  int index1, index2;
#endif
 public:
  Single_wall_creator3(SHalfedge_handle se)
    : ein(se->source()), c(se->circle().opposite())
#ifndef CGAL_NEF_NO_INDEXED_ITEMS
    , index1(0), index2(0)
#endif
    {}

 private:
  bool need_to_create_wall() const {
    CGAL_assertion(ein->point() == Sphere_point(1,0,0) ||
                   ein->twin()->point() == Sphere_point(1,0,0));

    SHalfedge_around_svertex_circulator svc(ein->out_sedge()), send(svc);
    CGAL_For_all(svc,send) {
      //      Rcpp::Rcerr << "check circles " << svc->circle() << std::endl;
      if(normalized(svc->circle()) == c &&
         !Sphere_segment(svc->source()->point(),
                         svc->twin()->source()->point(), c).is_long())
        return false;
    }
    return true;
  }

 public:
  void operator()(SNC_and_PL& sncpl) {

    CGAL_NEF_TRACEN( "Single_wall_creator3: ein " << ein->source()->point()
              << "->" << ein->twin()->source()->point() );
    CGAL_NEF_TRACEN( "double coords" << CGAL::to_double(ein->source()->point().x())
                     << ", " << CGAL::to_double(ein->source()->point().y())
                     << ", " << CGAL::to_double(ein->source()->point().z()) );
    CGAL_NEF_TRACEN( "double coords" << CGAL::to_double(ein->twin()->source()->point().x())
                     << ", " << CGAL::to_double(ein->twin()->source()->point().y())
                     << ", " << CGAL::to_double(ein->twin()->source()->point().z()) );

    if(!need_to_create_wall())
      return;

    SNC_structure* sncp(sncpl.sncp);
    SNC_point_locator* pl(sncpl.pl);

    //    if(ein->source()->point().hw() < ein->twin()->source()->point().hw())
    //      ein = ein->twin();

    SM_walls SMW_src(&*ein->source());
    SM_walls SMW_tgt(&*ein->twin()->source());

    Sphere_segment sphere_ray_src(ein->point(), ein->twin()->point(), c);
    Sphere_segment sphere_ray_tgt(ein->twin()->point(), ein->point(), c.opposite());
    SVertex_handle lateral_sv_tgt[2];
    lateral_sv_tgt[0] = SMW_src.add_lateral_svertex(sphere_ray_src);
    lateral_sv_tgt[1] = SMW_tgt.add_lateral_svertex(sphere_ray_tgt);
    CGAL_NEF_TRACEN("lateral_sv_tgt " << lateral_sv_tgt[0]->point() <<
                    ", " << lateral_sv_tgt[1]->point());

    CGAL_assertion(sphere_ray_src.sphere_circle() ==
                   sphere_ray_tgt.sphere_circle().opposite());
    CGAL_assertion(sphere_ray_src.sphere_circle() == c);

#ifndef CGAL_NEF_NO_INDEXED_ITEMS
    CGAL_assertion(!Sphere_segment(ein->point(),
                                   lateral_sv_tgt[0]->point(),
                                   sphere_ray_src.sphere_circle()).is_long());
    CGAL_assertion(!Sphere_segment(ein->twin()->point(),
                                   lateral_sv_tgt[1]->point(),
                                   sphere_ray_tgt.sphere_circle()).is_long());
    SMW_src.add_sedge_between(ein, lateral_sv_tgt[0], index1, index2, sphere_ray_src.sphere_circle());
    SMW_tgt.add_sedge_between(ein->twin(), lateral_sv_tgt[1], index2, index1, sphere_ray_tgt.sphere_circle());
#else
    CGAL_assertion(!Sphere_segment(ein->point(),
                                   lateral_sv_tgt[0]->point(),
                                   sphere_ray_src.sphere_circle()).is_long());
    CGAL_assertion(!Sphere_segment(ein->twin()->point(),
                                   lateral_sv_tgt[1]->point(),
                                   sphere_ray_tgt.sphere_circle()).is_long());
    SMW_src.add_sedge_between(ein, lateral_sv_tgt[0], sphere_ray_src.sphere_circle());
    SMW_tgt.add_sedge_between(ein->twin(), lateral_sv_tgt[1], sphere_ray_tgt.sphere_circle());
#endif
    Ray_hit rh(sncp, pl, 3);

    do {
      Ray_3 r(lateral_sv_tgt[0]->source()->point(),lateral_sv_tgt[0]->point()-CGAL::ORIGIN);

      CGAL_NEF_TRACEN( "nachbehandlung: ray " << r );
      CGAL_NEF_TRACEN( "double coords" << CGAL::to_double(r.source().x())
                << ", " << CGAL::to_double(r.source().y())
                << ", " << CGAL::to_double(r.source().z())
                << "->" << CGAL::to_double(r.to_vector().x())
                << ", " << CGAL::to_double(r.to_vector().y())
                << ", " << CGAL::to_double(r.to_vector().z()) );

      Vertex_handle v = rh.create_vertex_on_first_hit(r);

      if(v == lateral_sv_tgt[1]->source()) {
        lateral_sv_tgt[0]->twin() = lateral_sv_tgt[1];
        lateral_sv_tgt[1]->twin() = lateral_sv_tgt[0];
#ifndef CGAL_NEF_NO_INDEXED_ITEMS
        lateral_sv_tgt[1]->set_index(lateral_sv_tgt[0]->new_index());
#endif
        pl->add_edge(lateral_sv_tgt[0]);
        return;
      }

      SM_walls SMW_tgt(&*v);
      SVertex_handle opp = SMW_tgt.add_ray_svertex(lateral_sv_tgt[0]->point().antipode());
      opp->twin() = lateral_sv_tgt[0];
      lateral_sv_tgt[0]->twin() = opp;
      pl->add_edge(opp);
#ifndef CGAL_NEF_NO_INDEXED_ITEMS
      lateral_sv_tgt[0]->set_index(opp->new_index());
#endif
      lateral_sv_tgt[0] =
        SMW_tgt.add_lateral_svertex(Sphere_segment(lateral_sv_tgt[0]->point().antipode(),
                                                   lateral_sv_tgt[0]->point(),c));
#ifndef CGAL_NEF_NO_INDEXED_ITEMS
      CGAL_assertion(!Sphere_segment(opp->point(), lateral_sv_tgt[0]->point(), c).is_long());
      SMW_tgt.add_sedge_between(opp, lateral_sv_tgt[0], index1, index2, c);
#else
      CGAL_assertion(!Sphere_segment(opp->point(), lateral_sv_tgt[0]->point(), c).is_long());
      SMW_tgt.add_sedge_between(opp, lateral_sv_tgt[0], c);
#endif

    } while(true);
  }
};

} //namespace CGAL
#endif //CGAL_CD3_SINGLE_WALL_CREATOR3_H

