// This code is in the public domain -- castanyo@yahoo.es

#include "nvmesh.h" // pch

#include "Mesh.h"
#include "Edge.h"
#include "Vertex.h"
#include "Face.h"

#include "nvmesh/TriMesh.h"
#include "nvmesh/QuadTriMesh.h"
#include "nvmesh/MeshBuilder.h"

#include "nvmath/Vector.inl"
#include "nvcore/Array.inl"
#include "nvcore/HashMap.inl"


using namespace nv;
using namespace HalfEdge;

Mesh::Mesh() : m_colocalVertexCount(0)
{
    errorCount = 0;
}

Mesh::Mesh(const Mesh * mesh)
{
    errorCount = 0;

    // Copy mesh vertices.
    const uint vertexCount = mesh->vertexCount();
    m_vertexArray.resize(vertexCount);

    for (uint v = 0; v < vertexCount; v++)
    {
        const Vertex * vertex = mesh->vertexAt(v);
        nvDebugCheck(vertex->id == v);

        m_vertexArray[v] = new Vertex(v);
        m_vertexArray[v]->pos = vertex->pos;
        m_vertexArray[v]->nor = vertex->nor;
        m_vertexArray[v]->tex = vertex->tex;
    }

    m_colocalVertexCount = vertexCount;


    // Copy mesh faces.
    const uint faceCount = mesh->faceCount();

    Array<uint> indexArray(3);

    for (uint f = 0; f < faceCount; f++)
    {
        const Face * face = mesh->faceAt(f);

        for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
            const Vertex * vertex = it.current()->from();
            indexArray.append(vertex->id);
        }

        addFace(indexArray);
        indexArray.clear();
    }
}

Mesh::~Mesh()
{
    clear();
}


void Mesh::clear()
{
    deleteAll(m_vertexArray); 
    m_vertexArray.clear();

    foreach(i, m_edgeMap)
    {
        delete m_edgeMap[i].value;
    }
    //deleteAll(m_edgeArray);	// edgeArray only contains 1/2 of the edges!
    m_edgeArray.clear();
    m_edgeMap.clear();

    deleteAll(m_faceArray);
    m_faceArray.clear();
}


Vertex * Mesh::addVertex(const Vector3 & pos)
{
    nvDebugCheck(isFinite(pos));

    Vertex * v = new Vertex(m_vertexArray.count());
    v->pos = pos;
    m_vertexArray.append(v);

    return v;

//    return addVertex(m_vertexArray.count(), pos);
}

/*Vertex * Mesh::addVertex(uint id, const Vector3 & pos)
{
    nvDebugCheck(isFinite(pos));

    Vertex * v = new Vertex(id);
    v->pos = pos;
    m_vertexArray.append(v);

    return v;
}*/

/*void Mesh::addVertices(const Mesh * mesh)
{
nvCheck(mesh != NULL);

// Add mesh vertices
for (uint v = 0; v < vertexCount; v++)
{
const Vertex * vertex = mesh->vertexAt(v);
nvDebugCheck(vertex != NULL);

Vertex * v = addVertex(vertex->pos());
nvDebugCheck(v != NULL);

v->setNor(vertex->nor());
v->setTex(vertex->tex());
}
}*/


/// Link colocal vertices based on geometric location only.
void Mesh::linkColocals()
{
    nvDebug("--- Linking colocals:\n");

    const uint vertexCount = this->vertexCount();
    HashMap<Vector3, Vertex *> vertexMap(vertexCount);

    for (uint v = 0; v < vertexCount; v++)
    {
        Vertex * vertex = vertexAt(v);

        Vertex * colocal;
        if (vertexMap.get(vertex->pos, &colocal))
        {
            colocal->linkColocal(vertex);
        }
        else
        {
            vertexMap.add(vertex->pos, vertex);
        }
    }

    m_colocalVertexCount = vertexMap.count();

    nvDebug("---   %d vertex positions.\n", m_colocalVertexCount);

    // @@ Remove duplicated vertices? or just leave them as colocals?
}

