//
//   Copyright 2013 Pixar
//
//   Licensed under the terms set forth in the LICENSE.txt file available at
//   https://opensubdiv.org/license.
//

#ifndef OPENSUBDIV3_HBRMESH_H
#define OPENSUBDIV3_HBRMESH_H

#ifdef PRMAN
#include "libtarget/TgMalloc.h" // only for alloca
#include "libtarget/TgThread.h"
#ifdef HBRSTITCH
#include "libtarget/TgHashMap.h"
#endif
#endif

#include <algorithm>
#include <cstring>
#include <iterator>
#include <vector>
#include <set>
#include <iostream>

#include "../hbr/vertex.h"
#include "../hbr/face.h"
#include "../hbr/hierarchicalEdit.h"
#include "../hbr/vertexEdit.h"
#include "../hbr/creaseEdit.h"
#include "../hbr/allocator.h"

#include "../version.h"

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

template <class T> class HbrSubdivision;
template <class T> class HbrHalfedge;

template <class T> class HbrMesh {
public:
    HbrMesh(HbrSubdivision<T>* subdivision = 0, int fvarcount = 0, const int *fvarindices = 0, const int *fvarwidths = 0, int totalfvarwidth = 0
#ifdef HBRSTITCH
    , int stitchCount = 0
#endif
    );
    ~HbrMesh();

    // Create vertex with the indicated ID and data
    HbrVertex<T>* NewVertex(int id, const T &data);

    // Create vertex with the indicated data. The ID will be assigned
    // by the mesh.
    HbrVertex<T>* NewVertex(const T &data);

    // Create vertex without an ID - one will be assigned by the mesh,
    // and the data implicitly created will share the same id
    HbrVertex<T>* NewVertex();

    // Ask for vertex with the indicated ID
    HbrVertex<T>* GetVertex(int id) const {
        if (id >= nvertices) {
            return 0;
        } else {
            return vertices[id];
        }
    }        

    // Ask for client data associated with the vertex with the indicated ID
    void* GetVertexClientData(int id) const {
        if (id >= vertexClientData.size()) {        
            return 0;
        } else {
            return vertexClientData[id];
        }
    }

    // Set client data associated with the vertex with the indicated ID
    void SetVertexClientData(int id, void *data) {
        if (id >= vertexClientData.size()) {
            size_t oldsize = vertexClientData.size();
            vertexClientData.resize(nvertices);
            if (s_memStatsIncrement) {
                s_memStatsIncrement((vertexClientData.size() - oldsize) * sizeof(void*));
            }
        }
        vertexClientData[id] = data;
    }

    // Create face from a list of vertex IDs
    HbrFace<T>* NewFace(int nvertices, const int *vtx, int uindex);

    // Create face from a list of vertices
    HbrFace<T>* NewFace(int nvertices, HbrVertex<T>** vtx, HbrFace<T>* parent, int childindex);

    // "Create" a new uniform index
    int NewUniformIndex() { return ++maxUniformIndex; }

    // Finishes initialization of the mesh
    void Finish();

    // Remove the indicated face from the mesh
    void DeleteFace(HbrFace<T>* face);

    // Remove the indicated vertex from the mesh
    void DeleteVertex(HbrVertex<T>* vertex);

    // Returns number of vertices in the mesh
    int GetNumVertices() const;

    // Returns number of disconnected vertices in the mesh
    int GetNumDisconnectedVertices() const;

    // Returns number of faces in the mesh
    int GetNumFaces() const;

    // Returns number of coarse faces in the mesh
    int GetNumCoarseFaces() const;

    // Ask for face with the indicated ID
    HbrFace<T>* GetFace(int id) const;

    // Ask for client data associated with the face with the indicated ID
    void* GetFaceClientData(int id) const {
        if (id >= faceClientData.size()) {        
            return 0;
        } else {
            return faceClientData[id];
        }
    }

    // Set client data associated with the face with the indicated ID
    void SetFaceClientData(int id, void *data) {
        if (id >= faceClientData.size()) {
            size_t oldsize = faceClientData.size();
            faceClientData.resize(nfaces);
            if (s_memStatsIncrement) {
                s_memStatsIncrement((faceClientData.size() - oldsize) * sizeof(void*));
            }
        }
        faceClientData[id] = data;
    }
    
    // Returns a collection of all vertices in the mesh. This function
    // requires an output iterator; to get the vertices into a
    // std::vector, use GetVertices(std::back_inserter(myvector))
    template <typename OutputIterator>
    void GetVertices(OutputIterator vertices) const;

    // Applies operator to all vertices
    void ApplyOperatorAllVertices(HbrVertexOperator<T> &op) const;

    // Returns a collection of all faces in the mesh.  This function
    // requires an output iterator; to get the faces into a
    // std::vector, use GetFaces(std::back_inserter(myvector))
    template <typename OutputIterator>
    void GetFaces(OutputIterator faces) const;

    // Returns the subdivision method
    HbrSubdivision<T>* GetSubdivision() const { return subdivision; }

    // Return the number of facevarying variables
    int GetFVarCount() const { return fvarcount; }

    // Return a table of the start index of each facevarying variable
    const int *GetFVarIndices() const { return fvarindices; }

    // Return a table of the size of each facevarying variable
    const int *GetFVarWidths() const { return fvarwidths; }

    // Return the sum size of facevarying variables per vertex
    int GetTotalFVarWidth() const { return totalfvarwidth; }

#ifdef HBRSTITCH
    int GetStitchCount() const { return stitchCount; }
#endif

    void PrintStats(std::ostream& out);

    // Returns memory statistics
    size_t GetMemStats() const { return m_memory; }

    // Interpolate boundary management
    enum InterpolateBoundaryMethod {
        k_InterpolateBoundaryNone,
        k_InterpolateBoundaryEdgeOnly,
        k_InterpolateBoundaryEdgeAndCorner,
        k_InterpolateBoundaryAlwaysSharp
    };

    InterpolateBoundaryMethod GetInterpolateBoundaryMethod() const { return interpboundarymethod; }
    void SetInterpolateBoundaryMethod(InterpolateBoundaryMethod method) { interpboundarymethod = method; }
    InterpolateBoundaryMethod GetFVarInterpolateBoundaryMethod() const { return fvarinterpboundarymethod; }
    void SetFVarInterpolateBoundaryMethod(InterpolateBoundaryMethod method) { fvarinterpboundarymethod = method; }

    bool GetFVarPropagateCorners() const { return fvarpropagatecorners; }
    void SetFVarPropagateCorners(bool p) { fvarpropagatecorners = p; }

    // Register routines for keeping track of memory usage
    void RegisterMemoryRoutines(void (*increment)(unsigned long bytes), void (*decrement)(unsigned long bytes)) {
        m_faceAllocator.SetMemStatsIncrement(increment);
        m_faceAllocator.SetMemStatsDecrement(decrement);
        m_vertexAllocator.SetMemStatsIncrement(increment);
        m_vertexAllocator.SetMemStatsDecrement(decrement);
        s_memStatsIncrement = increment;
        s_memStatsDecrement = decrement;
    }

    // Add a vertex to consider for garbage collection. All
    // neighboring faces of that vertex will be examined to see if
    // they can be deleted
    void AddGarbageCollectableVertex(HbrVertex<T>* vertex) {
        if (!m_transientMode) {
            assert(vertex);
            if (!vertex->IsCollected()) {
                gcVertices.push_back(vertex); vertex->SetCollected();
            }
        }
    }

    // Apply garbage collection to the mesh
    void GarbageCollect();

    // Add a new hierarchical edit to the mesh
    void AddHierarchicalEdit(HbrHierarchicalEdit<T>* edit);

    // Return the hierarchical edits associated with the mesh
    const std::vector<HbrHierarchicalEdit<T>*> &GetHierarchicalEdits() const {
        return hierarchicalEdits;
    }

    // Return the hierarchical edits associated with the mesh at an
    // offset
    HbrHierarchicalEdit<T>** GetHierarchicalEditsAtOffset(int offset) {
        return &hierarchicalEdits[offset];
    }

    // Whether the mesh has certain types of edits
    bool HasVertexEdits() const { return hasVertexEdits; }
    bool HasCreaseEdits() const { return hasCreaseEdits; }

    void Unrefine(int numCoarseVerts, int numCoarseFaces) {

        for (int i = numCoarseFaces; i < maxFaceID; ++i) {
            HbrFace<T>* f = GetFace(i);
                if(f and not f->IsCoarse())
                    DeleteFace(f);
        }
        
        maxFaceID = numCoarseFaces;

        for(int i=numCoarseVerts; i<(int)vertices.size(); ++i ) {
            HbrVertex<T>* v = GetVertex(i);
            if(v and not v->IsReferenced())
                DeleteVertex(v);
        }
    }

    // When mode is true, the mesh is put in a "transient" mode,
    // i.e. all subsequent intermediate vertices/faces that are
    // created by subdivision are deemed temporary. This transient
    // data can be entirely freed by a subsequent call to
    // FreeTransientData(). Essentially, the mesh is checkpointed and
    // restored. This is useful when space is at a premium and
    // subdivided results are cached elsewhere. On the other hand,
    // repeatedly putting the mesh in and out of transient mode and
    // performing the same evaluations comes at a significant compute
    // cost.
    void SetTransientMode(bool mode) {
        m_transientMode = mode;
    }

    // Frees transient subdivision data; returns the mesh to a
    // checkpointed state prior to a call to SetTransientMode.
    void FreeTransientData();

    // Create new face children block for use by HbrFace
    HbrFaceChildren<T>* NewFaceChildren() {
        return m_faceChildrenAllocator.Allocate();
    }

    // Recycle face children block used by HbrFace
    void DeleteFaceChildren(HbrFaceChildren<T>* facechildren) {
        m_faceChildrenAllocator.Deallocate(facechildren);
    }

#ifdef HBRSTITCH
    void * GetStitchData(const HbrHalfedge<T>* edge) const {
        typename TgHashMap<const HbrHalfedge<T>*, void *>::const_iterator i =
            stitchData.find(edge);
        if (i != stitchData.end()) {
            return i->second;
        } else {
            return NULL;
        }
    }

    void SetStitchData(const HbrHalfedge<T>* edge, void *data) {
        stitchData[edge] = data;
    }
#endif

private:

    // Subdivision method used in this mesh
    HbrSubdivision<T>* subdivision;

    // Number of facevarying datums
    int fvarcount;

    // Start indices of the facevarying data we want to store
    const int *fvarindices;

    // Individual widths of the facevarying data we want to store
    const int *fvarwidths;

    // Total widths of the facevarying data
    const int totalfvarwidth;

#ifdef HBRSTITCH
    // Number of stitch edges per halfedge 
    const int stitchCount;

    // Client (sparse) data used on some halfedges
    TgHashMap<const HbrHalfedge<T>*, void *> stitchData;
#endif

    // Vertices which comprise this mesh
    std::vector<HbrVertex<T> *> vertices;
    int nvertices;

    // Client data associated with each face
    std::vector<void *> vertexClientData;

    // Faces which comprise this mesh
    std::vector<HbrFace<T> *> faces;
    int nfaces;

    // Client data associated with each face
    std::vector<void *> faceClientData;

    // Maximum vertex ID - may be needed when generating a unique
    // vertex ID
    int maxVertexID;

    // Maximum face ID - needed when generating a unique face ID
    int maxFaceID;

    // Maximum uniform index - needed to generate a new uniform index
    int maxUniformIndex;

    // Boundary interpolation method
    InterpolateBoundaryMethod interpboundarymethod;

    // Facevarying boundary interpolation method
    InterpolateBoundaryMethod fvarinterpboundarymethod;

    // Whether facevarying corners propagate their sharpness
    bool fvarpropagatecorners;

    // Memory statistics tracking routines
    HbrMemStatFunction s_memStatsIncrement;
    HbrMemStatFunction s_memStatsDecrement;

    // Vertices which may be garbage collected
    std::vector<HbrVertex<T>*> gcVertices;

    // List of vertex IDs which may be recycled
    std::set<int> recycleIDs;

    // Hierarchical edits. This vector is left unsorted until Finish()
    // is called, at which point it is sorted. After that point,
    // HbrFaces have pointers directly into this array so manipulation
    // of it should be avoided.
    std::vector<HbrHierarchicalEdit<T>*> hierarchicalEdits;

    // Size of faces (including 4 facevarying bits and stitch edges)
    const size_t m_faceSize;
    HbrAllocator<HbrFace<T> > m_faceAllocator;

    // Size of vertices (includes storage for one piece of facevarying data)
    const size_t m_vertexSize;
    HbrAllocator<HbrVertex<T> > m_vertexAllocator;

    // Allocator for face children blocks used by HbrFace
    HbrAllocator<HbrFaceChildren<T> > m_faceChildrenAllocator;
    
    // Memory used by this mesh alone, plus all its faces and vertices
    size_t m_memory;

    // Number of coarse faces. Initialized at Finish()
    int m_numCoarseFaces;

    // Flags which indicate whether the mesh has certain types of
    // edits
    unsigned hasVertexEdits:1;
    unsigned hasCreaseEdits:1;

    // True if the mesh is in "transient" mode, meaning all
    // vertices/faces that are created via NewVertex/NewFace should be
    // deemed temporary
    bool m_transientMode;

    // Vertices which are transient
    std::vector<HbrVertex<T>*> m_transientVertices;

    // Faces which are transient
    std::vector<HbrFace<T>*> m_transientFaces;

#ifdef HBR_ADAPTIVE
public:
    std::vector<std::pair<int, int> > const & GetSplitVertices() const {
        return m_splitVertices;
    }

protected:
    friend class HbrVertex<T>;
    
    void addSplitVertex(int splitIdx, int orgIdx) {
        m_splitVertices.push_back(std::pair<int,int>(splitIdx, orgIdx));
    }
    
private:
    std::vector<std::pair<int, int> >  m_splitVertices;
#endif
};

} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

} // end namespace OpenSubdiv

