#include <Rcpp.h>
// Copyright (c) 2005  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/Nef_3/include/CGAL/OFF_to_nef_3.h $
// $Id: include/CGAL/OFF_to_nef_3.h 50cfbde3b84 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Ralf Osbild <osbild@mpi-sb.mpg.de>

#ifndef CGAL_OFF_TO_NEF_3_H
#define CGAL_OFF_TO_NEF_3_H

#include <CGAL/license/Nef_3.h>

#include <CGAL/disable_warnings.h>

#include <CGAL/Nef_polyhedron_3.h>



// --- begin preliminary number type converter -----------------

#ifndef CGAL_NUMBER_TYPE_CONVERTER_NEF_3_H
#define CGAL_NUMBER_TYPE_CONVERTER_NEF_3_H

#include<sstream>
#include <CGAL/Exact_rational.h>
#include <CGAL/Exact_integer.h>
#include <CGAL/Fraction_traits.h>
#include<CGAL/Cartesian.h>
#include<CGAL/Homogeneous.h>
#include<CGAL/Nef_S2/Normalizing.h>
#include<CGAL/Nef_nary_union_3.h>

namespace CGAL {


typedef CGAL::Exact_integer Integer;
typedef CGAL::Exact_rational Rational;

class Homogeneous_tag;
class Cartesian_tag;
template<typename Tag, typename Kernel> class number_type_converter_nef_3;

template<class Kernel>
class number_type_converter_nef_3<Homogeneous_tag, Kernel> {
 public:
  static typename Kernel::Point_3
    convert (const CGAL::Cartesian<double>::Point_3 &d)
  {
    typedef typename Kernel::Point_3   Point_3;
    typedef typename Kernel::RT        RT;

    typedef Fraction_traits<Rational> F_traits;

    Rational x(d.x()), y(d.y()), z(d.z());
    Integer xn, xd, yn, yd, zn, zd;
    typename F_traits::Decompose decompose;
    decompose(x, xn, xd);
    decompose(y, yn, yd);
    decompose(z, zn, zd);
      CGAL::Homogeneous<Integer>::Point_3 b =
        normalized ( CGAL::Homogeneous<Integer>::Point_3 (
         xn * yd * zd,
         xd * yn * zd,
         xd * yd * zn,
         xd * yd * zd ) );

      std::ostringstream outx, outy, outz, outw;
      outx << b.hx();
      outy << b.hy();
      outz << b.hz();
      outw << b.hw();
      RT rx (outx.str().c_str());
      RT ry (outy.str().c_str());
      RT rz (outz.str().c_str());
      RT rw (outw.str().c_str());

      return Point_3 (rx, ry, rz, rw);
  }
};

template<class Kernel>
class number_type_converter_nef_3<Cartesian_tag, Kernel> {
 public:
  static typename Kernel::Point_3
    convert (const CGAL::Cartesian<double>::Point_3 &d)
  {  return typename Kernel::Point_3(d.x(), d.y(), d.z());
  }
};

} //namespace CGAL
#endif // NUMBER_TYPE_CONVERTER_NEF_3_H

// --- end preliminary number type converter -------------------

#include <iostream>
#include <map>
#include <vector>

#include <CGAL/Cartesian.h>
#include <CGAL/Nef_3/Mark_bounded_volumes.h>
#include <CGAL/IO/OFF.h>
#include <CGAL/normal_vector_newell_3.h>

#ifdef CGAL_NEF_OFF_TO_NEF_TIMER
#include <CGAL/Timer.h>
#endif

