//
//   Copyright 2013 Pixar
//
//   Licensed under the Apache License, Version 2.0 (the "Apache License")
//   with the following modification; you may not use this file except in
//   compliance with the Apache License and the following modification to it:
//   Section 6. Trademarks. is deleted and replaced with:
//
//   6. Trademarks. This License does not grant permission to use the trade
//      names, trademarks, service marks, or product names of the Licensor
//      and its affiliates, except as required to comply with Section 4(c) of
//      the License and to reproduce the content of the NOTICE file.
//
//   You may obtain a copy of the Apache License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the Apache License with the above modification is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//   KIND, either express or implied. See the Apache License for the specific
//   language governing permissions and limitations under the Apache License.
//

#ifndef FAR_PATCH_TABLES_FACTORY_H
#define FAR_PATCH_TABLES_FACTORY_H

#include "../version.h"

#include "../far/patchTables.h"

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

/// \brief A specialized factory for feature adaptive FarPatchTables
///
/// FarPatchTables contain the lists of vertices for each patch of an adaptive
/// mesh representation. This  specialized factory is a private helper for FarMeshFactory.
///
/// Separating the factory allows us to isolate Far data structures from Hbr dependencies.
///
template <class T> class FarPatchTablesFactory {

protected:
    template <class X, class Y> friend class FarMeshFactory;

    /// \brief Factory constructor for feature-adaptive meshes
    ///
    /// @param mesh        Hbr mesh to generate tables for
    ///
    /// @param nfaces      Number of faces in the mesh (cached for speed)
    ///
    /// @param remapTable  Vertex remapping table generated by FarMeshFactory
    ///
    FarPatchTablesFactory( HbrMesh<T> const * mesh, int nfaces, std::vector<int> const & remapTable );
    
    /// \brief Returns a feature-adaptive FarPatchTables instance 
    ///
    /// @param maxlevel         Highest level of refinement processed
    ///
    /// @param maxvalence       Maximum vertex valence in the mesh
    ///
    /// @param requireFVarData  Flag for generating face-varying data
    ///
    /// @return                 A new instance of FarPatchTables
    ///
    FarPatchTables * Create( int maxlevel, int maxvalence, bool requireFVarData=false );


    typedef std::vector<std::vector< HbrFace<T> *> > FacesList;

    /// \brief Factory constructor for uniform meshes
    ///
    /// @param mesh             Hbr mesh to generate tables for
    ///
    /// @param flist            Vectors of pointers to HbrFace<T> for each level
    ///                         of subdivision
    ///
    /// @param requireFVarData  Flag for generating face-varying data
    ///
    /// @param firstLevel       First level of subdivision to use when building the
    ///                         PatchArrayVector (default -1 means only generate
    ///                         a single patch array for the highest level of 
    ///                         subdivision)
    ///
    /// @param remapTable       Vertex remapping table generated by FarMeshFactory
    ///
    /// @return                 A new instance of FarPatchTables
    ///
    static FarPatchTables * Create( HbrMesh<T> const * mesh, 
                                    FacesList const & flist,
                                    std::vector<int> const & remapTable, 
                                    int firstLevel=-1, 
                                    bool requireFVarData=false );
    
private:

    typedef FarPatchTables::Descriptor Descriptor;

    // Returns true if one of v's neighboring faces has vertices carrying the tag "wasTagged"
    static bool vertexHasTaggedNeighbors(HbrVertex<T> * v);

    // Returns the rotation for a boundary patch
    static unsigned char computeBoundaryPatchRotation( HbrFace<T> * f );

    // Returns the rotation for a corner patch
    static unsigned char computeCornerPatchRotation( HbrFace<T> * f );

    // Populates an array of indices with the "one-ring" vertices for the given face
    void getOneRing( HbrFace<T> * f, int ringsize, unsigned int const * remap, unsigned int * result );
    
    // Populates the Gregory patch quad offsets table
    static void getQuadOffsets( HbrFace<T> * f, unsigned int * result );

    // Iterates through the faces of an HbrMesh and tags the _adaptiveFlags on faces and vertices
    void tagAdaptivePatches( HbrMesh<T> const * mesh, int nfaces );
    
    // Hbr mesh accessor
    HbrMesh<T> const * getMesh() const { return _mesh; }

    // Number of faces in the Hbr mesh (cached for speed)
    int getNumFaces() const { return _nfaces; }

    // The number of patch arrays in the mesh
    int getNumPatchArrays() const;
    
    // The number of patches in the mesh
    static int getNumPatches( FarPatchTables::PatchArrayVector const & parrays );

    // Reserves tables based on the contents of the PatchArrayVector
    static void allocateTables( FarPatchTables * tables, int fvarwidth );
    
    // A convenience container for the different types of feature adaptive patches
    template<class TYPE> struct PatchTypes {
        TYPE R,       // regular patch 
             B[4],    // boundary patch (4 rotations)
             C[4],    // corner patch (4 rotations)
             G[2];    // gregory patch (boundary & corner)
        
        PatchTypes() { memset(this, 0, sizeof(PatchTypes<TYPE>)); }
        
        // Returns the number of patches based on the patch type in the descriptor
        TYPE & getValue( FarPatchTables::Descriptor desc );
        
        // Counts the number of arrays required to store each type of patch used
        // in the primitive
        int getNumPatchArrays() const;
    };

    typedef PatchTypes<unsigned int*>   CVPointers;
    typedef PatchTypes<FarPatchParam *> ParamPointers;
    typedef PatchTypes<float *>         FVarPointers;
    typedef PatchTypes<int>             Counter;
    
    // Creates a PatchArray and appends it to a vector and keeps track of both
    // vertex and patch offsets
    void pushPatchArray( FarPatchTables::Descriptor desc,
                         FarPatchTables::PatchArrayVector & parray,
                         Counter & counter,                                 
                         int * voffset, int * poffset, int * qoffset );

    Counter _patchCtr[6];  // counters for full and transition patches
             
    HbrMesh<T> const * _mesh;

    // Reference to the vertex remapping table generated by FarMeshFactory
    std::vector<int> const &_remapTable;
    
    int _nfaces;
};