#include <algorithm>
#include "../hbr/mesh.h"
#include "../hbr/halfedge.h"

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

template <class T>
HbrMesh<T>::HbrMesh(HbrSubdivision<T>* s, int _fvarcount, const int *_fvarindices, const int *_fvarwidths, int _totalfvarwidth
#ifdef HBRSTITCH
    , int _stitchCount
#endif
    )
    : subdivision(s), fvarcount(_fvarcount), fvarindices(_fvarindices),
      fvarwidths(_fvarwidths), totalfvarwidth(_totalfvarwidth),
#ifdef HBRSTITCH
      stitchCount(_stitchCount),
#endif
      nvertices(0), nfaces(0), maxVertexID(0), maxFaceID(0), maxUniformIndex(0),
      interpboundarymethod(k_InterpolateBoundaryNone),
      fvarinterpboundarymethod(k_InterpolateBoundaryNone),
      fvarpropagatecorners(false),
      s_memStatsIncrement(0), s_memStatsDecrement(0),
      m_faceSize(sizeof(HbrFace<T>) + 4 *
                 ((fvarcount + 15) / 16 * sizeof(unsigned int)
#ifdef HBRSTITCH
                 + stitchCount * sizeof(StitchEdge*)
#endif
                  )),
      m_faceAllocator(&m_memory, 512, 0, 0, m_faceSize),
      m_vertexSize(sizeof(HbrVertex<T>) +
          (totalfvarwidth ? (sizeof(HbrFVarData<T>) + (totalfvarwidth - 1) * sizeof(float)) : 0)),
      m_vertexAllocator(&m_memory, 512, 0, 0, m_vertexSize),
      m_faceChildrenAllocator(&m_memory, 512, 0, 0),
      m_memory(0),
      m_numCoarseFaces(-1),
      hasVertexEdits(0),
      hasCreaseEdits(0),
      m_transientMode(false) {
}

