// Cutmesh - Copyright (C) 2010-2018 T. Mouton, E. Bechet 
//
// See the LICENSE file for license information and contributions.
// bugs and problems to <thibaud.mouton@gmail.com>.

#ifndef _COMMON_CUTMESH_H_
#define _COMMON_CUTMESH_H_

#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <list>
#include <iterator>
#include <queue>
#include <deque>
#include <stack>
#include <algorithm>
#include <set>
#include <math.h>
#include <assert.h>
#include <limits>
#include <climits>


#define DEBUG_LEVEL 0

// macro tprintf
#define tprintf(...) printf("### %lf ### ", clock ()*1e-6); printf(__VA_ARGS__)
// #define tprintf(...) printf("### "); printf(__VA_ARGS__)
// fin de macro

// macro dbgprintf
#if DEBUG_LEVEL > 0
#define dbgprintf(x, ...) if(x<=DEBUG_LEVEL) printf(__VA_ARGS__)
#else
#define dbgprintf(x, ...)
#endif
// fin de macro

// #define NDEBUG
#define THRESHOLD 1e-4
#define THRESHOLD_VOLUME 1e-6
#define THRESHOLD_BARY_FACE 1e-3

#define TOL 1e-6
#define NEW_PHYSICAL_NUMBERING
#define NOPROGRESS

#define DEFAULT_MAX_SURF_SAMPLES 50
#define DEFAULT_ENLARGE_MESH_FACTOR 0.05
#define DEFAULT_MAX_MESH_SIZE 5
#define DEFAULT_MAX_REFINEMENT_LEVEL 3
#define DEFAULT_REFINEMENT_ERROR_THRESHOLD 1e-2

#define MAJOR_VERSION 1
#define MINOR_VERSION 0

class options
{
  public :
    int read_meshfile;
    int read_lsfile;
    int read_edges;
    int read_fiber_surface;
    int load_gmodel_mesh;
    int load_ibrep;
    int read_topo_file;
    int generate_mesh;
    int max_surf_sample;
    int max_mesh_size;
    double enlarge_mesh_factor;
    int compute_levelsets;
    int refine_mesh_curvature;
    int max_refinement_level;
    double refinement_error_threshold;
    int load_gmodel_geometry;
    int brep_topo;
    int auto_topo;
    int only_valid_topo;
    int recut_enable;
    int write_gmodel;
    int library_mode;
    int debug;
    char basename[100];
    int major_version;
    int minor_version;
    double mesh_dotmin_merge_surface;
    double mesh_distmin_merge_surface;

  public :
    options()
    {
      load_gmodel_mesh = 0;
      load_gmodel_geometry = 0;
      read_edges = 0;
      read_fiber_surface = 0;
      load_ibrep = 0;
      read_meshfile = 0;
      read_lsfile = 0;
      read_topo_file = 0;
      generate_mesh = 0;
      max_surf_sample = DEFAULT_MAX_SURF_SAMPLES;
      enlarge_mesh_factor = DEFAULT_ENLARGE_MESH_FACTOR;
      max_mesh_size = DEFAULT_MAX_MESH_SIZE;
      compute_levelsets = 0;
      refine_mesh_curvature = 0;
      max_refinement_level = DEFAULT_MAX_REFINEMENT_LEVEL;
      refinement_error_threshold = DEFAULT_REFINEMENT_ERROR_THRESHOLD;
      brep_topo = 0;
      only_valid_topo = 0;
      auto_topo = 0;
      recut_enable = 0;
      write_gmodel = 0;
      library_mode = 0;
      debug = 0;
      mesh_dotmin_merge_surface = -INFINITY;
      mesh_distmin_merge_surface = -INFINITY;
      basename[0] = '\0';
      major_version = MAJOR_VERSION;
      minor_version = MINOR_VERSION;
    }
    void usage ( const char *program_name );
    void parse ( int argc, char **argv );
    void presets ( int num );
    void use_as_library()
    {
      library_mode = 1;
    }
};


class adouble
{

