#include "Scene_nef_polyhedron_item.h"
#include "Scene_polyhedron_item.h"
#include "Nef_type.h"
#include "Polyhedron_type.h"
#include <CGAL/Polyhedron_incremental_builder_3.h>
// #include <CGAL/OFF_to_nef_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/IO/Nef_polyhedron_iostream_3.h>
#include <CGAL/Inverse_index.h>

#include <QObject>
#include "Scene_nef_rendering.h"

#include <CGAL/minkowski_sum_3.h>
#include <CGAL/convex_decomposition_3.h> 


Scene_nef_polyhedron_item::Scene_nef_polyhedron_item()
  : Scene_item_with_display_list(),
    nef_poly(new Nef_polyhedron)
{
}

Scene_nef_polyhedron_item::Scene_nef_polyhedron_item(Nef_polyhedron* const p)
  : Scene_item_with_display_list(),
    nef_poly(p)
{
}

Scene_nef_polyhedron_item::Scene_nef_polyhedron_item(const Nef_polyhedron& p)
  : Scene_item_with_display_list(),
    nef_poly(new Nef_polyhedron(p))
{
}

// Scene_nef_polyhedron_item::Scene_nef_polyhedron_item(const Scene_nef_polyhedron_item& item)
//   : Scene_item_with_display_list(item),
//     nef_poly(new Nef_polyhedron(*item.nef_poly))
// {
// }

Scene_nef_polyhedron_item::~Scene_nef_polyhedron_item()
{
  delete nef_poly;
}

Scene_nef_polyhedron_item* 
Scene_nef_polyhedron_item::clone() const {
  return new Scene_nef_polyhedron_item(*nef_poly);
}

bool
Scene_nef_polyhedron_item::load_from_off(std::istream& in)
{
//   const std::size_t discarded = CGAL::OFF_to_nef_3(in, *nef_poly);
//   return discarded != 0;

  Exact_polyhedron exact_poly;
  in >> exact_poly;
  *nef_poly = Nef_polyhedron(exact_poly);

//   Polyhedron poly;
//   in >> poly;
//   *nef_poly = Nef_polyhedron(poly);
  return (bool) in;
}

QFont 
Scene_nef_polyhedron_item::font() const {
  QFont font;
  font.setItalic(!font.italic());
  return font;
}

bool
Scene_nef_polyhedron_item::load(std::istream& in)
{
  in >> *nef_poly;
  return (bool) in;
}

bool
Scene_nef_polyhedron_item::save(std::ostream& in) const
{
  in << *nef_poly;
  return (bool) in;
}

QString 
Scene_nef_polyhedron_item::toolTip() const
{
  if(!nef_poly)
    return QString();

  return QObject::tr("<p><b>%1</b> (mode: %5, color: %6)<br />"
                     "<i>Nef_3 polyhedron</i></p>"
                    "<p>Number of vertices: %2<br />"
                     "Number of edges: %3<br />"
                     "Number of facets: %4<br />"
                     "number of volumes: %7</p>")
    .arg(this->name())
    .arg(nef_poly->number_of_vertices())
    .arg(nef_poly->number_of_edges())
    .arg(nef_poly->number_of_facets())
    .arg(this->renderingModeName())
    .arg(this->color().name())
    .arg(nef_poly->number_of_volumes());
}

void
Scene_nef_polyhedron_item::direct_draw() const {
  gl_render_nef_facets(nef_poly);

  GLboolean lighting;
  glGetBooleanv(GL_LIGHTING, &lighting);
  glDisable(GL_LIGHTING);

  GLfloat point_size;
  glGetFloatv(GL_POINT_SIZE, &point_size);
  glPointSize(10.f);

  gl_render_nef_vertices(nef_poly);

  if(lighting) {
    glEnable(GL_LIGHTING);
  }
  glPointSize(point_size);
}

void
Scene_nef_polyhedron_item::draw_edges() const {
  gl_render_nef_edges(nef_poly);
}

Nef_polyhedron* 
Scene_nef_polyhedron_item::nef_polyhedron() {
  return nef_poly;
}

bool
Scene_nef_polyhedron_item::isEmpty() const {
  return (nef_poly == 0) || nef_poly->is_empty();
}

Scene_nef_polyhedron_item::Bbox
Scene_nef_polyhedron_item::bbox() const {
  if(isEmpty())
    return Bbox();
  CGAL::Bbox_3 bbox(nef_poly->vertices_begin()->point().bbox());
  for(Nef_polyhedron::Vertex_const_iterator it = nef_poly->vertices_begin();
      it != nef_poly->vertices_end();
      ++it) {
    bbox = bbox + it->point().bbox();
  }
  return Bbox(bbox.xmin(),bbox.ymin(),bbox.zmin(),
              bbox.xmax(),bbox.ymax(),bbox.zmax());
}