// True if the surrounding faces are "tagged" (unsupported feature : watertight 
// critical patches)
template <class T> bool
FarPatchTablesFactory<T>::vertexHasTaggedNeighbors(HbrVertex<T> * v) {

    assert(v);
    
    HbrHalfedge<T> * start = v->GetIncidentEdge(),
                   * next=start;
    do {
        HbrFace<T> * right = next->GetRightFace(),
                   * left = next->GetLeftFace();

        if (right and (not right->hasTaggedVertices()))
            return true;

        if (left and (not left->hasTaggedVertices()))
            return true;

        next = v->GetNextEdge(next);

    } while (next and next!=start);
    return false;
}

// Returns a rotation index for boundary patches (range [0-3])
template <class T> unsigned char 
FarPatchTablesFactory<T>::computeBoundaryPatchRotation( HbrFace<T> * f ) {
    unsigned char rot=0;
    for (unsigned char i=0; i<4;++i) {
        if (f->GetVertex(i)->OnBoundary() and
            f->GetVertex((i+1)%4)->OnBoundary())
            break;
        ++rot;
    }
    return rot;
}

// Returns a rotation index for corner patches (range [0-3])
template <class T> unsigned char 
FarPatchTablesFactory<T>::computeCornerPatchRotation( HbrFace<T> * f ) {
    unsigned char rot=0;
    for (unsigned char i=0; i<4; ++i) {
        if (not f->GetVertex((i+3)%4)->OnBoundary())
            break;
        ++rot;
    }
    return rot;
}

// Reserves tables based on the contents of the PatchArrayVector
template <class T> void
FarPatchTablesFactory<T>::allocateTables( FarPatchTables * tables, int fvarwidth ) {

    int nverts = tables->GetNumControlVertices(),
        npatches = getNumPatches(tables->GetPatchArrayVector());

    if (nverts==0 or npatches==0)
        return;

    tables->_patches.resize( nverts );

    tables->_paramTable.resize( npatches );

    if (fvarwidth>0) {
        tables->_fvarTable.resize( npatches * 4 * fvarwidth );
    }
}

// Uniform mesh factory (static function because it requires no cached state)
template <class T> FarPatchTables * 
FarPatchTablesFactory<T>::Create( HbrMesh<T> const * mesh, FacesList const & flist, std::vector<int> const & remapTable, int firstLevel, bool requireFVarData ) {

    if (flist.size()<2)
        return 0;

    FarPatchTables * result = new FarPatchTables(0);
    
    bool isLoop = FarMeshFactory<T,T>::isLoop(mesh);

    int nv = isLoop ? 3 : 4;

    int firstArray = firstLevel > -1 ? firstLevel : (int)flist.size()-1; 
    
    // Populate the patch array descriptors
    
    FarPatchTables::PatchArrayVector & parray = result->_patchArrays;
    parray.reserve( (int)flist.size() - firstArray );
    
    Descriptor desc( isLoop ? FarPatchTables::TRIANGLES : FarPatchTables::QUADS, FarPatchTables::NON_TRANSITION, 0 );

    for (int i=1, poffset=0, voffset=0; i<(int)flist.size(); ++i) {
    
        int nfaces = (int)flist[i].size();

        if (i>=firstArray) {
            parray.push_back( FarPatchTables::PatchArray(desc, voffset, poffset, nfaces, 0 ) );
            
            voffset += nfaces * nv;
            poffset += nfaces;
        }
    }
    
    int fvarwidth = requireFVarData ? mesh->GetTotalFVarWidth() : 0;

    // Populate the patch / param / fvar tables
    
    allocateTables( result, fvarwidth ); 

    unsigned int  * iptr = &result->_patches[0];
    FarPatchParam * pptr = &result->_paramTable[0];
    float         * fptr = fvarwidth>0 ? &result->_fvarTable[0] : 0;
    
    for (int level=firstArray; level<(int)flist.size(); ++level) {

        for (int i=0; i<(int)flist[level].size(); ++i) {
            HbrFace<T> * f = flist[level][i];
            assert( f and f->GetNumVertices()==nv);

            for (int j=0; j<f->GetNumVertices(); ++j) {
                *iptr++ = remapTable[f->GetVertex(j)->GetID()];
            }
            
            pptr = computePatchParam(f, pptr);
            
            if (fvarwidth>0)
                fptr = computeFVarData(f, fvarwidth, fptr, /*isAdaptive=*/false);
        }
    }

    return result;
}