  protected:
    int _id;
    double _v;

    friend bool operator< ( adouble a, adouble b )
    {
      return ( a.val() < b.val() ? true : false );
    }

  public:

    adouble ( int i, double v )
    {
      _id = i;
      _v = v;
    }
    inline void set ( int i, double v )
    {
      _id = i;
      _v = v;
    }
    inline int id()
    {
      return _id;
    }
    inline double val()
    {
      return _v;
    }
};

class progress_bar
{

  protected:
    int _max;
    int _inc;
    int _last;

  public:

    progress_bar()
    {
      _max = 100;
      _inc = 1;
      _last = 0;
    }
    progress_bar ( int max )
    {
      _max = max;
      _inc = 1;
      _last = 0;
      if ( _max > 100 )
        _inc = floor ( ( double ) max/100.0 );
    }

    inline void progress ( int i )
    {
#if defined(NOPROGRESS)
      return;
#endif
      if ( i >=_last )
      {
        _last += _inc;
        printf ( "\033[M\033[A\033[M" );
        int percent = ( int ) ( ( double ) i/ ( double ) _max*100.0 );
        printf ( "%d %% : ", percent );
        for ( int j = 0; j < percent; j++ )
          printf ( "#" );
        printf ( "\n" );
      }
    }

    inline void start()
    {
#if defined(NOPROGRESS)
      return;
#endif
      printf ( "\n" );
    }

    inline void start ( int max )
    {
#if defined(NOPROGRESS)
      return;
#endif
      _max = max;
      _inc = 1;
      _last = 0;
      if ( _max > 100 )
        _inc = floor ( ( double ) max/100.0 );
      printf ( "\n" );
    }

    inline void end()
    {
#if defined(NOPROGRESS)
      return;
#endif
      printf ( "\033[M\033[A\033[M" );
    }
};

class randomize
{
  protected:
    std::vector<int> _rand;
  public:
    randomize ( int begin, int inc, int size )
    {
      for ( int i = 0; i < size; i++ )
        _rand.push_back ( begin+i*inc );
      srand ( 0 );
      for ( int i = 0; i < 100*size; i++ )
      {
        int i0 = rand() % size;
        int i1 = rand() % size;
        int tmp = _rand[i0];
        _rand[i0] = _rand[i1];
        _rand[i1] = tmp;
      }
    }
    inline int get ( int i )
    {
      printf ( "return %d\n", _rand[i] );
      return _rand[i];
    }
};

// class Histogram
// {
// protected:
//     double _min;
//     double _max;
//     int _step;
//     double _refsize;
//     std::vector<int> _val;
//     int _tot;
//     int _out;
//
//   public :
//     Histogram()
//     {
//       _min = 0.0;
//       _max = 0.0;
//       _step = 0;
//       _refsize = 0.0;
//       _tot = 0;
//       _out = 0;
//     }
//     inline void set_interval(double min, double max)
//     {
//       _min = min;
//       _max = max;
//     }
//     inline void set_subdivision(int subdivision)
//     {
//       _step = subdivision;
//       _refsize = (_max - _min)/(double)_step;
//       _val.resize(_step);
//     }
//     inline void add_value(double val)
//     {
//       _tot++;
//       if (val < _min || val > _max)
//       {
//           _out++;
//           return;
//       }
//       _val[floor((val-_min)/_refsize)]++;
//     }
//     inline void dump()
//     {
//         int percent = (int)((double)i/(double)max*100.0);
//         printf("%d %% : ", percent);
//         for (int i = 0; i < _val.s; i++)
//             printf("#");
//     }
//
// };

