// 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>.

#include "hashstruct.h"

hashFaceKey::hashFaceKey ( LTetra *t, int face )
{
  assert ( t != NULL );
  assert ( face>=0 && face<4 );
  assert ( t->getFaceVertex ( face, 0 )->getIndex() != -1 );
  assert ( t->getFaceVertex ( face, 1 )->getIndex() != -1 );
  assert ( t->getFaceVertex ( face, 2 )->getIndex() != -1 );

  _t = t;
  _f = face;
  _key = t->getFaceVertex ( face, 0 )->getIndex() + t->getFaceVertex ( face, 1 )->getIndex() + t->getFaceVertex ( face, 2 )->getIndex();

  min = t->getFaceVertex ( face, 0 )->getIndex();
  if ( t->getFaceVertex ( face, 1 )->getIndex() < min )
    min = t->getFaceVertex ( face, 1 )->getIndex();
  if ( t->getFaceVertex ( face, 2 )->getIndex() < min )
    min = t->getFaceVertex ( face, 2 )->getIndex();

  max = t->getFaceVertex ( face, 0 )->getIndex();
  if ( t->getFaceVertex ( face, 1 )->getIndex() > max )
    max = t->getFaceVertex ( face, 1 )->getIndex();
  if ( t->getFaceVertex ( face, 2 )->getIndex() > max )
    max = t->getFaceVertex ( face, 2 )->getIndex();
  _id = -1;
}

hashFaceKey::hashFaceKey ( int v0, int v1, int v2 )
{
  assert ( v0 != -1 );
  assert ( v1 != -1 );
  assert ( v2 != -1 );

  _t = NULL;
  _f = -1;
  _key = v0 + v1 + v2;

  min = v0;
  if ( v1 < min )
    min = v1;
  if ( v2 < min )
    min = v2;

  max = v0;
  if ( v1 > max )
    max = v1;
  if ( v2 > max )
    max = v2;
  _id = -1;
}

hashFaceKey *hashFaceStruct::addAdjFace ( LTetra *t, int face )
{
  assert ( t != NULL );
  assert ( face>=0 && face<4 );

  hashFaceKey *k1 = new hashFaceKey ( t, face );
  std::multimap<int, hashFaceKey*>::iterator it;
  std::pair<std::multimap<int, hashFaceKey*>::iterator,std::multimap<int, hashFaceKey*>::iterator> ret;
  ret = table.equal_range ( k1->key() );
  for ( it=ret.first; it!=ret.second; ++it )
  {
    hashFaceKey *k2 = it->second;
    if ( k2->equal ( k1 ) )
    {
      k1->tetra()->setMutualAdj ( k1->face(), k2->tetra(), k2->face() );
      table.erase ( it );
      delete k1;
      delete k2;
      k1 = NULL;
      k2 = NULL;
      break;
    }
  }
  if ( k1 )
    table.insert ( std::pair<int, hashFaceKey*> ( k1->key(), k1 ) );
  return k1;
}

hashFaceKey *hashFaceStruct::addFace ( LTetra *t, int face )
{
  assert ( t != NULL );
  assert ( face>=0 && face<4 );

  hashFaceKey *k1 = new hashFaceKey ( t, face );
  std::multimap<int, hashFaceKey*>::iterator it;
  std::pair<std::multimap<int, hashFaceKey*>::iterator,std::multimap<int, hashFaceKey*>::iterator> ret;
  ret = table.equal_range ( k1->key() );
  for ( it=ret.first; it!=ret.second; ++it )
  {
    hashFaceKey *k2 = it->second;
    if ( k2->equal ( k1 ) )
    {
      delete k1;
      k1 = NULL;
      break;
    }
  }
  if ( k1 )
    table.insert ( std::pair<int, hashFaceKey*> ( k1->key(), k1 ) );
  return k1;
}

hashFaceKey *hashFaceStruct::getFace ( int v0, int v1, int v2 )
{
  hashFaceKey *k1 = new hashFaceKey ( v0, v1, v2 );
  std::multimap<int, hashFaceKey*>::iterator it;
  std::pair<std::multimap<int, hashFaceKey*>::iterator,std::multimap<int, hashFaceKey*>::iterator> ret;
  ret = table.equal_range ( k1->key() );
  for ( it=ret.first; it!=ret.second; ++it )
  {
    hashFaceKey *k2 = it->second;
    if ( k2->equal ( k1 ) )
    {
      delete k1;
      k1 = NULL;
      return k2;
    }
  }
  delete k1;
  return NULL;
}

void hashFaceStruct::clear()
{
  table.clear();
}

hashEdgeKey::hashEdgeKey ( LTetra *t, int edge, LVertex *m )
{
  assert ( t != NULL );
  assert ( edge>=0 && edge<6 );
  assert ( t->getEdgeVertex ( edge, 0 )->getIndex() != -1 );
  assert ( t->getEdgeVertex ( edge, 1 )->getIndex() != -1 );

  _t = t;
  _m = m;
//   _e = edge;
  _v[0] = t->getEdgeVertex ( edge, 0 );
  _v[1] = t->getEdgeVertex ( edge, 1 );
  _key = _v[0]->getIndex() + _v[1]->getIndex();
  min = _v[0]->getIndex();
  if ( _v[1]->getIndex() < min )
    min = _v[1]->getIndex();
  _id = -1;
}