void Mesh::linkColocalsWithCanonicalMap(const Array<uint> & canonicalMap)
{
    nvDebug("--- Linking colocals:\n");

    uint vertexMapSize = 0;
    foreach(i, canonicalMap) {
        vertexMapSize = max(vertexMapSize, canonicalMap[i] + 1);
    }
    
    Array<Vertex *> vertexMap;
    vertexMap.resize(vertexMapSize, NULL);

    m_colocalVertexCount = 0;

    const uint vertexCount = this->vertexCount();
    for (uint v = 0; v < vertexCount; v++)
    {
        Vertex * vertex = vertexAt(v);

        Vertex * colocal = vertexMap[canonicalMap[v]];
        if (colocal != NULL)
        {
            nvDebugCheck(vertex->pos == colocal->pos);
            colocal->linkColocal(vertex);
        }
        else
        {
            vertexMap[canonicalMap[v]] = vertex;
            m_colocalVertexCount++;
        }
    }

    nvDebug("---   %d vertex positions.\n", m_colocalVertexCount);
}


Face * Mesh::addFace()
{
    Face * f = new Face(m_faceArray.count());
    m_faceArray.append(f);
    return f;
}

Face * Mesh::addFace(uint v0, uint v1, uint v2)
{
    Array<uint> indexArray(3);
    indexArray << v0 << v1 << v2;
    return addFace(indexArray, 0, 3);
}

Face * Mesh::addFace(uint v0, uint v1, uint v2, uint v3)
{
    Array<uint> indexArray(4);
    indexArray << v0 << v1 << v2 << v3;
    return addFace(indexArray, 0, 4);
}

Face * Mesh::addFace(const Array<uint> & indexArray)
{
    return addFace(indexArray, 0, indexArray.count());
}


Face * Mesh::addFace(const Array<uint> & indexArray, uint first, uint num)
{
    nvDebugCheck(first < indexArray.count());
    nvDebugCheck(num <= indexArray.count()-first);
    nvDebugCheck(num > 2);

    if (!canAddFace(indexArray, first, num)) {
        errorCount++;
        return NULL;
    }

    Face * f = new Face(m_faceArray.count());

    Edge * firstEdge = NULL;
    Edge * last = NULL;
    Edge * current = NULL;

    for(uint i = 0; i < num-1; i++)
    {
        current = addEdge(indexArray[first+i], indexArray[first+i+1]);
        nvCheck(current != NULL && current->face == NULL);

        current->face = f;

        if (last != NULL) last->setNext(current);
        else firstEdge = current;

        last = current;
    }

    current = addEdge(indexArray[first+num-1], indexArray[first]);
    nvCheck(current != NULL && current->face == NULL);

    current->face = f;

    last->setNext(current);
    current->setNext(firstEdge);

    f->edge = firstEdge;
    m_faceArray.append(f);

    return f;
}

/*void Mesh::addFaces(const Mesh * mesh)
{
nvCheck(mesh != NULL);

Array indexArray;
// Add faces

}*/


// Return true if the face can be added to the manifold mesh.
bool Mesh::canAddFace(const Array<uint> & indexArray, uint first, uint num) const
{
    for (uint j = num - 1, i = 0; i < num; j = i++) {
        if (!canAddEdge(indexArray[first+j], indexArray[first+i])) {
            errorIndex0 = indexArray[first+j];
            errorIndex1 = indexArray[first+i];
            return false;
        }
    }

    // We also have to make sure the face does not have any duplicate edge!
    for (uint i = 0; i < num; i++) {

        int i0 = indexArray[first + i + 0];
        int i1 = indexArray[first + (i + 1)%num];

        for (uint j = i + 1; j < num; j++) {
            int j0 = indexArray[first + j + 0];
            int j1 = indexArray[first + (j + 1)%num];

            if (i0 == j0 && i1 == j1) {
                return false;
            }
        }
    }

    return true;
}

// Return true if the edge doesn't exist or doesn't have any adjacent face. 
bool Mesh::canAddEdge(uint i, uint j) const
{
    if (i == j) {
        // Skip degenerate edges.
        return false;
    }

    // Same check, but taking into account colocal vertices.
    const Vertex * v0 = vertexAt(i);
    const Vertex * v1 = vertexAt(j);

    for(Vertex::ConstVertexIterator it(v0->colocals()); !it.isDone(); it.advance())
    {
        if (it.current() == v1)
        {
            // Skip degenerate edges.
            return false;
        }
    }

    // Make sure edge has not been added yet.
    Edge * edge = findEdge(i, j);

    return edge == NULL || edge->face == NULL; // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face.
}