// class Counter
// {
// protected:
//     std::vector<int> _c;
//     int _gs;
//     std::vector<int> _diff;
//     int _min;
//     int _max;
//
// public:
//     Counter(int initial_size, int grow_size)
//     {
//         _c.resize(initial_size, 0);
//         _gs = grow_size;
//     }
//     void add(int i)
//     {
//         if (_c.size() <= i)
//             _c.resize(i+_gs, 0);
//         if (_c[i] == 0)
//             _diff.push_back(i);
//         _c[i]++;
//     }
//     void reset()
//     {
//         _c.assign(_c.size(), 0);
//         _diff.assign(_diff.size(), 0);
//     }
//     int nb_different()
//     {
//         return _diff.size();
//     }
//     int min_value()
//     {
//       _min = _diff[0];
//      for(int i = 1; i < _diff.size(); i++)
//        if (_diff[i] < _min)
//          _min = _diff[i];
//         return _min;
//     }
//     int max_value()
//     {
//        _max = _diff[0];
//      for(int i = 1; i < _diff.size(); i++)
//        if (_diff[i] > _max)
//          _max = _diff[i];
//         return _max;
//     }
//     std::vector<int> list()
//     {
//         return _diff;
//     }
//
// };

// void dump_tetra(LTetra *t)
// {
//     char filename[100];
//     sprintf(filename, "dump_tetra_%d.vtk", t->getId());
//     tprintf("Writing %s ....\n", filename);
//     FILE *fp = fopen(filename, "w");
//
//     fprintf (fp, "# vtk DataFile Version 2.0\n");
//     fprintf (fp, "Really cool data\n");
//     fprintf (fp, "ASCII\n");
//     fprintf (fp, "DATASET UNSTRUCTURED_GRID\n");
//
//     printf("LTetra %d --> %d %d %d %d\n", t->getIndex(), t->getVertex(0)->getIndex(), t->getVertex(1)->getIndex(), t->getVertex(2)->getIndex(), t->getVertex(3)->getIndex());
//     fprintf (fp, "POINTS %d float\n", 4);
//     for (int i = 0; i < 4; i++)
//         fprintf(fp, "%lf %lf %lf\n", t->getVertex(i)->x(), t->getVertex(i)->y(), t->getVertex(i)->z());
//
//     fprintf(fp, "CELLS %d %d\n", 1, 5);
//     fprintf(fp, "%d %d %d %d %d\n", 4, 0, 1, 2, 3);
//
//     fprintf(fp, "CELL_TYPES %d\n", 1);
//     fprintf(fp, "%d\n", 10);
//
//     fclose(fp);
// }
//
// void dump_tetra(std::vector<LTetra *> tl)
// {
//     char filename[100];
//     sprintf(filename, "dump_tetra_vector.vtk");
//     tprintf("Writing %s ....\n", filename);
//     FILE *fp = fopen(filename, "w");
//
//     fprintf (fp, "# vtk DataFile Version 2.0\n");
//     fprintf (fp, "Really cool data\n");
//     fprintf (fp, "ASCII\n");
//     fprintf (fp, "DATASET UNSTRUCTURED_GRID\n");
//
//     for (int i = 0; i < tl.size(); i++)
//         printf("LTetra %d --> %d %d %d %d\n", tl[i]->getIndex(), tl[i]->getVertex(0)->getIndex(), tl[i]->getVertex(1)->getIndex(), tl[i]->getVertex(2)->getIndex(), tl[i]->getVertex(3)->getIndex());
//
//     fprintf (fp, "POINTS %d float\n", 4*(int)tl.size());
//     for (int i = 0; i < tl.size(); i++)
//         for (int j = 0; j < 4; j++)
//             fprintf(fp, "%lf %lf %lf\n", tl[i]->getVertex(j)->x(), tl[i]->getVertex(j)->y(), tl[i]->getVertex(j)->z());
//
//     fprintf(fp, "CELLS %d %d\n", (int)tl.size(), 5*(int)tl.size());
//     for (int i = 0; i < tl.size(); i++)
//         fprintf(fp, "%d %d %d %d %d\n", 4, 4*i, 4*i+1, 4*i+2, 4*i+3);
//
//     fprintf(fp, "CELL_TYPES %d\n", (int)tl.size());
//     for (int i = 0; i < tl.size(); i++)
//         fprintf(fp, "%d\n", 10);
//
//     fclose(fp);
// }


#endif