template <class T>
HbrMesh<T>::~HbrMesh() {
    GarbageCollect();

    int i;
    if (!faces.empty()) {
        for (i = 0; i < nfaces; ++i) {
            if (faces[i]) {
                faces[i]->Destroy();
                m_faceAllocator.Deallocate(faces[i]);
            }
        }
        if (s_memStatsDecrement) {
            s_memStatsDecrement(faces.size() * sizeof(HbrFace<T>*));
        }
    }
    if (!vertices.empty()) {
        for (i = 0; i < nvertices; ++i) {
            if (vertices[i]) {
                vertices[i]->Destroy(this);
                m_vertexAllocator.Deallocate(vertices[i]);
            }
        }
        if (s_memStatsDecrement) {
            s_memStatsDecrement(vertices.size() * sizeof(HbrVertex<T>*));
        }
    }
    if (!vertexClientData.empty() && s_memStatsDecrement) {
        s_memStatsDecrement(vertexClientData.size() * sizeof(void*));
    }
    if (!faceClientData.empty() && s_memStatsDecrement) {
        s_memStatsDecrement(faceClientData.size() * sizeof(void*));
    }
    for (typename std::vector<HbrHierarchicalEdit<T>* >::iterator hi =
             hierarchicalEdits.begin(); hi != hierarchicalEdits.end(); ++hi) {
        delete *hi;
    }
}