Edge * Mesh::addEdge(uint i, uint j)
{
    nvCheck(i != j);

    Edge * edge = findEdge(i, j);

    if (edge != NULL) {
        // Edge may already exist, but its face must not be set.
        nvDebugCheck(edge->face == NULL);

        // Nothing else to do!

    }
    else {
        // Add new edge.

        // Lookup pair.
        Edge * pair = findEdge(j, i);

        if (pair != NULL)
        {
            // Create edge with same id.
            edge = new Edge(pair->id + 1);

            // Link edge pairs.
            edge->pair = pair;
            pair->pair = edge;

            // @@ I'm not sure this is necessary!
            pair->vertex->setEdge(pair);
        }
        else
        {
            // Create edge.
            edge = new Edge(2*m_edgeArray.count());

            // Add only unpaired edges.
            m_edgeArray.append(edge);
        }

        edge->vertex = m_vertexArray[i];
        m_edgeMap.add(Key(i,j), edge);
    }

    // Face and Next are set by addFace.

    return edge;
}


/// Find edge, test all colocals.
Edge * Mesh::findEdge(uint i, uint j) const
{
    Edge * edge = NULL;

    const Vertex * v0 = vertexAt(i);
    const Vertex * v1 = vertexAt(j);

    // Test all colocal pairs.
    for(Vertex::ConstVertexIterator it0(v0->colocals()); !it0.isDone(); it0.advance())
    {
        for(Vertex::ConstVertexIterator it1(v1->colocals()); !it1.isDone(); it1.advance())
        {
            Key key(it0.current()->id, it1.current()->id);

            if (edge == NULL) {
                m_edgeMap.get(key, &edge);
#if !defined(_DEBUG)
                if (edge != NULL) return edge;
#endif
            }
            else {
                // Make sure that only one edge is found.
                nvDebugCheck(!m_edgeMap.get(key));
            }
        }
    }

    return edge;
}

/// Link boundary edges once the mesh has been created.
void Mesh::linkBoundary()
{
    nvDebug("--- Linking boundaries:\n");

    int num = 0;

    // Create boundary edges.
    uint edgeCount = this->edgeCount();
    for(uint e = 0; e < edgeCount; e++)
    {
        Edge * edge = edgeAt(e);
        if (edge != NULL && edge->pair == NULL) {
            Edge * pair = new Edge(edge->id + 1);

            uint i = edge->from()->id;
            uint j = edge->next->from()->id;

            Key key(j,i);
            nvCheck(!m_edgeMap.get(key));

            pair->vertex = m_vertexArray[j];
            m_edgeMap.add(key, pair);

            edge->pair = pair;
            pair->pair = edge;

            num++;
        }
    }

    // Link boundary edges.
    for (uint e = 0; e < edgeCount; e++) {
        Edge * edge = edgeAt(e);
        if (edge != NULL && edge->pair->face == NULL) {
            linkBoundaryEdge(edge->pair);
        }
    }

    nvDebug("---   %d boundary edges.\n", num);
}

/// Link this boundary edge.
void Mesh::linkBoundaryEdge(Edge * edge)
{
    nvCheck(edge->face == NULL);

    // Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes.
    //nvCheck(edge->next() == NULL);

    Edge * next = edge;
    while(next->pair->face != NULL) {
        // Get pair prev
        Edge * e = next->pair->next;
        while (e->next != next->pair) {
            e = e->next;
        }
        next = e;
    }
    edge->setNext(next->pair);

    // Adjust vertex edge, so that it's the boundary edge. (required for isBoundary())
    if (edge->vertex->edge != edge)
    {
        // Multiple boundaries in the same edge.
        //nvCheck( edge->vertex()->edge() == NULL || edge->vertex()->edge()->face() != NULL );
        edge->vertex->edge = edge;
    }
}


