#include "hashstruct.h"
#include "simplex.h"

hashFaceKey::hashFaceKey(Tetra *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 *hashFaceStruct::addFace(Tetra *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());
    
    int found = 0;
    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;
            found = 1;
            break;
        }
    }
    if (!found)
        table.insert(std::pair<int, hashFaceKey*>(k1->key(), k1));
    return k1;
}

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

hashEdgeKey::hashEdgeKey(Tetra *t, int edge, Vertex *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;
//   _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;
    _m = m;
}

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(Tetra *t, int edge, Vertex *m)
{
    int s = 0;
    assert(t != NULL);
    assert(t->getIndex() != -1);
    assert(edge>=0 && edge<6);    
    assert(s >= _cstack);
    assert(m != NULL);
    hashEdgeKey *k1 = new hashEdgeKey(t, edge, m);
    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::updateEdge(Tetra *t_old, int edge_old, Tetra *t_new)
{
    assert(t_old->getIndex() == -1);
    assert(t_new->getIndex() != -1);
    hashEdgeKey *k1 = new hashEdgeKey(t_old, edge_old, NULL);
    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(Tetra **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;
    }
}

Vertex *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;
}