template <class T>
HbrVertex<T>*
HbrMesh<T>::NewVertex(int id, const T &data) {
    HbrVertex<T>* v = 0;
    if (nvertices <= id) {
        while (nvertices <= maxVertexID) {
            nvertices *= 2;
            if (nvertices < 1) nvertices = 1;
        }
        size_t oldsize = vertices.size();
        vertices.resize(nvertices);
        if (s_memStatsIncrement) {
            s_memStatsIncrement((vertices.size() - oldsize) * sizeof(HbrVertex<T>*));
        }
    }
    v = vertices[id];
    if (v) {
        v->Destroy(this);
    } else {
        v = m_vertexAllocator.Allocate();
    }
    v->Initialize(id, data, GetTotalFVarWidth());
    vertices[id] = v;

    if (id >= maxVertexID) {
        maxVertexID = id + 1;
    }

    // Newly created vertices are always candidates for garbage
    // collection, until they get "owned" by someone who
    // IncrementsUsage on the vertex.
    AddGarbageCollectableVertex(v);

    // If mesh is in transient mode, add vertex to transient list
    if (m_transientMode) {
        m_transientVertices.push_back(v);
    }
    return v;
}

template <class T>
HbrVertex<T>*
HbrMesh<T>::NewVertex(const T &data) {
    // Pick an ID - either the maximum vertex ID or a recycled ID if
    // we can
    int id = maxVertexID;
    if (!recycleIDs.empty()) {
        id = *recycleIDs.begin();
        recycleIDs.erase(recycleIDs.begin());
    }
    if (id >= maxVertexID) {
        maxVertexID = id + 1;
    }
    return NewVertex(id, data);
}