/// Convert to tri mesh.
TriMesh * Mesh::toTriMesh() const
{
    uint triangleCount = 0;

    // Count triangle faces.
    const uint faceCount = this->faceCount();
    for(uint f = 0; f < faceCount; f++)
    {
        const Face * face = faceAt(f);
        triangleCount += face->edgeCount() - 2;
    }

    TriMesh * triMesh = new TriMesh(triangleCount, vertexCount());

    // Add vertices.
    Array<TriMesh::Vertex> & vertices = triMesh->vertices();

    const uint vertexCount = this->vertexCount();
    for(uint v = 0; v < vertexCount; v++)
    {
        const Vertex * vertex = vertexAt(v);

        TriMesh::Vertex triVertex;
        triVertex.id = vertices.count();
        triVertex.pos = vertex->pos;
        triVertex.nor = vertex->nor;
        triVertex.tex = vertex->tex;

        vertices.append(triVertex);
    }

    // Add triangles.
    Array<TriMesh::Face> & triangles = triMesh->faces();

    for(uint f = 0; f < faceCount; f++)
    {
        const Face * face = faceAt(f);

        // @@ Triangulate arbitrary polygons correctly.
        const uint v0 = face->edge->vertex->id;
        uint v1 = face->edge->next->vertex->id;

        for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
        {
            uint v2 = it.current()->vertex->id;

            // Skip the first two vertices.
            if (v2 == v0 || v2 == v1) continue;

            TriMesh::Face triangle;
            triangle.id = triangles.count();
            triangle.v[0] = v0;
            triangle.v[1] = v1;
            triangle.v[2] = v2;

            v1 = v2;

            triangles.append(triangle);
        }
    }

    return triMesh;
}

QuadTriMesh * Mesh::toQuadTriMesh() const
{
    MeshBuilder builder;

    const uint vertexCount = this->vertexCount();
    builder.hintVertexCount(vertexCount);

    for(uint v = 0; v < vertexCount; v++)
    {
        const Vertex * vertex = vertexAt(v);

        builder.addPosition(vertex->pos);
        builder.addNormal(vertex->nor);
        builder.addTexCoord(vertex->tex);
    }

    const uint faceCount = this->faceCount();
    builder.hintTriangleCount(faceCount);

    for(uint f = 0; f < faceCount; f++)
    {
        const Face * face = faceAt(f);

        builder.beginPolygon();

        for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
        {
            uint v = it.current()->vertex->id;
            builder.addVertex(v, v, v);
        }

        builder.endPolygon();
    }

    builder.done();

    return builder.buildQuadTriMesh();
}


// Triangulate in place.
void Mesh::triangulate() {

    bool all_triangles = true;

    const uint faceCount = m_faceArray.count();
    for (uint f = 0; f < faceCount; f++) {
        Face * face = m_faceArray[f];
        if (face->edgeCount() != 3) {
            all_triangles = false;
            break;
        }
    }

    if (all_triangles) {
        return;
    }


    // Do not touch vertices, but rebuild edges and faces.
    Array<Edge *> edgeArray;
    Array<Face *> faceArray;

    swap(edgeArray, m_edgeArray);
    swap(faceArray, m_faceArray);
    m_edgeMap.clear();

    for (uint f = 0; f < faceCount; f++) {
        Face * face = faceArray[f];

        // Trivial fan-like triangulation.
        const uint v0 = face->edge->vertex->id;
        uint v2, v1 = -1;

        for (Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
            Edge * edge = it.current();
            v2 = edge->to()->id;
            if (v2 == v0) break;
            if (v1 != -1) addFace(v0, v1, v2);
            v1 = v2;
        }
    }

    nvDebugCheck(m_faceArray.count() > faceCount); // triangle count > face count

    linkBoundary();

    deleteAll(edgeArray);
    deleteAll(faceArray);
}


