// 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 _HASHSTRUCT_CUTMESH_H_
#define _HASHSTRUCT_CUTMESH_H_

#include "common.h"
#include "LTetra.h"

class hashFaceKey
{
  private :
    int _key;
    int min;
    int max;
    LTetra *_t;
    int _f;
    int _id;
//     void *_obj;

  public :
    hashFaceKey ( LTetra *t, int face );
    hashFaceKey ( int v0, int v1, int v2 );
//     inline size_t memory()
//     {
//       return sizeof(*this);
//     }
    inline int face()
    {
      return _f;
    }
    inline LTetra * tetra()
    {
      return _t;
    }
    inline int key()
    {
      return _key;
    }
    inline int equal ( hashFaceKey *k )
    {
      return ( k->min == this->min ) && ( k->max == this->max );
    }
    inline void setIndex ( int i )
    {
      _id = i;
    }
    inline int index()
    {
      return _id;
    }
//     inline void set_obj(void *obj)
//     {
//         _obj = obj;
//     }
//     inline void *get_obj()
//     {
//         return _obj;
//     }
};


class hashFaceStruct
{
  private :
    std::multimap<int, hashFaceKey*> table;

  public :
    hashFaceKey *addAdjFace ( LTetra *t, int face );
    hashFaceKey *addFace ( LTetra *t, int face );
//     inline size_t memory()
//     {
//       return sizeof(*this) + table.size()*(sizeof(int) + sizeof(hashFaceKey*));
//     }
    hashFaceKey *getFace ( int v0, int v1, int v2 );
    int size()
    {
      return table.size();
    }
    void clear();
};

class hashEdgeKey
{
  private :
    int _key;
    int min;
    LVertex *_v[2];
    LTetra *_t;
    int _id;
    LVertex *_m;

  public :
    hashEdgeKey ( LTetra *t, int edge, LVertex *m = NULL );
//     inline size_t memory()
//     {
//       return sizeof(*this);
//     }
    inline LTetra * tetra()
    {
      return _t;
    }
    inline LVertex * vertex ( int i )
    {
      assert ( i>=0 && i<2 );
      return _v[i];
    }
    inline void setTetra ( LTetra *t )
    {
      _t = t;
    }
    inline int key()
    {
      return _key;
    }
    inline int equal ( hashEdgeKey *k )
    {
      return ( k->min == this->min );  // si la key est identique !!
    }
    inline void setIndex ( int i )
    {
      _id = i;
    }
    inline int index()
    {
      return _id;
    }
    inline LVertex * middle()
    {
      return _m;
    }
};

class hashEdgeStruct
{
  private :
    std::multimap<int, hashEdgeKey*> _table;
    std::multimap<int, hashEdgeKey*>::iterator _it;
    std::vector<std::queue<hashEdgeKey*> > _stack;
    int _ss;
    int _cstack;
    int _lock_it;

  public :
    hashEdgeStruct()
    {
      _cstack = 0;
      _ss = 0;
      _lock_it = 0;
    }

//     inline size_t memory()
//     {
//       return sizeof(*this) + _table.size()*(sizeof(int) + sizeof(hashEdgeKey*)) + sizeof(_it) + _stack.size()*sizeof(hashEdgeKey*);
//     }
    void dump();
    hashEdgeKey *stack_front();
    void stack_pop();
    hashEdgeKey *query ( hashEdgeKey *k1 );
    hashEdgeKey *addEdge ( LTetra *t, int edge, LVertex *m );
    hashEdgeKey *addEdge ( LTetra *t, int edge, int surf );
//     void updateEdges(LTetra *t_old, int edge_old, LTetra *t1, LTetra *t2);
    void updateEdge ( LTetra *t_old, int edge_old, LTetra *t_new/*, int edge_new*/ );
    void deleteCurrent();
    void nextEdge();
    void getEdge ( LTetra **t, int *edge );
    inline LVertex *getVertex ( int i );
    inline void set_stack_number ( int i )
    {
      _stack.resize ( i );
      _cstack = 0;
      _ss = 0;
      _lock_it = 0;
    }
    inline int get_stack_number()
    {
      return _stack.size();
    }
    inline int size()
    {
      int ss = 0;
      for ( int i = 0; i < _stack.size(); i++ )
        ss += _stack[i].size();
      assert ( _ss == ss );
      return _ss;
    }
    inline int get_current_surf()
    {
      return _cstack;
    }

    void check_internal();
    inline void lock_query()
    {
      _lock_it = 1;
    }
    inline void unlock_query()
    {
      _lock_it = 0;
    }
    inline int query_locked()
    {
      return _lock_it;
    }
    void clear();
};

#endif