template <class T>
HbrVertex<T>*
HbrMesh<T>::NewVertex() {
    // Pick an ID - either the maximum vertex ID or a recycled ID if
    // we can
    int id = maxVertexID;
    if (!recycleIDs.empty()) {
        id = *recycleIDs.begin();
        recycleIDs.erase(recycleIDs.begin());
    }
    if (id >= maxVertexID) {
        maxVertexID = id + 1;
    }
    T data(id);
    data.Clear();
    return NewVertex(id, data);
}

template <class T>
HbrFace<T>*
HbrMesh<T>::NewFace(int nv, const int *vtx, int uindex) {
    HbrVertex<T>** facevertices = reinterpret_cast<HbrVertex<T>**>(alloca(sizeof(HbrVertex<T>*) * nv));
    int i;
    for (i = 0; i < nv; ++i) {
        facevertices[i] = GetVertex(vtx[i]);
        if (!facevertices[i]) {
            return 0;
        }
    }
    HbrFace<T> *f = 0;
    // Resize if needed
    if (nfaces <= maxFaceID) {
        while (nfaces <= maxFaceID) {
            nfaces *= 2;
            if (nfaces < 1) nfaces = 1;
        }
        size_t oldsize = faces.size();
        faces.resize(nfaces);
        if (s_memStatsIncrement) {
            s_memStatsIncrement((faces.size() - oldsize) * sizeof(HbrVertex<T>*));
        }
    }
    f = faces[maxFaceID];
    if (f) {
        f->Destroy();
    } else {
        f = m_faceAllocator.Allocate();
    }
    f->Initialize(this, NULL, -1, maxFaceID, uindex, nv, facevertices, totalfvarwidth, 0);
    faces[maxFaceID] = f;
    maxFaceID++;
    // Update the maximum encountered uniform index
    if (uindex > maxUniformIndex) maxUniformIndex = uindex;

    // If mesh is in transient mode, add face to transient list
    if (m_transientMode) {
        m_transientFaces.push_back(f);
    }
    return f;
}