/*
Fixing T-junctions.

- Find T-junctions. Find  vertices that are on an edge. 
    - This test is approximate.
    - Insert edges on a spatial index to speedup queries.
    - Consider only open edges, that is edges that have no pairs.
    - Consider only vertices on boundaries.
- Close T-junction.
    - Split edge.

*/
bool Mesh::splitBoundaryEdges() {
    
    Array<Vertex *> boundaryVertices;

    foreach(i, m_vertexArray) {
        Vertex * v = m_vertexArray[i];
        if (v->isBoundary()) {
            boundaryVertices.append(v);
        }
    }

    nvDebug("Fixing T-junctions:\n");

    int splitCount = 0;

    foreach(v, boundaryVertices) {
        Vertex * vertex = boundaryVertices[v];

        Vector3 x0 = vertex->pos;

        // Find edges that this vertex overlaps with.
        foreach(e, m_edgeArray) {
        //for (uint e = 0; e < m_edgeArray.count(); e++) {
            Edge * edge = m_edgeArray[e];
            if (edge != NULL && edge->isBoundary()) {

                if (edge->from() == vertex || edge->to() == vertex) {
                    continue;
                }

                Vector3 x1 = edge->from()->pos;
                Vector3 x2 = edge->to()->pos;

                Vector3 v01 = x0 - x1;
                Vector3 v21 = x2 - x1;

                float l = length(v21);
                float d = length(cross(v01, v21)) / l;

                if (isZero(d)) {
                    float t = dot(v01, v21) / (l * l);

                    // @@ Snap x0 to x1 or x2, if too close? No, do vertex snapping elsewhere.
                    /*if (equal(t, 0.0f, 0.01f)) {
                        //vertex->setPos(x1);
                    }
                    else if (equal(t, 1.0f, 0.01f)) {
                        //vertex->setPos(x2);
                    }
                    else*/
                    if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) {
                        nvDebugCheck(equal(lerp(x1, x2, t), x0));

                        Vertex * splitVertex = splitBoundaryEdge(edge, t, x0);
                        vertex->linkColocal(splitVertex);   // @@ Should we do this here?
                        splitCount++;
                    }
                }
            }
        }
    }

    nvDebug(" - %d edges split.\n", splitCount);

    nvDebugCheck(isValid());

    return splitCount != 0;
}


// For this to be effective, we have to fix the boundary junctions first.
Edge * Mesh::sewBoundary(Edge * startEdge) {
    nvDebugCheck(startEdge->face == NULL);

    // @@ We may want to be more conservative linking colocals in order to preserve the input topology. One way of doing that is by linking colocals only 
    // if the vertices next to them are linked as well. That is, by sewing boundaries after detecting them. If any pair of consecutive edges have their first
    // and last vertex in the same position, then it can be linked.

    Edge * lastBoundarySeen = startEdge;

    nvDebug("Sewing Boundary:\n");

    int count = 0;
    int sewnCount = 0;

    Edge * edge = startEdge;
    do {
        nvDebugCheck(edge->face == NULL);

        Edge * edge_a = edge;
        Edge * edge_b = edge->prev;

        Edge * pair_a = edge_a->pair;
        Edge * pair_b = edge_b->pair;

        Vertex * v0a = edge_a->to();
        Vertex * v0b = edge_b->from();
        Vertex * v1a = edge_a->from();
        Vertex * v1b = edge_b->to();

        nvDebugCheck(v1a->isColocal(v1b));

        /*
        v0b +      _+ v0a
             \     /
            b \   / a
               \|/
            v1b + v1a
        */

        // @@ This should not happen while sewing, but it may be produced somewhere else.
        nvDebugCheck(edge_a != edge_b);

        if (v0a->pos == v0b->pos) {

            // Link vertices.
            v0a->linkColocal(v0b);
            
            // Remove edges to be collapsed.
            disconnect(edge_a);
            disconnect(edge_b);
            disconnect(pair_a);
            disconnect(pair_b);

            // Link new boundary edges.
            Edge * prevBoundary = edge_b->prev;
            Edge * nextBoundary = edge_a->next;
            if (nextBoundary != NULL) {
                nvDebugCheck(nextBoundary->face == NULL);
                nvDebugCheck(prevBoundary->face == NULL);
                nextBoundary->setPrev(prevBoundary);
            
                // Make sure boundary vertex points to boundary edge.
                v0a->setEdge(nextBoundary); // This updates all colocals.
            }
            lastBoundarySeen = prevBoundary;

            // Creat new edge.
            Edge * newEdge_a = addEdge(v0a->id, v1a->id);   // pair_a->from()->id, pair_a->to()->id
            Edge * newEdge_b = addEdge(v1b->id, v0b->id);

            newEdge_a->pair = newEdge_b;
            newEdge_b->pair = newEdge_a;

            newEdge_a->face = pair_a->face;
            newEdge_b->face = pair_b->face;

            newEdge_a->setNext(pair_a->next);
            newEdge_a->setPrev(pair_a->prev);

            newEdge_b->setNext(pair_b->next);
            newEdge_b->setPrev(pair_b->prev);

            delete edge_a;
            delete edge_b;
            delete pair_a;
            delete pair_b;

            edge = nextBoundary;    // If nextBoundary is NULL we have closed the loop.
            sewnCount++;
        }
        else {
            edge = edge->next;
        }
        
        count++;
    } while(edge != NULL && edge != lastBoundarySeen);

    nvDebug(" - Sewn %d out of %d.\n", sewnCount, count);

    if (lastBoundarySeen != NULL) {
        nvDebugCheck(lastBoundarySeen->face == NULL);
    }

    return lastBoundarySeen;
}