// Feature adaptive mesh factory
template <class T>
FarPatchTablesFactory<T>::FarPatchTablesFactory( HbrMesh<T> const * mesh, int nfaces,  std::vector<int> const & remapTable ) :
    _mesh(mesh),
    _remapTable(remapTable),
    _nfaces(nfaces)
{
    assert(mesh and nfaces>0);

    // First pass : identify transition / watertight-critical
    for (int i=0; i<nfaces; ++i) {

        HbrFace<T> * f = mesh->GetFace(i);

        if (f->_adaptiveFlags.isTagged and (not f->IsHole())) {
            HbrVertex<T> * v = f->Subdivide();
            assert(v);
            v->_adaptiveFlags.wasTagged=true;
        }

        int nv = f->GetNumVertices();
        for (int j=0; j<nv; ++j) {
        
            if (f->IsCoarse())
                f->GetVertex(j)->_adaptiveFlags.wasTagged=true;
                    
            HbrHalfedge<T> * e = f->GetEdge(j);

            // Flag transition edge that require a triangulated transition
            if (f->_adaptiveFlags.isTagged) {
                e->_adaptiveFlags.isTriangleHead=true;

                // Both half-edges need to be tagged if an opposite exists
                if (e->GetOpposite())
                    e->GetOpposite()->_adaptiveFlags.isTriangleHead=true;
            }
            
            HbrFace<T> * left = e->GetLeftFace(),
                       * right = e->GetRightFace();
            
            if (not (left and right))
                continue;
            
            // a tagged edge w/ no children is inside a hole
            if (e->HasChild() and (left->_adaptiveFlags.isTagged ^ right->_adaptiveFlags.isTagged)) {
                
                e->_adaptiveFlags.isTransition = true;
                
                HbrVertex<T> * child = e->Subdivide();
                assert(child);
                
                // These edges will require extra rows of CVs to maintain water-tightness
                // Note : vertices inside holes have no children
                if (e->GetOrgVertex()->HasChild()) {
                    HbrHalfedge<T> * org = child->GetEdge(e->GetOrgVertex()->Subdivide());
                    if (org)
                        org->_adaptiveFlags.isWatertightCritical=true;
                }

                if (e->GetDestVertex()->HasChild()) {
                    HbrHalfedge<T> * dst = child->GetEdge(e->GetDestVertex()->Subdivide());
                    if (dst)
                        dst->_adaptiveFlags.isWatertightCritical=true;
                }
            }
        }
    }

    // Second pass : count boundaries / identify transition constellation
    for (int i=0; i<nfaces; ++i) {

        HbrFace<T> * f = mesh->GetFace(i);

        if (mesh->GetSubdivision()->FaceIsExtraordinary(mesh,f))
            continue;
            
        if (f->IsHole())
            continue;
           
        bool isTagged=0, wasTagged=0, isConnected=0, isWatertightCritical=0, isExtraordinary=0;
        int  triangleHeads=0, boundaryVerts=0;

        int nv = f->GetNumVertices();
        for (int j=0; j<nv; ++j) {
            HbrVertex<T> * v = f->GetVertex(j);
            
            if (v->OnBoundary()) {
                boundaryVerts++;
                
                // Boundary vertices with valence higher than 3 aren't Full Boundary
                // patches, they are Gregory Boundary patches.
                if (v->IsSingular() or v->GetValence()>3)
                    isExtraordinary=true;
                    
            } else if (v->IsExtraordinary())
                isExtraordinary=true;
                
            if (f->GetParent() and (not isWatertightCritical))
                isWatertightCritical = vertexHasTaggedNeighbors(v);
            
            if (v->_adaptiveFlags.isTagged)
                isTagged=1;

            if (v->_adaptiveFlags.wasTagged)
                wasTagged=1;

            // Count the number of triangle heads to find which transition
            // pattern to use.
            HbrHalfedge<T> * e = f->GetEdge(j);
            if (e->_adaptiveFlags.isTriangleHead) {

                ++triangleHeads;
                if (f->GetEdge((j+1)%4)->_adaptiveFlags.isTriangleHead)
                    isConnected=true;
            }
        }

        f->_adaptiveFlags.bverts=boundaryVerts;
        f->_adaptiveFlags.isCritical=isWatertightCritical;

        // Regular Boundary Patch
        if (wasTagged)
            // XXXX manuelk - need to implement end patches
            f->_adaptiveFlags.patchType = HbrFace<T>::kEnd;
        
        if (f->_adaptiveFlags.isTagged)
            continue;
        
        assert(f->_adaptiveFlags.rots==0 and nv==4);
        
        if (not isTagged and wasTagged) {

            if (triangleHeads==0) {

                 if (not isExtraordinary and boundaryVerts!=1) {

                    // Full Patches
                    f->_adaptiveFlags.patchType = HbrFace<T>::kFull;

                    switch (boundaryVerts) {
                    
                        case 0 : {   // Regular patch
                                     _patchCtr[0].R++;
                                 } break; 
                        
                        case 2 : {   // Boundary patch
                                     f->_adaptiveFlags.rots=computeBoundaryPatchRotation(f);
                                     _patchCtr[0].B[0]++;
                                 } break;
                        
                        case 3 : {   // Corner patch
                                     f->_adaptiveFlags.rots=computeCornerPatchRotation(f);
                                     _patchCtr[0].C[0]++;
                                 } break;
                        
                        default : break;
                    }
                } else {
                
                    // Default to Gregory Patch
                    f->_adaptiveFlags.patchType = HbrFace<T>::kGregory;

                    switch (boundaryVerts) {
                    
                        case 0 : {   // Regular Gregory patch
                                     _patchCtr[0].G[0]++;
                                 } break; 
 
                        
                        default : { // Boundary Gregory patch
                                     _patchCtr[0].G[1]++;
                                  } break;
                    }
                }
            
            } else {
                
                // Transition Patch
                
                // Resolve transition constellation : 5 types (see p.5 fig. 7)
                switch (triangleHeads) {

                    case 1 : {   for (unsigned char j=0; j<4; ++j) {
                                     if (f->GetEdge(j)->IsTriangleHead())
                                         break;
                                     f->_adaptiveFlags.rots++;
                                 }
                                 f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition0;
                            } break;

                    case 2 : {   for (unsigned char j=0; j<4; ++j) {
                                     if (isConnected) {
                                         if (f->GetEdge(j)->IsTriangleHead() and 
                                             f->GetEdge((j+3)%4)->IsTriangleHead())
                                             break;
                                     } else {
                                         if (f->GetEdge(j)->IsTriangleHead())
                                             break;
                                     }
                                     f->_adaptiveFlags.rots++;
                                 }

                                 if (isConnected)
                                    f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition1;
                                 else
                                    f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition4;
                             } break;

                    case 3 : {   for (unsigned char j=0; j<4; ++j) {
                                     if (not f->GetEdge(j)->IsTriangleHead())
                                         break;
                                     f->_adaptiveFlags.rots++;
                                 }
                                 f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition2;
                             } break;

                    case 4 : f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition3;
                             break;

                    default: break;
                }

                int tidx = f->_adaptiveFlags.transitionType;
                assert(tidx>=0);
                
                // Correct rotations for corners & boundaries
                if (not isExtraordinary and boundaryVerts!=1) {
                    
                    switch (boundaryVerts) {
                    
                        case 0 : {   // regular patch
                                     _patchCtr[tidx+1].R++;
                                 } break; 
                        
                        case 2 : {   // boundary patch
                                     unsigned char rot=computeBoundaryPatchRotation(f);

                                     f->_adaptiveFlags.brots=(4-f->_adaptiveFlags.rots+rot)%4;
                                     
                                     f->_adaptiveFlags.rots=rot; // override the transition rotation
                                     
                                     _patchCtr[tidx+1].B[f->_adaptiveFlags.brots]++;
                                 } break;
                        
                        case 3 : {   // corner patch
                                     unsigned char rot=computeCornerPatchRotation(f);
                                     
                                     f->_adaptiveFlags.brots=(4-f->_adaptiveFlags.rots+rot)%4;
                                     
                                     f->_adaptiveFlags.rots=rot; // override the transition rotation
                                     
                                     _patchCtr[tidx+1].C[f->_adaptiveFlags.brots]++;
                                 } break;
                        
                        default : assert(0); break;
                    }
                } else {
                    // Use Gregory Patch transition ?
                }
            }
        }
    }
}