template <class T>
HbrFace<T>*
HbrMesh<T>::NewFace(int nv, HbrVertex<T> **vtx, HbrFace<T>* parent, int childindex) {
    HbrFace<T> *f = 0;
    // Resize if needed
    if (nfaces <= maxFaceID) {
        while (nfaces <= maxFaceID) {
            nfaces *= 2;
            if (nfaces < 1) nfaces = 1;
        }
        size_t oldsize = faces.size();        
        faces.resize(nfaces);
        if (s_memStatsIncrement) {
            s_memStatsIncrement((faces.size() - oldsize) * sizeof(HbrVertex<T>*));
        }
    }
    f = faces[maxFaceID];
    if (f) {
        f->Destroy();
    } else {
        f = m_faceAllocator.Allocate();
    }
    f->Initialize(this, parent, childindex, maxFaceID, parent ? parent->GetUniformIndex() : 0, nv, vtx, totalfvarwidth, parent ? parent->GetDepth() + 1 : 0);
    if (parent) {
        f->SetPtexIndex(parent->GetPtexIndex());
    }
    faces[maxFaceID] = f;
    maxFaceID++;

    // If mesh is in transient mode, add face to transient list
    if (m_transientMode) {
        m_transientFaces.push_back(f);
    }
    return f;
}

template <class T>
void
HbrMesh<T>::Finish() {
    int i, j;
    m_numCoarseFaces = 0;
    for (i = 0; i < nfaces; ++i) {
        if (faces[i]) {
            faces[i]->SetCoarse();
            m_numCoarseFaces++;
        }
    }

    std::vector<HbrVertex<T>*> vertexlist;
    GetVertices(std::back_inserter(vertexlist));
    for (typename std::vector<HbrVertex<T>*>::iterator vi = vertexlist.begin();
         vi != vertexlist.end(); ++vi) {
        HbrVertex<T>* vertex = *vi;
        if (vertex->IsConnected()) vertex->Finish();
    }
    // Finish may have added new vertices
    vertexlist.clear();
    GetVertices(std::back_inserter(vertexlist));

    // If interpolateboundary is on, process boundary edges
    if (interpboundarymethod == k_InterpolateBoundaryEdgeOnly || interpboundarymethod == k_InterpolateBoundaryEdgeAndCorner) {
        for (i = 0; i < nfaces; ++i) {
            if (HbrFace<T>* face = faces[i]) {
                int nv = face->GetNumVertices();
                for (int k = 0; k < nv; ++k) {
                    HbrHalfedge<T>* edge = face->GetEdge(k);
                    if (edge->IsBoundary()) {
                        edge->SetSharpness(HbrHalfedge<T>::k_InfinitelySharp);
                    }
                }
            }
        }
    }
    // Process corners
    if (interpboundarymethod == k_InterpolateBoundaryEdgeAndCorner) {
        for (typename std::vector<HbrVertex<T>*>::iterator vi = vertexlist.begin();
             vi != vertexlist.end(); ++vi) {
            HbrVertex<T>* vertex = *vi;
            if (vertex && vertex->IsConnected() && vertex->OnBoundary() && vertex->GetCoarseValence() == 2) {
                vertex->SetSharpness(HbrVertex<T>::k_InfinitelySharp);
            }
        }
    }

    // Sort the hierarchical edits
    if (!hierarchicalEdits.empty()) {
        HbrHierarchicalEditComparator<T> cmp;
        int nHierarchicalEdits = (int)hierarchicalEdits.size();
        std::sort(hierarchicalEdits.begin(), hierarchicalEdits.end(), cmp);
        // Push a sentinel null value - we rely upon this sentinel to
        // ensure face->GetHierarchicalEdits knows when to terminate
        hierarchicalEdits.push_back(0);
        j = 0;
        // Link faces to hierarchical edits
        for (i = 0; i < nfaces; ++i) {
            if (faces[i]) {
                while (j < nHierarchicalEdits && hierarchicalEdits[j]->GetFaceID() < i) {
                    ++j;
                }
                if (j < nHierarchicalEdits && hierarchicalEdits[j]->GetFaceID() == i) {
                    faces[i]->SetHierarchicalEdits(&hierarchicalEdits[j]);
                }
            }
        }
    }
}