// @@ We must always disconnect edge pairs simultaneously.
void Mesh::disconnect(Edge * edge) {
    nvDebugCheck(edge != NULL);

    // Remove from edge list.
    if ((edge->id & 1) == 0) {
        nvDebugCheck(m_edgeArray[edge->id / 2] == edge);
        m_edgeArray[edge->id / 2] = NULL;
    }

    // Remove edge from map. @@ Store map key inside edge?
    nvDebugCheck(edge->from() != NULL && edge->to() != NULL);
    bool removed = m_edgeMap.remove(Key(edge->from()->id, edge->to()->id));
    nvDebugCheck(removed == true);

    // Disconnect from vertex.
    if (edge->vertex != NULL) {
        if (edge->vertex->edge == edge) {
            if (edge->prev && edge->prev->pair) {
                edge->vertex->edge = edge->prev->pair;
            }
            else if (edge->pair && edge->pair->next) {
                edge->vertex->edge = edge->pair->next;
            }
            else {
                edge->vertex->edge = NULL;
                // @@ Remove disconnected vertex?
            }
        }
        //edge->setVertex(NULL);
    }

    // Disconnect from face.
    if (edge->face != NULL) {
        if (edge->face->edge == edge) {
            if (edge->next != NULL && edge->next != edge) {
                edge->face->edge = edge->next;
            }
            else if (edge->prev != NULL && edge->prev != edge) {
                edge->face->edge = edge->prev;
            }
            else {
                edge->face->edge = NULL;
                // @@ Remove disconnected face?
            }
        }
        //edge->setFace(NULL);
    }

    // @@ Hack, we don't disconnect from pair, because pair needs us to remove itself from the map.
    // Disconect from pair.
    /*if (edge->pair != NULL) {
        if (edge->pair->pair == edge) {
            edge->pair->setPair(NULL);
        }
        //edge->setPair(NULL);
    }*/

    // Disconnect from previous.
    if (edge->prev) {
        if (edge->prev->next == edge) {
            edge->prev->setNext(NULL);
        }
        //edge->setPrev(NULL);
    }

    // Disconnect from next.
    if (edge->next) {
        if (edge->next->prev == edge) {
            edge->next->setPrev(NULL);
        }
        //edge->setNext(NULL);
    }
}


void Mesh::remove(Edge * edge) {
    nvDebugCheck(edge != NULL);

    disconnect(edge);

    delete edge;
}

void Mesh::remove(Vertex * vertex) {
    nvDebugCheck(vertex != NULL);

    // Remove from vertex list.
    m_vertexArray[vertex->id] = NULL;

    // Disconnect from colocals.
    vertex->unlinkColocal();

    // Disconnect from edges.
    if (vertex->edge != NULL) {
        // @@ Removing a connected vertex is asking for trouble...
        if (vertex->edge->vertex == vertex) {
            // @@ Connect edge to a colocal?
            vertex->edge->vertex = NULL;
        }

        vertex->setEdge(NULL);
    }

    delete vertex;
}