template <class T> 
    template <class TYPE> TYPE & 
FarPatchTablesFactory<T>::PatchTypes<TYPE>::getValue( FarPatchTables::Descriptor desc ) {
    switch (desc.GetType()) {
        case FarPatchTables::REGULAR          : return R;
        case FarPatchTables::BOUNDARY         : return B[desc.GetRotation()];
        case FarPatchTables::CORNER           : return C[desc.GetRotation()];
        case FarPatchTables::GREGORY          : return G[0];
        case FarPatchTables::GREGORY_BOUNDARY : return G[1];
        default : assert(0);
    }
    // can't be reached (suppress compiler warning)
    return R;
}

template <class T> 
    template <class TYPE> int
FarPatchTablesFactory<T>::PatchTypes<TYPE>::getNumPatchArrays() const {

    int result=0;

    if (R) ++result;
    for (int i=0; i<4; ++i) {
        if (B[i]) ++result;
        if (C[i]) ++result;
        if ((i<2) and G[i]) ++result;
    }
    return result;
}

template <class T> int 
FarPatchTablesFactory<T>::getNumPatchArrays() const {

    int result = 0;
    
    for (int i=0; i<6; ++i)
        result += _patchCtr[i].getNumPatchArrays();
        
    return result;
}

template <class T> int 
FarPatchTablesFactory<T>::getNumPatches( FarPatchTables::PatchArrayVector const & parrays ) {

    int result=0;
    for (int i=0; i<(int)parrays.size(); ++i) {
        result += parrays[i].GetNumPatches();
    }

    return result;
}