// quick hacks to convert polyhedra from exact to inexact and vice-versa
template <class Polyhedron_input,
class Polyhedron_output>
struct Copy_polyhedron_to 
  : public CGAL::Modifier_base<typename Polyhedron_output::HalfedgeDS>
{
  Copy_polyhedron_to(const Polyhedron_input& in_poly) 
    : in_poly(in_poly) {}

  void operator()(typename Polyhedron_output::HalfedgeDS& out_hds)
  {
    typedef typename Polyhedron_output::HalfedgeDS Output_HDS;

    CGAL::Polyhedron_incremental_builder_3<Output_HDS> builder(out_hds);

    typedef typename Polyhedron_input::Vertex_const_iterator Vertex_const_iterator;
    typedef typename Polyhedron_input::Facet_const_iterator  Facet_const_iterator;
    typedef typename Polyhedron_input::Halfedge_around_facet_const_circulator HFCC;

    builder.begin_surface(in_poly.size_of_vertices(),
      in_poly.size_of_facets(),
      in_poly.size_of_halfedges());

    for(Vertex_const_iterator
      vi = in_poly.vertices_begin(), end = in_poly.vertices_end();
      vi != end ; ++vi) 
    {
      typename Polyhedron_output::Point_3 p(::CGAL::to_double( vi->point().x()),
	::CGAL::to_double( vi->point().y()),
	::CGAL::to_double( vi->point().z()));
      builder.add_vertex(p);
    }

    typedef CGAL::Inverse_index<Vertex_const_iterator> Index;
    Index index( in_poly.vertices_begin(), in_poly.vertices_end());

    for(Facet_const_iterator 
      fi = in_poly.facets_begin(), end = in_poly.facets_end();
      fi != end; ++fi) 
    {
      HFCC hc = fi->facet_begin();
      HFCC hc_end = hc;
      //     std::size_t n = circulator_size( hc);
      //     CGAL_assertion( n >= 3);
      builder.begin_facet ();
      do {
	builder.add_vertex_to_facet(index[hc->vertex()]);
	++hc;
      } while( hc != hc_end);
      builder.end_facet();
    }
    builder.end_surface();
  } // end operator()(..)
private:
  const Polyhedron_input& in_poly;
}; // end Copy_polyhedron_to<>

template <class Poly_A, class Poly_B>
void copy_to(const Poly_A& poly_a, Poly_B& poly_b)
{
  Copy_polyhedron_to<Poly_A, Poly_B> modifier(poly_a);
  poly_b.delegate(modifier);
}

void from_exact(Exact_polyhedron& in,
		Polyhedron& out)
{
  copy_to(in, out);
  CGAL_assertion(out.is_valid());
}

void to_exact(Polyhedron& in,
	      Exact_polyhedron& out)
{
  copy_to(in, out);
  CGAL_assertion(out.is_valid());
}

bool Scene_nef_polyhedron_item::is_simple() const
{
  return nef_poly->is_simple();
}

// [static]
Scene_nef_polyhedron_item* 
Scene_nef_polyhedron_item::from_polyhedron(Scene_polyhedron_item* item)
{
  Polyhedron* poly = item->polyhedron();
  if(!poly) return 0;

  Exact_polyhedron exact_poly;
  to_exact(*poly, exact_poly);
  Nef_polyhedron* nef_poly = new Nef_polyhedron(exact_poly);
  exact_poly.clear();

  return new Scene_nef_polyhedron_item(nef_poly);
}

Scene_polyhedron_item*
Scene_nef_polyhedron_item::convert_to_polyhedron() const {
  Exact_polyhedron exact_poly;
  nef_poly->convert_to_Polyhedron(exact_poly);
  Polyhedron* poly = new Polyhedron;
  from_exact(exact_poly, *poly);
  exact_poly.clear();
  return new Scene_polyhedron_item(poly);
}

Scene_nef_polyhedron_item&
Scene_nef_polyhedron_item::
operator+=(const Scene_nef_polyhedron_item& other)
{
  (*nef_poly) += (*other.nef_poly);
  return *this;
}

Scene_nef_polyhedron_item&
Scene_nef_polyhedron_item::
operator*=(const Scene_nef_polyhedron_item& other)
{
  (*nef_poly) *= (*other.nef_poly);
  return *this;
}

Scene_nef_polyhedron_item&
Scene_nef_polyhedron_item::
operator-=(const Scene_nef_polyhedron_item& other)
{
  (*nef_poly) -= (*other.nef_poly);
  return *this;
}

Scene_nef_polyhedron_item*
Scene_nef_polyhedron_item::
sum(const Scene_nef_polyhedron_item& a, 
    const Scene_nef_polyhedron_item& b)
{
  return new Scene_nef_polyhedron_item(CGAL::minkowski_sum_3(*a.nef_poly,
                                                             *b.nef_poly));
}

void
Scene_nef_polyhedron_item::
convex_decomposition(std::list< Scene_polyhedron_item*>& convex_parts)
{
  // copy the Nef polyhedron, as markers are added
  Nef_polyhedron N(*nef_poly);
  CGAL::convex_decomposition_3(N);
  
  typedef Nef_polyhedron::Volume_const_iterator Volume_const_iterator;
  
  Volume_const_iterator ci = ++N.volumes_begin();
  for( ; ci != N.volumes_end(); ++ci) {
    if(ci->mark()) {
      Exact_polyhedron P;
      N.convert_inner_shell_to_polyhedron(ci->shells_begin(), P);
      Polyhedron* poly = new Polyhedron;
      from_exact(P, *poly);
      convex_parts.push_back(new Scene_polyhedron_item(poly));
    }
  }
}


#include "Scene_nef_polyhedron_item.moc"
