//-------------------------------------------------------------------------
//===================================================================
//=   CGAL Mesh Simplification Code Modified for iso2mesh toolbox   =
//===================================================================
//
//Modified by Qianqian Fang <fangq at nmr.mgh.harvard.edu>
//patches from
//  Fernando Cacciola <fernando.cacciola at geometryfactory.com>
//  Andreas Fabri <andreas.fabri at geometryfactory.com>
//
//Date: 2008/03
//
//-------------------------------------------------------------------------

#include <iostream>
#include <fstream>

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>

// Adaptor for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>// AF: added
//#include <CGAL/Surface_mesh_simplification/HalfedgeGraph_Polyhedron_3.h> // AF: removed

// Simplification function
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>

// Extended polyhedron items which include an id() field
#include <CGAL/Polyhedron_items_with_id_3.h>

// Stop-condition policy
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_ratio_stop_predicate.h>

// Default cost and placement policies
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/LindstromTurk_placement.h>

// Non-default cost and placement policies
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_length_cost.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Midpoint_placement.h>

typedef CGAL::Simple_cartesian<double> Kernel;

typedef Kernel::Point_3 Point ;

//
// Setup an enriched polyhedron type which stores an id() field in the items
//
typedef CGAL::Polyhedron_3<Kernel,CGAL::Polyhedron_items_with_id_3> Surface;

typedef Surface::Halfedge_handle Halfedge_handle ;

namespace SMS = CGAL::Surface_mesh_simplification ;

typedef SMS::Edge_profile<Surface> Profile ;


// The following is a Visitor that keeps track of the simplification process.
// In this example the progress is printed real-time and a few statistics are
// recorded (and printed in the end).
//
struct Visitor
{
  Visitor()
    : collected(0)
    , processed(0)
    , collapsed(0)
    , non_collapsable(0)
    , cost_uncomputable(0)
    , placement_uncomputable(0)
  {}

  // Called on algorithm entry
  void OnStarted( Surface& ) {}

  // Called on algorithm exit
  void OnFinished ( Surface& ) { std::cerr << "\n" << std::flush ; }

  // Called when the stop condition returned true
  void OnStopConditionReached( Profile const& ) {}

  // Called during the collecting phase for each edge collected.
  template <class Optional>
  void OnCollected( Profile const&, Optional const& )
  {
    ++ collected ;
//    std::cerr << "\rEdges collected: " << collected << std::flush ;
  }

  // Called during the processing phase for each edge selected.
  // If cost is absent the edge won't be collapsed.
  template <class Optional>
  void OnSelected(Profile const&
                 ,Optional cost
                 ,std::size_t             initial
                 ,std::size_t             current
                 )
  {
    ++ processed ;
    if ( !cost )
      ++ cost_uncomputable ;
 /*
    if ( current == initial )
      std::cerr << "\n" << std::flush ;
    std::cerr << "\r" << current << std::flush ;
 */
  }

  // Called during the processing phase for each edge being collapsed.
  // If placement is absent the edge is left uncollapsed.
  template <class Optional>
  void OnCollapsing(Profile const&
                   ,Optional  placement
                   )
  {
    if ( placement )
         ++ collapsed;
    else ++ placement_uncomputable ;
  }

  // Called for each edge which failed the so called link-condition,
  // that is, which cannot be collapsed because doing so would
  // turn the surface into a non-manifold.
  void OnNonCollapsable( Profile const& )
  {
    ++ non_collapsable;
  }

  // AF: added
  void OnCollapsed(Profile const&, Surface::Vertex_handle)
  {}

  std::size_t  collected
             , processed
             , collapsed
             , non_collapsable
             , cost_uncomputable
             , placement_uncomputable ;
} ;

template<class GetCost_>
struct Cost_with_fixed_edges : GetCost_
{
 typedef GetCost_ GetCost ;