template <class T> void
FarPatchTablesFactory<T>::pushPatchArray( FarPatchTables::Descriptor desc, 
                                          FarPatchTables::PatchArrayVector & parray, 
                                          typename FarPatchTablesFactory<T>::Counter & counter, 
                                          int * voffset, int * poffset, int * qoffset ) {

    int npatches = counter.getValue( desc );
    
    if (npatches>0) {
        parray.push_back( FarPatchTables::PatchArray(desc, *voffset, *poffset, npatches, *qoffset) );

        *voffset += npatches * desc.GetNumControlVertices();
        *poffset += npatches;
        *qoffset += (desc.GetType() == FarPatchTables::GREGORY) ? npatches * desc.GetNumControlVertices() : 0;
    }
}

template <class T> FarPatchTables *
FarPatchTablesFactory<T>::Create( int maxlevel, int maxvalence, bool requireFVarData ) {

    static const unsigned int remapRegular        [16] = {5,6,10,9,4,0,1,2,3,7,11,15,14,13,12,8};
    static const unsigned int remapRegularBoundary[12] = {1,2,6,5,0,3,7,11,10,9,8,4};
    static const unsigned int remapRegularCorner  [ 9] = {1,2,5,4,0,8,7,6,3};

    assert(getMesh() and getNumFaces()>0);

    FarPatchTables * result = new FarPatchTables(maxvalence);
    
    // Populate the patch array descriptors
    FarPatchTables::PatchArrayVector & parray = result->_patchArrays;
    parray.reserve( getNumPatchArrays() );

    int voffset=0, poffset=0, qoffset=0;


    for (Descriptor::iterator it=Descriptor::begin(); it!=Descriptor::end(); ++it) {
        pushPatchArray( *it, parray, _patchCtr[it->GetPattern()], &voffset, &poffset, &qoffset );
    }

    int fvarwidth = requireFVarData ? getMesh()->GetTotalFVarWidth() : 0;
    
    allocateTables( result, fvarwidth ); 

    FarPatchTables::QuadOffsetTable quad_G_C0; // Quad-offsets tables (for Gregory patches)
    quad_G_C0.resize(_patchCtr[0].G[0]*4);

    FarPatchTables::QuadOffsetTable quad_G_C1;
    quad_G_C1.resize(_patchCtr[0].G[1]*4);

    FarPatchTables::QuadOffsetTable::value_type *quad_G_C0_P = quad_G_C0.empty() ? 0 : &quad_G_C0[0];
    FarPatchTables::QuadOffsetTable::value_type *quad_G_C1_P = quad_G_C1.empty() ? 0 : &quad_G_C1[0];


    // Setup convenience pointers at the beginning of each patch array for each
    // table (patches, ptex, fvar)
    CVPointers    iptrs[6];
    ParamPointers pptrs[6];
    FVarPointers  fptrs[6];

    for (Descriptor::iterator it=Descriptor::begin(); it!=Descriptor::end(); ++it) {
    
        FarPatchTables::PatchArray * pa = result->findPatchArray(*it);

        if (not pa)
            continue;

        iptrs[(int)pa->GetDescriptor().GetPattern()].getValue( *it ) = &result->_patches[pa->GetVertIndex()];
        pptrs[(int)pa->GetDescriptor().GetPattern()].getValue( *it ) = &result->_paramTable[pa->GetPatchIndex()];
        
        if (fvarwidth>0)
            fptrs[(int)pa->GetDescriptor().GetPattern()].getValue( *it ) = &result->_fvarTable[pa->GetPatchIndex() * 4 * fvarwidth];
    }
 
    // Populate patch index tables with vertex indices
    for (int i=0; i<getNumFaces(); ++i) {
        
        HbrFace<T> * f = getMesh()->GetFace(i);
    
        if (not f->isTransitionPatch() ) {
        
            // Full / End patches

            if (f->_adaptiveFlags.patchType==HbrFace<T>::kFull) {
                if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {

                    switch (f->_adaptiveFlags.bverts) {
                        case 0 : {   // Regular Patch (16 CVs)
                                     getOneRing(f, 16, remapRegular, iptrs[0].R);
                                     iptrs[0].R+=16;
                                     pptrs[0].R = computePatchParam(f, pptrs[0].R);
                                     fptrs[0].R = computeFVarData(f, fvarwidth, fptrs[0].R, /*isAdaptive=*/true);
                                 } break;

                        case 2 : {   // Boundary Patch (12 CVs)
                                     f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
                                     getOneRing(f, 12, remapRegularBoundary, iptrs[0].B[0]);
                                     iptrs[0].B[0]+=12;
                                     pptrs[0].B[0] = computePatchParam(f, pptrs[0].B[0]);
                                     fptrs[0].B[0] = computeFVarData(f, fvarwidth, fptrs[0].B[0], /*isAdaptive=*/true);
                                 } break;

                        case 3 : {   // Corner Patch (9 CVs)
                                     f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
                                     getOneRing(f, 9, remapRegularCorner, iptrs[0].C[0]);
                                     iptrs[0].C[0]+=9;
                                     pptrs[0].C[0] = computePatchParam(f, pptrs[0].C[0]);
                                     fptrs[0].C[0] = computeFVarData(f, fvarwidth, fptrs[0].C[0], /*isAdaptive=*/true);
                                 } break;

                        default : assert(0);
                    }
                }
            } else if (f->_adaptiveFlags.patchType==HbrFace<T>::kGregory) {

                if (f->_adaptiveFlags.bverts==0) {
                
                    // Gregory Regular Patch (4 CVs + quad-offsets / valence tables)
                    for (int j=0; j<4; ++j)
                        iptrs[0].G[0][j] = _remapTable[f->GetVertex(j)->GetID()];
                    iptrs[0].G[0]+=4;
                    getQuadOffsets(f, quad_G_C0_P);
                    quad_G_C0_P += 4;
                    pptrs[0].G[0] = computePatchParam(f, pptrs[0].G[0]);
                    fptrs[0].G[0] = computeFVarData(f, fvarwidth, fptrs[0].G[0], /*isAdaptive=*/true);
                } else {
                
                    // Gregory Boundary Patch (4 CVs + quad-offsets / valence tables)
                    for (int j=0; j<4; ++j)
                        iptrs[0].G[1][j] = _remapTable[f->GetVertex(j)->GetID()];
                    iptrs[0].G[1]+=4;
                    getQuadOffsets(f, quad_G_C1_P);
                    quad_G_C1_P += 4;
                    pptrs[0].G[1] = computePatchParam(f, pptrs[0].G[1]);
                    fptrs[0].G[1] = computeFVarData(f, fvarwidth, fptrs[0].G[1], /*isAdaptive=*/true);
                }
            } else {
                // XXXX manuelk - end patches here
            }
        } else {
         
            // Transition patches
            
            int tcase = f->_adaptiveFlags.transitionType;
            assert( tcase>=HbrFace<T>::kTransition0 and tcase<=HbrFace<T>::kTransition4 );
            ++tcase;  // TransitionPattern begin with NON_TRANSITION

            if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {

                switch (f->_adaptiveFlags.bverts) {
                    case 0 : {   // Regular Transition Patch (16 CVs)
                                 getOneRing(f, 16, remapRegular, iptrs[tcase].R);

                                 iptrs[tcase].R+=16;
                                 pptrs[tcase].R = computePatchParam(f, pptrs[tcase].R);
                                 fptrs[tcase].R = computeFVarData(f, fvarwidth, fptrs[tcase].R, /*isAdaptive=*/true);
                             } break;

                    case 2 : {   // Boundary Transition Patch (12 CVs)
                                 unsigned rot = f->_adaptiveFlags.brots;
                                 getOneRing(f, 12, remapRegularBoundary, iptrs[tcase].B[rot]);
                                 iptrs[tcase].B[rot]+=12;
                                 pptrs[tcase].B[rot] = computePatchParam(f, pptrs[tcase].B[rot]);
                                 fptrs[tcase].B[rot] = computeFVarData(f, fvarwidth, fptrs[tcase].B[rot], /*isAdaptive=*/true);
                             } break;

                    case 3 : {   // Corner Transition Patch (9 CVs)
                                 unsigned rot = f->_adaptiveFlags.brots;
                                 getOneRing(f, 9, remapRegularCorner, iptrs[tcase].C[rot]);
                                 iptrs[tcase].C[rot]+=9;
                                 pptrs[tcase].C[rot] = computePatchParam(f, pptrs[tcase].C[rot]);
                                 fptrs[tcase].C[rot] = computeFVarData(f, fvarwidth, fptrs[tcase].C[rot], /*isAdaptive=*/true);
                             } break;
                }
            } else
                // No transition Gregory patches
                assert(false);
        }
    }
     
    // Build Gregory patches vertex valence indices table
    if ((_patchCtr[0].G[0] > 0) or (_patchCtr[0].G[1] > 0)) {

        // MAX_VALENCE is a property of hardware shaders and needs to be matched in OSD
        const int perVertexValenceSize = 2*maxvalence + 1;

        const int nverts = getMesh()->GetNumVertices();

        FarPatchTables::VertexValenceTable & table = result->_vertexValenceTable;
        table.resize(nverts * perVertexValenceSize);

        class GatherNeighborsOperator : public HbrVertexOperator<T> {
        public:
            HbrVertex<T> * center;
            FarPatchTables::VertexValenceTable & table;
            int offset, valence;
            std::vector<int> const & remap;

            GatherNeighborsOperator(FarPatchTables::VertexValenceTable & itable, int ioffset, HbrVertex<T> * v, std::vector<int> const & iremap) : 
                center(v), table(itable), offset(ioffset), valence(0), remap(iremap) { }

            // Operator iterates over neighbor vertices of v and accumulates
            // pairs of indices the neighbor and diagonal vertices
            //
            //          Regular case
            //                                           Boundary case
            //      o ------- o      D3 o
            //   D0        N0 |         |
            //                |         |             o ------- o      D2 o
            //                |         |          D0        N0 |         |
            //                |         |                       |         |
            //      o ------- o ------- o                       |         |
            //   N1 |       V |      N3                         |         |
            //      |         |                       o ------- o ------- o
            //      |         |                    N1          V       N2
            //      |         |
            //      o         o ------- o
            //   D1         N2        D2
            //
            virtual void operator() (HbrVertex<T> &v) {

                table[offset++] = remap[v.GetID()];

                HbrVertex<T> * diagonal=&v;

                HbrHalfedge<T> * e = center->GetEdge(&v);
                if ( e ) {
                    // If v is on a boundary, there may not be a diagonal vertex
                    diagonal = e->GetNext()->GetDestVertex();
                }
                //else {
                //    diagonal = v.GetQEONext( center );
                //}

                table[offset++] = remap[diagonal->GetID()];

                ++valence;
            }
        };

        for (int i=0; i<nverts; ++i) {
            HbrVertex<T> * v = getMesh()->GetVertex(i);

            int outputVertexID = _remapTable[v->GetID()];
            int offset = outputVertexID * perVertexValenceSize;

            // feature adaptive refinement can generate un-connected face-vertices
            // that have a valence of 0
            if (not v->IsConnected()) {
                assert( v->GetParentFace() );
                table[offset] = 0;
                continue;
            }

            // "offset+1" : the first table entry is the vertex valence, which
            // is gathered by the operator (see note below)
            GatherNeighborsOperator op( table, offset+1, v, _remapTable );
            v->ApplyOperatorSurroundingVertices( op );

            // Valence sign bit used to mark boundary vertices
            table[offset] = v->OnBoundary() ? -op.valence : op.valence;

            // Note : some topologies can cause v to be singular at certain
            // levels of adaptive refinement, which prevents us from using
            // the GetValence() function. Fortunately, the GatherNeighbors
            // operator above just performed a similar traversal, so it is
            // very convenient to use it to accumulate the actionable valence.
        }
    } else {
        result->_vertexValenceTable.clear();
    }

    // Combine quad offset buffers
    result->_quadOffsetTable.resize((_patchCtr[0].G[0]+_patchCtr[0].G[1])*4);
    std::copy(quad_G_C0.begin(), quad_G_C0.end(), result->_quadOffsetTable.begin());
    std::copy(quad_G_C1.begin(), quad_G_C1.end(), result->_quadOffsetTable.begin()+_patchCtr[0].G[0]*4);

    return result;
}