void Mesh::remove(Face * face) {
    nvDebugCheck(face != NULL);

    // Remove from face list.
    m_faceArray[face->id] = NULL;

    // Disconnect from edges.
    if (face->edge != NULL) {
        nvDebugCheck(face->edge->face == face);

        face->edge->face = NULL;

        face->edge = NULL;
    }

    delete face;
}


void Mesh::compactEdges() {
    const uint edgeCount = m_edgeArray.count();

    uint c = 0;
    for (uint i = 0; i < edgeCount; i++) {
        if (m_edgeArray[i] != NULL) {
            if (i != c) {
                m_edgeArray[c] = m_edgeArray[i];
                m_edgeArray[c]->id = 2 * c;
                if (m_edgeArray[c]->pair != NULL) {
                    m_edgeArray[c]->pair->id = 2 * c + 1;
                }
            }
            c++;
        }
    }

    m_edgeArray.resize(c);
}


void Mesh::compactVertices() {
    const uint vertexCount = m_vertexArray.count();

    uint c = 0;
    for (uint i = 0; i < vertexCount; i++) {
        if (m_vertexArray[i] != NULL) {
            if (i != c) {
                m_vertexArray[c] = m_vertexArray[i];
                m_vertexArray[c]->id = c;
            }
            c++;
        }
    }

    m_vertexArray.resize(c);

    // @@ Generate xref array for external attributes.
}


void Mesh::compactFaces() {
    const uint faceCount = m_faceArray.count();

    uint c = 0;
    for (uint i = 0; i < faceCount; i++) {
        if (m_faceArray[i] != NULL) {
            if (i != c) {
                m_faceArray[c] = m_faceArray[i];
                m_faceArray[c]->id = c;
            }
            c++;
        }
    }

    m_faceArray.resize(c);
}


Vertex * Mesh::splitBoundaryEdge(Edge * edge, float t, const Vector3 & pos) {

    /*
      We want to go from this configuration:
           
            +   +
            |   ^
       edge |<->|  pair
            v   |
            +   +
      
      To this one:

            +   +
            |   ^
         e0 |<->| p0
            v   |
     vertex +   + 
            |   ^
         e1 |<->| p1
            v   |
            +   +

    */


    Edge * pair = edge->pair;

    // Make sure boundaries are linked.
    nvDebugCheck(pair != NULL); 

    // Make sure edge is a boundary edge.
    nvDebugCheck(pair->face == NULL);

    // Add new vertex.
    Vertex * vertex = addVertex(pos);
    vertex->nor = lerp(edge->from()->nor, edge->to()->nor, t);
    vertex->tex = lerp(edge->from()->tex, edge->to()->tex, t);
    vertex->col = lerp(edge->from()->col, edge->to()->col, t);

    disconnect(edge);
    disconnect(pair);

    // Add edges.
    Edge * e0 = addEdge(edge->from()->id, vertex->id);
    Edge * p0 = addEdge(vertex->id, pair->to()->id);

    Edge * e1 = addEdge(vertex->id, edge->to()->id);
    Edge * p1 = addEdge(pair->from()->id, vertex->id);

    // Link edges.
    e0->setNext(e1);
    p1->setNext(p0);

    e0->setPrev(edge->prev);
    e1->setNext(edge->next);

    p1->setPrev(pair->prev);
    p0->setNext(pair->next);

    nvDebugCheck(e0->next == e1);
    nvDebugCheck(e1->prev == e0);

    nvDebugCheck(p1->next == p0);
    nvDebugCheck(p0->prev == p1);

    nvDebugCheck(p0->pair == e0);
    nvDebugCheck(e0->pair == p0);

    nvDebugCheck(p1->pair == e1);
    nvDebugCheck(e1->pair == p1);

    // Link faces.
    e0->face = edge->face;
    e1->face = edge->face;

    // Link vertices.
    edge->from()->setEdge(e0);
    vertex->setEdge(e1);

    delete edge;
    delete pair;

    return vertex;
}