namespace CGAL {

template <class Nef_3>
std::size_t
OFF_to_nef_3 (std::istream &i_st, Nef_3 &nef_union, bool verb=false)
{
   // Nef
   typedef typename Nef_3::Kernel          Kernel;
   typedef typename Nef_3::Point_3         Point_3;
   typedef typename std::vector<Point_3>   Point_set;
   CGAL::Nef_nary_union_3<Nef_3>            nary_union;
   // input data structure
   typedef double                           Scan_NT;
   typedef CGAL::Cartesian<Scan_NT>        Scan_kernel;
   typedef Scan_kernel::Point_3            Scan_point;
   typedef std::vector<Scan_point>         Scan_point_set;
   typedef Scan_kernel::Vector_3           Scan_vector;

   typedef CGAL::Scanner_OFF<Scan_kernel>  Scan_OFF;
   typedef Scan_OFF::Vertex_iterator       Scan_vertex_it;
   typedef Scan_OFF::Facet_iterator        Scan_facet_it;
   typedef Scan_OFF::Index_iterator        Scan_index_it;

   typedef typename Kernel::Kernel_tag Kernel_tag;
   typedef typename CGAL::number_type_converter_nef_3<Kernel_tag,Kernel> ntc;

   // declarations and defaults
   std::size_t discarded_facets=0;
   std::size_t idx;

#ifdef CGAL_NEF_OFF_TO_NEF_TIMER
   CGAL::Timer t_convert, t_union;
#endif

   // input: description of polyhedron in object file format (OFF)
   // with cartesian double coordinates
   Scan_OFF scan (i_st);
   std::size_t NOV = scan.size_of_vertices();

   // read and store vertices
   Scan_point_set V_scan;
   V_scan.reserve (NOV);
   Point_set V;
   V.reserve (NOV);

#ifdef CGAL_NEF_OFF_TO_NEF_TIMER
   t_convert.start();
#endif

   Scan_vertex_it v_it = scan.vertices_begin();
   for (idx=0; v_it != scan.vertices_end(); ++v_it, ++idx)
   {  V_scan.push_back (*v_it);
     V.push_back (ntc::convert(*v_it));
   }
   CGAL_warning ( idx==NOV );
   NOV = idx;

   // for each facet
   Scan_facet_it f_it = scan.facets_begin();
   for (idx=0; f_it != scan.facets_end(); ++f_it, ++idx)
   {  // read facet
      Scan_facet_it::indices_size_type NOI=f_it.size_of_indices(), jdx;
      Scan_point_set V_f_scan;
      V_f_scan.reserve(NOI);
      Point_set V_f;
      V_f.reserve(NOI);

      Scan_index_it ind_it = f_it->begin();
      for (jdx=0; ind_it != f_it->end(); ++ind_it, ++jdx)
      {  // assertion: index out of range?
         CGAL_assertion (*ind_it < NOV );
         V_f_scan.push_back (V_scan[*ind_it]);
         V_f.push_back (V[*ind_it]);
      }
      CGAL_warning ( jdx==NOI );
      NOI = jdx;

      bool is_nef = false;
      CGAL_assertion_msg( V_f.size() >= 1 || !verb, "empty vertex cycle");
      if ( V_f.size() >= 1 )
      {  // compute Newell vector <double>
         Scan_vector normal;
         normal_vector_newell_3(V_f_scan.begin(),V_f_scan.end(),normal);

         // construct and enqueue Nef_polyhedron_3 <Kernel>
         Nef_3 nef (V_f.begin(), V_f.end(), normal, verb);
         if ( !nef.is_empty() )
           {nary_union.add_polyhedron(nef);
            is_nef = true;
         }
      }

      if ( !is_nef )
      {  ++discarded_facets;
         if (verb)
         {  Rcpp::Rcerr << "Hence, discard input facet " << (idx+1)
               << " (enumerated beginning with 1)."
               << " Check semantics!\n" << std::endl;
         }
      }
   }

#ifdef CGAL_NEF_OFF_TO_NEF_TIMER
   t_convert.stop();
   Rcpp::Rcout << "time (conversion): " << t_convert.time()<< std::endl;
#endif

   // union of queue entries

#ifdef CGAL_NEF_OFF_TO_NEF_TIMER
   t_union.start();
#endif

#ifdef CGAL_NEF_OFF_TO_NEF_TIMER
   t_union.stop();
   Rcpp::Rcout << "time (union): " << t_union.time() << "\n" << std::endl;
#endif

   // return values
   //   if ( nef_map.size() == 0 ) nef_union = Nef_3 ();
   //   else
   nef_union = nary_union.get_union();
   CGAL::Mark_bounded_volumes<Nef_3> mbv (true);
   nef_union.delegate (mbv);

   return discarded_facets;
}

} //namespace CGAL

#include <CGAL/enable_warnings.h>

#endif // CGAL_OFF_TO_NEF_3_H