// The One Ring vertices to rule them all !
template <class T> void 
FarPatchTablesFactory<T>::getOneRing( HbrFace<T> * f, int ringsize, unsigned int const * remap, unsigned int * result) {

    assert( f and f->GetNumVertices()==4 and ringsize >=4 );

    int idx=0;

    for (unsigned char i=0; i<4; ++i)
        result[remap[idx++ % ringsize]] = _remapTable[f->GetVertex( (i+f->_adaptiveFlags.rots)%4 )->GetID()];

    if (ringsize==16) {

        // Regular case
        //
        //       |      |      |      |      
        //       | 4    | 15   | 14   | 13    
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       | 5    | 0    | 3    | 12    
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       | 6    | 1    | 2    | 11    
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       | 7    | 8    | 9    | 10    
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       |      |      |      |      

        for (int i=0; i<4; ++i) {
            int rot = i+f->_adaptiveFlags.rots;
            HbrVertex<T> * v0 = f->GetVertex( rot % 4 ),
                         * v1 = f->GetVertex( (rot+1) % 4 );

            HbrHalfedge<T> * e = v0->GetNextEdge( v0->GetNextEdge( v0->GetEdge(v1) ) );

            for (int j=0; j<3; ++j) {
                e = e->GetNext();
                result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
            }
        }            
    } else if (ringsize==12) {

        // Boundary case
        //
        //         4      0      3      5
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       | 11   | 1    | 2    | 6    
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       | 10   | 9    | 8    | 7    
        //  ---- o ---- o ---- o ---- o ---- 
        //       |      |      |      |      
        //       |      |      |      |      

        HbrVertex<T> * v[4];
        for (int i=0; i<4; ++i)
            v[i] = f->GetVertex( (i+f->_adaptiveFlags.rots)%4 );

        HbrHalfedge<T> * e;

        e = v[0]->GetIncidentEdge()->GetPrev()->GetOpposite()->GetPrev();
        result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];

        e = v[1]->GetIncidentEdge();
        result[remap[idx++ % ringsize]] = _remapTable[e->GetDestVertex()->GetID()];

        e = v[2]->GetNextEdge( v[2]->GetEdge(v[1]) );
        for (int i=0; i<3; ++i) {
            e = e->GetNext();
            result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
        }

        e = v[3]->GetNextEdge( v[3]->GetEdge(v[2]) );
        for (int i=0; i<3; ++i) {
            e = e->GetNext();
            result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
        }
    } else if (ringsize==9) {

        // Corner case
        //
        //     0      1      4
        //   o ---- o ---- o ----
        //   |      |      |
        //   | 3    | 2    | 5
        //   o ---- o ---- o ----
        //   |      |      |
        //   | 8    | 7    | 6
        //   o ---- o ---- o ----
        //   |      |      |
        //   |      |      |

        HbrVertex<T> * v0 = f->GetVertex( (0+f->_adaptiveFlags.rots)%4 ),
                     * v2 = f->GetVertex( (2+f->_adaptiveFlags.rots)%4 ),
                     * v3 = f->GetVertex( (3+f->_adaptiveFlags.rots)%4 );

        HbrHalfedge<T> * e;

        e = v0->GetIncidentEdge()->GetPrev()->GetOpposite()->GetPrev();
        result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];

        e = v2->GetIncidentEdge();
        result[remap[idx++ % ringsize]] = _remapTable[e->GetDestVertex()->GetID()];

        e = v3->GetNextEdge( v3->GetEdge(v2) );
        for (int i=0; i<3; ++i) {
            e = e->GetNext();
            result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
        }
    }
    assert(idx==ringsize);
}