#if 0
// Without introducing new vertices.
void Mesh::splitBoundaryEdge(Edge * edge, Vertex * vertex) {

    /*
      We want to go from this configuration:

            |   | pn
            +   +
            |   ^
            |   |
       edge |<->| pair
            |   |
            v   |
            +   +
            |   | pp
      
      To this one:
          \       /
           \     /
            +   +
            |   ^
         e0 |<->| p0
            v   |
     vertex +   + 
            |   ^
         e1 |<->| p1
            v   |
            +   +
           /     \
          /       \
    */


    Edge * pair = edge->pair;
    Edge * pn = pair->next();
    Edge * pp = pair->prev();

    // Make sure boundaries are linked.
    nvDebugCheck(pair != NULL);

    // Make sure edge is a boundary edge.
    nvDebugCheck(pair->face() == NULL);

    nvDebugCheck(edge->isValid());
    nvDebugCheck(pair->isValid());

    disconnect(edge);
    disconnect(pair);

    // Add edges.
    Edge * e0 = addEdge(edge->from()->id(), vertex->id());
    Edge * e1 = addEdge(vertex->id(), edge->to()->id());

    // Link faces.
    e0->setFace(edge->face());
    e1->setFace(edge->face());

    // Link pairs.
    Edge * p0 = findEdge(vertex->id(), pair->to()->id());
    if (p0 == NULL) {
        p0 = addEdge(vertex->id(), pair->to()->id());
        pn->setPrev(p0);
    }
    else {
        nvDebugCheck(p0->face() != NULL);
        if (e0->prev() != NULL) {
            pn->setPrev(e0->prev());
        }
        else {
            nvDebugCheck(pn == e0);
        }
    }
    
    Edge * p1 = findEdge(pair->from()->id(), vertex->id());
    if (p1 == NULL) {
        p1 = addEdge(pair->from()->id(), vertex->id());
        pp->setNext(p1);
    }
    else {
        nvDebugCheck(p1->face() != NULL);
        if (e1->next() != NULL) {
            pp->setPrev(e1->next());
        }
        else {
            nvDebugCheck(pp == e1);
        }
    }

    // Link edges.
    e0->setNext(e1); // e1->setPrev(e0)

    if (p0->face() == p1->face()) { // can be null
        p1->setNext(p0); // p0->setPrev(p1)
    }
    else {
        //if (p1->face() == NULL) p1->setNext(
    }
    

    e0->setPrev(edge->prev());
    e1->setNext(edge->next());

    nvDebugCheck(e0->pair == p0);
    nvDebugCheck(e1->pair == p1);
    nvDebugCheck(p0->pair == e0);
    nvDebugCheck(p1->pair == e1);

    nvDebugCheck(e0->isValid());
    nvDebugCheck(e1->isValid());
    nvDebugCheck(pp->isValid());
    nvDebugCheck(pn->isValid());

    nvDebugCheck(e0->pair->isValid());
    nvDebugCheck(e1->pair->isValid());
    nvDebugCheck(pp->pair->isValid());
    nvDebugCheck(pn->pair->isValid());

    nvDebugCheck(edge->face->isValid());

    if (pn->pair->face != NULL) {
        nvDebugCheck(pn->pair->face->isValid());
    }

    if (pp->pair->face() != NULL) {
        nvDebugCheck(pn->pair->face->isValid());
    }

    if (p0->face != NULL) {
        nvDebugCheck(p0->face->isValid());
    }

    if (p1->face() != NULL) {
        nvDebugCheck(p1->face()->isValid());
    }

    nvDebugCheck(isValid()); // Only for extreme debugging.

    // Link vertices.
    edge->from()->setEdge(e0);
    vertex->setEdge(p0);

    delete edge;
    delete pair;
}
#endif

bool Mesh::isValid() const
{
    // Make sure all edges are valid.
    const uint edgeCount = m_edgeArray.count();
    for (uint e = 0; e < edgeCount; e++) {
        Edge * edge = m_edgeArray[e];
        if (edge != NULL) {
            if (edge->id != 2*e) {
                return false;
            }
            if (!edge->isValid()) {
                return false;
            }

            if (edge->pair->id != 2*e+1) {
                return false;
            }
            if (!edge->pair->isValid()) {
                return false;
            }
        }
    }

    // @@ Make sure all faces are valid.

    // @@ Make sure all vertices are valid.

    return true;
}