//=============================================================================
//                                                                            
//                               OpenMesh                                     
//      Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen       
//                           www.openmesh.org                                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//                                License                                     
//                                                                            
//   This library is free software; you can redistribute it and/or modify it 
//   under the terms of the GNU Library General Public License as published  
//   by the Free Software Foundation, version 2.                             
//                                                                             
//   This library is distributed in the hope that it will be useful, but       
//   WITHOUT ANY WARRANTY; without even the implied warranty of                
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         
//   Library General Public License for more details.                          
//                                                                            
//   You should have received a copy of the GNU Library General Public         
//   License along with this library; if not, write to the Free Software       
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//   $Revision: 1.2 $
//   $Date: 2005-12-21 14:00:11 $
//                                                                            
//=============================================================================


#include <iostream>

#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/Types/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Tools/Decimater/DecimaterT.hh>
#include <OpenMesh/Tools/Decimater/ModQuadricT.hh>
#include <OpenMesh/Tools/Decimater/ModNormalFlippingT.hh>
#include <OpenMesh/Tools/Decimater/ModRoundnessT.hh>
#include <OpenMesh/Tools/Decimater/ModProgMeshT.hh>
#include <OpenMesh/Tools/Utils/getopt.h>
#include <OpenMesh/Tools/Utils/Timer.hh>

#include <iostream>
#include <set>
#include <float.h>



//-----------------------------------------------------------------------------


struct MyTraits : public OpenMesh::DefaultTraits
{
  HalfedgeAttributes(OpenMesh::Attributes::PrevHalfedge);
};
typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits>  MyMesh;



//-----------------------------------------------------------------------------


void usage_and_exit()
{
  std::cout << std::endl;
  std::cout << "Usage:\ndecimater [options] <input> <output>\n";
  std::cout << "Options:\n"
	    << "  -q <error>      Quadric error threshold\n"
            << "  -r <roundness>  Roundness of triangles in [0..1]\n"
            << "  -n <angle>      Change of triangle normals per collapse\n"
            << "  -v <#vertices>  Target complexity\n"
            << "  -p              Write progressive mesh to <output<\n"
	    << std::endl;
  exit(1);
}


//-----------------------------------------------------------------------------


int main(int argc, char **argv)
{
  MyMesh  mesh;


  int    c;
  float  error        = -1.0;
  float  roundness    = -1.0;
  float  normal_angle = -1.0;
  int    complexity   = 0;
  bool   progmesh     = false;

  while ((c=getopt(argc, argv, "q:r:n:v:ph")) != -1)
  {
    switch(c)
    {
      case 'q':
	error = (float) atof(optarg);
	break;

      case 'r':
	roundness = (float) atof(optarg);
	break;

      case 'n':
	normal_angle = (float) atof(optarg);
	break;

      case 'v':
	complexity = atoi(optarg);
	break;
	
      case 'p':
	progmesh = true;
	break;
	
      case 'h':
      default:
	usage_and_exit();
	break;
    }
  }


  if (argc - optind < 2)
    usage_and_exit();



  // read mesh
  if (!OpenMesh::IO::read_mesh(mesh, argv[optind]))
  {
    std::cerr << "Read error\n";
    exit(1);
  }


  // setup decimater
  typedef OpenMesh::Decimater::DecimaterT<MyMesh>  Decimater;
  OpenMesh::Decimater::ModQuadricT<Decimater>::Handle          modQ;
  OpenMesh::Decimater::ModNormalFlippingT<Decimater>::Handle   modN;
  OpenMesh::Decimater::ModRoundnessT<Decimater>::Handle        modR;
  OpenMesh::Decimater::ModProgMeshT<Decimater>::Handle         modP;

  Decimater deci(mesh);

  deci.add_priority(modQ);
  if (error >= 0.0) 
  {
    deci.module(modQ).set_max_err(error);
  }

  if (roundness >= 0.0)
  {
    deci.add_binary(modR);
    deci.module(modR).set_min_roundness(roundness);
  }

  if (normal_angle >= 0.0)
  {
    deci.add_binary(modN);
    deci.module(modN).set_max_normal_deviation(normal_angle);
  }

  if (progmesh != 0)
  {
    deci.add_binary(modP);
  }




  // decimate
  std::cerr << "Decimating...";
  OpenMesh::Utils::Timer  timer;
  timer.start();
  deci.initialize();
  deci.decimate_to( complexity );
  timer.stop();
  std::cerr << "done (" << timer.as_string() << ")\n";


  // write progressive mesh (has to be done before garbage_collection!)
  if (progmesh)
  {
    std::cerr << "Writing progmesh...";
    deci.module(modP).write(argv[optind+1]);
    std::cerr << "done\n";
  }

  // write standard mesh (do garbage_collection first!)
  else
  {
    mesh.garbage_collection();
    std::cerr << "Writing...";
    OpenMesh::IO::write_mesh(mesh, argv[optind+1]);
    std::cerr << "done\n";
  }
}


//=============================================================================