// Populate the quad-offsets table used by Gregory patches
template <class T> void
FarPatchTablesFactory<T>::getQuadOffsets( HbrFace<T> * f, unsigned int * result ) {

    assert( f and f->GetNumVertices()==4 );
    
    // Builds a table of value pairs for each vertex of the patch.
    // 
    //            o
    //         N0 |
    //            |
    //            |
    //   o ------ o ------ o
    // N1       V | .... M3
    //            | .......
    //            | .......
    //            o .......
    //          N2
    //
    // [...] [N2 - N3] [...]
    //
    // Each value pair is composed of 2 index values in range [0-4[ pointing
    // to the 2 neighbor vertices to the vertex that belong to the Gregory patch. 
    // Neighbor ordering is valence counter-clockwise and must match the winding
    // used to build the vertexValenceTable.
    // 

    class GatherOffsetsOperator : public HbrVertexOperator<T> {
    public:
        HbrVertex<T> ** verts; int offsets[2]; int index; int count;

        GatherOffsetsOperator(HbrVertex<T> ** iverts) : verts(iverts) { }

        void reset() {
            index=count=offsets[0]=offsets[1]=0;
        }

        virtual void operator() (HbrVertex<T> &v) {
             // Resolve which 2 neighbor vertices of v belong to the Gregory patch
             for (unsigned char i=0; i<4; ++i)
                if (&v==verts[i]) {
                    assert(count<3);
                    offsets[count++]=index;
                    break;
                }
            ++index;
        }
    };

    // 4 central CVs of the Gregory patch
    HbrVertex<T> * fvs[4] = { f->GetVertex(0),
                              f->GetVertex(1),
                              f->GetVertex(2),
                              f->GetVertex(3) };


    // Hbr vertex operator that iterates over neighbor vertices
    GatherOffsetsOperator op( fvs );

    for (unsigned char i=0; i<4; ++i) {
    
        op.reset();
    
        fvs[i]->ApplyOperatorSurroundingVertices( op );
  
        if (op.offsets[1] - op.offsets[0] != 1)
            std::swap(op.offsets[0], op.offsets[1]);

        // Pack the 2 indices in 16 bits
        result[i] = (op.offsets[0] | (op.offsets[1] << 8));
    }
}

} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

} // end namespace OpenSubdiv

#endif /* FAR_PATCH_TABLES_FACTORY_H */