template <class T>
void
HbrMesh<T>::DeleteFace(HbrFace<T>* face) {
    if (face->GetID() < nfaces) {
        HbrFace<T>* f = faces[face->GetID()];
        if (f == face) {
            faces[face->GetID()] = 0;
            face->Destroy();
            m_faceAllocator.Deallocate(face);
        }
    }
}

template <class T>
void
HbrMesh<T>::DeleteVertex(HbrVertex<T>* vertex) {
    HbrVertex<T> *v = GetVertex(vertex->GetID());
    if (v == vertex) {
        recycleIDs.insert(vertex->GetID());
        int id = vertex->GetID();
        vertices[id] = 0;
        vertex->Destroy(this);
        m_vertexAllocator.Deallocate(vertex);
    }
}

template <class T>
int
HbrMesh<T>::GetNumVertices() const {
    int count = 0;
    for (int i = 0; i < nvertices; ++i) {
        if (vertices[i]) count++;
    }
    return count;
}

template <class T>
int
HbrMesh<T>::GetNumDisconnectedVertices() const {
    int disconnected = 0;
    for (int i = 0; i < nvertices; ++i) {
        if (HbrVertex<T>* v = vertices[i]) {
                if (!v->IsConnected()) {
                    disconnected++;
                }
            }
        }
    return disconnected;
}

template <class T>
int
HbrMesh<T>::GetNumFaces() const {
    int count = 0;
    for (int i = 0; i < nfaces; ++i) {
        if (faces[i]) count++;
    }
    return count;
}

template <class T>
int
HbrMesh<T>::GetNumCoarseFaces() const {
    // Use the value computed by Finish() if it exists
    if (m_numCoarseFaces >= 0) return m_numCoarseFaces;
    // Otherwise we have to just count it up now
    int count = 0;
    for (int i = 0; i < nfaces; ++i) {
        if (faces[i] && faces[i]->IsCoarse()) count++;
    }
    return count;
}

template <class T>
HbrFace<T>*
HbrMesh<T>::GetFace(int id) const {
    if (id < nfaces) {
        return faces[id];
    }
    return 0;
}

template <class T>
template <typename OutputIterator>
void
HbrMesh<T>::GetVertices(OutputIterator lvertices) const {
    for (int i = 0; i < nvertices; ++i) {
        if (vertices[i]) *lvertices++ = vertices[i];
    }
}

template <class T>
void
HbrMesh<T>::ApplyOperatorAllVertices(HbrVertexOperator<T> &op) const {
    for (int i = 0; i < nvertices; ++i) {    
        if (vertices[i]) op(*vertices[i]);
    }
}

template <class T>
template <typename OutputIterator>
void
HbrMesh<T>::GetFaces(OutputIterator lfaces) const {
    for (int i = 0; i < nfaces; ++i) {
        if (faces[i]) *lfaces++ = faces[i];
    }
}

template <class T>
void
HbrMesh<T>::PrintStats(std::ostream &out) {
    int singular = 0;
    int sumvalence = 0;
    int i, nv = 0;
    int disconnected = 0;
    int extraordinary = 0;
    for (i = 0; i < nvertices; ++i) {
        if (HbrVertex<T>* v = vertices[i]) {
            nv++;
            if (v->IsSingular()) {
                out << "  singular: " << *v << "\n";
                singular++;
            } else if (!v->IsConnected()) {
                out << "  disconnected: " << *v << "\n";
                disconnected++;
            } else {
                if (v->IsExtraordinary()) {
                    extraordinary++;
                }
                sumvalence += v->GetValence();
            }
        }
    }
    out << "Mesh has " << nv << " vertices\n";
    out << "Total singular vertices " << singular << "\n";
    out << "Total disconnected vertices " << disconnected << "\n";
    out << "Total extraordinary vertices " << extraordinary << "\n";
    out << "Average valence " << (float) sumvalence / nv << "\n";

    int sumsides = 0;
    int numfaces = 0;
    for (i = 0; i < nfaces; ++i) {
        if (HbrFace<T>* f = faces[i]) {
            numfaces++;
            sumsides += f->GetNumVertices();
        }
    }
    out << "Mesh has " << nfaces << " faces\n";
    out << "Average sidedness " << (float) sumsides / nfaces << "\n";
}