  // typedef typename GetCost::Profile     Profile ; // AF Profile no longer nested type
  //typedef typename GetCost::Point       Point ; // AF
  //typedef typename GetCost::result_type result_type ; // AF

template <typename Profile, typename T> // AF: make it a template
auto
operator()(
Profile const& aProfile,
T const& aPlacement ) const // replace result_type
{
    using Opt = decltype(this->GetCost::operator()(aProfile, aPlacement));
    if ( aProfile.border_edges().size() > 0 )
          return Opt() ;
    else
         return this->GetCost::operator()(aProfile, aPlacement);
}
} ;

typedef Cost_with_fixed_edges< SMS::Edge_length_cost<Surface> > My_cost ;
typedef Cost_with_fixed_edges< SMS::LindstromTurk_cost<Surface> > LT_cost ;


int main( int argc, char** argv )
{
  Surface surface;
  float maxface=0.1;
  int defaultpolicy=0;

  printf("= Surface Mesh Simplification Utility (Based on CGAL %s) =\n(modified for iso2mesh by Qianqian Fang, Fernando Cacciola and Andreas Fabri)\nhttp://iso2mesh.sf.net\n\n",CGAL_VERSION_STR);
  if(argc<2){
  	printf("command options\n\t%s input.off keepratio\nFor example:\n\t%s input.off 0.1\n",argv[0],argv[0]);
	exit(0);
  }
  std::ifstream is(argv[1]) ; is >> surface ;
  if(argc>2) maxface=atof(argv[2]);
  if(maxface<0){
      defaultpolicy=1;
      maxface=-maxface;
  }

  printf("max face ratio=%f\n",maxface);


  // The items in this polyhedron have an "id()" field
  // which the default index maps used in the algorithm
  // need to get the index of a vertex/edge.
  // However, the Polyhedron_3 class doesn't assign any value to
  // this id(), so we must do it here:
  int index = 0 ;

  for( Surface::Halfedge_iterator eb = surface.halfedges_begin()
     , ee = surface.halfedges_end()
     ; eb != ee
     ; ++ eb
     )
    eb->id() = index++;

  printf("edge index number %d\n",index);


  index = 0 ;
  for( Surface::Vertex_iterator vb = surface.vertices_begin()
     , ve = surface.vertices_end()
     ; vb != ve
     ; ++ vb
     )
    vb->id() = index++;

  // In this example, the simplification stops when the number of undirected edges
  // drops below 10% of the initial count
  SMS::Count_ratio_stop_predicate<Surface> stop(maxface);

  Visitor vis ;

  // The index maps are not explicitelty passed as in the previous
  // example because the surface items have a proper id() field.
  // On the other hand, we pass here explicit cost and placement
  // function which differ from the default policies, ommited in
  // the previous example.

  printf("mesh simplificaton in progress ...\n");

  int r;
  if(defaultpolicy)
      r = SMS::edge_collapse
           (surface
           ,stop
           ,CGAL::parameters::get_cost     (LT_cost())
                 .get_placement   (SMS::LindstromTurk_placement<Surface>())
            .visitor(vis)  // AF: not a pointer
           );
  else
      r = SMS::edge_collapse
           (surface
           ,stop
           ,CGAL::parameters::get_cost     (My_cost())
                 .get_placement   (SMS::Midpoint_placement<Surface>())
                 .visitor(vis)
           );

  std::cout << "\nEdges collected: " << vis.collected
            << "\nEdges proccessed: " << vis.processed
            << "\nEdges collapsed: " << vis.collapsed
            << std::endl
            << "\nEdges not collapsed due to topological constrians: "
            << vis.non_collapsable
            << "\nEdge not collapsed due to cost computation constrians: "
            << vis.cost_uncomputable
            << "\nEdge not collapsed due to placement computation constrians: "
            << vis.placement_uncomputable
            << std::endl ;

  std::cout << "\nFinished...\n" << r << " edges removed.\n"
            << (surface.size_of_halfedges()/2) << " final edges.\n" ;

  std::ofstream os( argc > 3 ? argv[3] : "out.off" ) ; os << surface ;

  return 0 ;
}



// EOF //