hashEdgeKey *hashEdgeStruct::stack_front()
{
  assert ( _cstack < _stack.size() );
  while ( _stack[_cstack].empty() )
  {
    _cstack++;
    if ( _cstack == _stack.size() )
      return NULL;
  }
  return _stack[_cstack].front();
}

void hashEdgeStruct::stack_pop()
{
  if ( _cstack < _stack.size() )
  {
    if ( !_stack[_cstack].empty() )
      _ss--;
    _stack[_cstack].pop();
  }
  assert ( _ss >= 0 );
}

hashEdgeKey *hashEdgeStruct::addEdge ( LTetra *t, int edge, LVertex *m )
{
  assert ( t != NULL );
  assert ( t->getIndex() != -1 );
  assert ( edge>=0 && edge<6 );
  hashEdgeKey *k1 = new hashEdgeKey ( t, edge, m );
  hashEdgeKey *k2 = query ( k1 );
  unlock_query();
  if ( k2 != NULL )
  {
    delete k1;
    return k2;
  }
  assert ( m != NULL );
  // not found
  _table.insert ( std::pair<int, hashEdgeKey*> ( k1->key(), k1 ) );
//     _ss++;
  return k1;
}

hashEdgeKey *hashEdgeStruct::addEdge ( LTetra *t, int edge, int surf )
{
  assert ( t != NULL );
  assert ( t->getIndex() != -1 );
  assert ( edge>=0 && edge<6 );
  assert ( surf != -1 );
  int s = ( surf < 0 ) ? -surf : surf;
  assert ( s >= _cstack );
  hashEdgeKey *k1 = new hashEdgeKey ( t, edge );
  hashEdgeKey *k2 = query ( k1 );
  unlock_query();
  if ( k2 != NULL )
  {
    delete k1;
    return k2;
  }
  // not found
  _table.insert ( std::pair<int, hashEdgeKey*> ( k1->key(), k1 ) );
  _stack[s].push ( k1 );
  _ss++;
  return k1;
}

void hashEdgeStruct::deleteCurrent()
{
  hashEdgeKey *k1 = stack_front();
  hashEdgeKey *k2 = query ( k1 );
  assert ( k2 != NULL );
  _table.erase ( _it );
  unlock_query();
  delete k1;
}

void hashEdgeStruct::nextEdge()
{
//     deleteCurrent();
  stack_pop();
}


// void hashEdgeStruct::updateEdges(LTetra *t_old, int edge_old, LTetra *t1, LTetra *t2)
// {
//     assert(t_old != NULL);
//     assert(edge_old>=0 && edge_old<6);
//     assert(t1 != NULL);
//     assert(t2 != NULL);
//
//     static int eu[6][5] = {{1, 2, 4, 3, 5},{0, 2, 5, 3, 4},{0, 1, 5, 4, 3},{0, 4, 5, 1, 2},{0, 3, 5, 2, 1},{3, 1, 2, 4, 0}};
//     updateEdge(t_old, eu[edge_old][0], t1);
//     updateEdge(t_old, eu[edge_old][1], t1);
//     updateEdge(t_old, eu[edge_old][2], t2);
//     updateEdge(t_old, eu[edge_old][3], t2);
//     updateEdge(t_old, eu[edge_old][4], t1);
// }

void hashEdgeStruct::updateEdge ( LTetra *t_old, int edge_old, LTetra *t_new )
{
  assert ( t_old->getIndex() == -1 );
  assert ( t_new->getIndex() != -1 );
  hashEdgeKey *k1 = new hashEdgeKey ( t_old, edge_old );
  hashEdgeKey *k2 = query ( k1 );
  unlock_query();
  if ( k2 != NULL )
  {
    k2->setTetra ( t_new );
    delete k1;
  }
}

void hashEdgeStruct::dump()
{
  printf ( "Edge struct ## current surface --> %d\n", _cstack );
  for ( int i = 0; i < _stack.size(); i++ )
    printf ( "--> stack %d : size = %d\n", i, ( int ) _stack[i].size() );
}

hashEdgeKey *hashEdgeStruct::query ( hashEdgeKey *k1 )
{
  assert ( !query_locked() );
  lock_query();
  std::pair<std::multimap<int, hashEdgeKey*>::iterator,std::multimap<int, hashEdgeKey*>::iterator> ret;
  ret = _table.equal_range ( k1->key() );
  for ( _it=ret.first; _it!=ret.second; ++_it )
  {
    hashEdgeKey *k2 = _it->second;
    if ( k2->equal ( k1 ) )
    {
      return k2;
    }
  }
  return NULL;
}

void hashEdgeStruct::getEdge ( LTetra **t, int *edge )
{
  if ( size() )
  {
    hashEdgeKey *k = stack_front();
    assert ( k != NULL );
    *t = k->tetra();
    assert ( ( *t )->getIndex() != -1 );
    *edge = k->tetra()->getEdgeIndex ( k->vertex ( 0 ), k->vertex ( 1 ) );
  }
  else
  {
    *t = NULL;
    *edge = -1;
  }
}

LVertex *hashEdgeStruct::getVertex ( int i )
{
  if ( size() )
    return stack_front()->vertex ( i );
  else
    return NULL;
}

void hashEdgeStruct::clear()
{
  _table.clear();
  while ( _cstack < _stack.size() )
  {
    while ( !_stack[_cstack].empty() )
      _stack[_cstack].pop();
    _cstack++;
  }
  _stack.clear();
  _cstack = 0;
  _ss = 0;
}