template <class T>
void
HbrMesh<T>::GarbageCollect() {
    if (gcVertices.empty()) return;

    static const size_t gcthreshold = 4096;

    if (gcVertices.size() <= gcthreshold) return;
    // Go through the list of garbage collectable vertices and gather
    // up the neighboring faces of those vertices which can be garbage
    // collected.
    std::vector<HbrFace<T>*> killlist;
    std::vector<HbrVertex<T>*> vlist;

    // Process the vertices in the same order as they were collected
    // (gcVertices used to be declared as a std::deque, but that was
    // causing unnecessary heap traffic).
    int numprocessed = (int)gcVertices.size() - gcthreshold / 2;
    for (int i = 0; i < numprocessed; ++i) {
        HbrVertex<T>* v = gcVertices[i];
        v->ClearCollected();
        if (v->IsUsed()) continue;
        vlist.push_back(v);
        HbrHalfedge<T>* start = v->GetIncidentEdge(), *edge;
        edge = start;
        while (edge) {
            HbrFace<T>* f = edge->GetLeftFace();
            if (!f->IsCollected()) {
                f->SetCollected();
                killlist.push_back(f);
            }
            edge = v->GetNextEdge(edge);
            if (edge == start) break;
        }
    }

    gcVertices.erase(gcVertices.begin(), gcVertices.begin() + numprocessed);

    // Delete those faces
    for (typename std::vector<HbrFace<T>*>::iterator fi = killlist.begin(); fi != killlist.end(); ++fi) {
        if ((*fi)->GarbageCollectable()) {
            DeleteFace(*fi);
        } else {
            (*fi)->ClearCollected();
        }
    }

    // Delete as many vertices as we can
    for (typename std::vector<HbrVertex<T>*>::iterator vi = vlist.begin(); vi != vlist.end(); ++vi) {
        HbrVertex<T>* v = *vi;
        if (!v->IsReferenced()) {
            DeleteVertex(v);
        }
    }
}

template <class T>
void
HbrMesh<T>::AddHierarchicalEdit(HbrHierarchicalEdit<T>* edit) {
    hierarchicalEdits.push_back(edit);
    if (dynamic_cast<HbrVertexEdit<T>*>(edit) ||
        dynamic_cast<HbrMovingVertexEdit<T>*>(edit)) {
        hasVertexEdits = 1;
    } else if (dynamic_cast<HbrCreaseEdit<T>*>(edit)) {
        hasCreaseEdits = 1;
    }
}

template <class T>
void
HbrMesh<T>::FreeTransientData() {
    // When purging transient data, we must clear the faces first
    for (typename std::vector<HbrFace<T>*>::iterator fi = m_transientFaces.begin();
         fi != m_transientFaces.end(); ++fi) {
        DeleteFace(*fi);
    }
    // The vertices should now be trivial to purge after the transient
    // faces have been cleared
    for (typename std::vector<HbrVertex<T>*>::iterator vi = m_transientVertices.begin();
         vi != m_transientVertices.end(); ++vi) {
        DeleteVertex(*vi);
    }
    m_transientVertices.clear();
    m_transientFaces.clear();
    // Reset max face ID
    int i;
    for (i = nfaces - 1; i >= 0; --i) {
        if (faces[i]) {
            maxFaceID = i + 1;
            break;
        }
    }
    // Reset max vertex ID
    for (i = nvertices - 1; i >= 0; --i) {
        if (vertices[i]) {
            maxVertexID = i + 1;
            break;
        }
    }
}

} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

} // end namespace OpenSubdiv

#endif /* OPENSUBDIV3_HBRMESH_H */
