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

#ifndef OPENSUBDIV3_BFR_PATCH_TREE_H
#define OPENSUBDIV3_BFR_PATCH_TREE_H

#include "../version.h"

#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../vtr/array.h"

#include <vector>
#include <cstring>

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

namespace Bfr {

//
//  A PatchTree is a hierarchical collection of parametric patches that
//  form a piecewise representation of the limit surface for a single face
//  of a mesh.  Using the patch representations from Far, it combines
//  stripped down versions of the PatchTable and PatchMap from Far and a
//  raw representation of stencils into a more compact representation
//  suited to evaluating a single face. These are constructed based on
//  adaptive refinement of a single face of a Far::TopologyRefiner.
//
//  As the internal representation for the limit surface of a face with
//  irregular topology, the PatchTree is not publicly exposed. As is the
//  case with other internal Bfr classes whose headers are not exported,
//  it is not further protected by the "internal" namespace.
//
//  PatchTree was initially developed as an internal class for Far, so
//  some comments may still reflect that origin.
//
//  PatchTree also includes functionality beyond what is needed for Bfr
//  for potential future use. Most notable is the ability for the tree
//  to store a patch at interior nodes -- in addition to the leaf nodes.
//  This allows the depth of evaluation to be varied, which takes place
//  when searching a patch from the tree (specifying a maximum depth for
//  the search). Construction options allow this functionality to be
//  selectively enabled/disabled, and Bfr currently disables it.
//
class PatchTree {
public:
    //  Constructors are protected
    ~PatchTree();

    //  Simple public accessors:
    int GetNumControlPoints() const  { return _numControlPoints; }
    int GetNumSubPatchPoints() const { return _numSubPatchPoints; }
    int GetNumPointsTotal() const    { return _numControlPoints +
                                              _numSubPatchPoints; }

    //  These queries may not be necessary...
    int GetDepth() const      { return _treeDepth; }
    int GetNumPatches() const { return (int)_patchParams.size(); }

    //  Methods to access stencils to compute patch points:
    template <typename REAL>
    REAL const * GetStencilMatrix() const;

    bool UsesDoublePrecision() const { return _useDoublePrecision; }

    //  Methods supporting evaluation:
    int HasSubFaces() const    { return _numSubFaces > 0; }
    int GetNumSubFaces() const { return _numSubFaces; }

    int FindSubPatch(double u, double v, int subFace=0, int maxDep=-1) const;

    typedef Vtr::ConstArray<int> PatchPointArray;
    PatchPointArray GetSubPatchPoints(int subPatch) const;
    Far::PatchParam GetSubPatchParam( int subPatch) const;

    //  Main evaluation methods - basis weights or limit stencils:
    template <typename REAL>
    int EvalSubPatchBasis(int subPatch, REAL u, REAL v, REAL w[],
                          REAL wDu[],  REAL wDv[],
                          REAL wDuu[], REAL wDuv[], REAL wDvv[]) const;

    template <typename REAL>
    int EvalSubPatchStencils(int subPatch, REAL u, REAL v, REAL s[],
                             REAL sDu[],  REAL sDv[],
                             REAL sDuu[], REAL sDuv[], REAL sDvv[]) const;

protected:
    PatchTree();
    friend class PatchTreeBuilder;

    //
    //  Internal utilities to support the stencil matrix of variable precision
    //
    template <typename REAL> std::vector<REAL> const & getStencilMatrix() const;
    template <typename REAL> std::vector<REAL>       & getStencilMatrix();

    template <typename REAL_MATRIX, typename REAL>
    int evalSubPatchStencils(int subPatch, REAL u, REAL v, REAL s[],
                             REAL sDu[],  REAL sDv[],
                             REAL sDuu[], REAL sDuv[], REAL sDvv[]) const;

protected:
    //  Internal quad-tree node type and assembly and search methods:
    struct TreeNode {
        struct Child {
            unsigned int isSet  :  1;
            unsigned int isLeaf :  1;
            unsigned int index  : 28;

            void SetIndex(int indexArg) { index = indexArg & 0xfffffff; }
        };

        TreeNode() : patchIndex(-1) {
            std::memset(children, 0, sizeof(children));
        }

        void SetChildren(int index);
        void SetChild(int quadrant, int index, bool isLeaf);

        int   patchIndex;
        Child children[4];
    };

    int searchQuadtree(double u, double v, int subFace=0, int depth=-1) const;
    void buildQuadtree();

    TreeNode * assignLeafOrChildNode(TreeNode * node,
                                     bool isLeaf, int quadrant, int index);

private:
    //  Private members:
    typedef Far::PatchDescriptor::Type PatchType;

    //  Simple configuration members:
    unsigned int _useDoublePrecision    : 1;
    unsigned int _patchesIncludeNonLeaf : 1;
    unsigned int _patchesAreTriangular  : 1;

    PatchType _regPatchType;
    PatchType _irregPatchType;
    int       _regPatchSize;
    int       _irregPatchSize;
    int       _patchPointStride;

    //  Simple topology inventory members:
    int _numSubFaces;
    int _numControlPoints;
    int _numRefinedPoints;
    int _numSubPatchPoints;
    int _numIrregPatches;

    //  Vectors for points and PatchParams of all patches:
    //
    //  Note we store both regular and irregular patch point indices in the
    //  same vector (using a common stride for each patch) and the patch type
    //  determined by the PatchParam -- in the same way that face-varying
    //  patches are stored in the PatchTable.  Could also be stored in
    //  separate "patch arrays" or separated in other ways and managed with
    //  a bit more book-keeping.
    //
    std::vector<int>             _patchPoints;
    std::vector<Far::PatchParam> _patchParams;

    //  The quadtree organizing the patches:
    std::vector<TreeNode>  _treeNodes;
    int                    _treeDepth;

    //  Array of stencils for computing patch points from control points
    //  (single or double to be used as specified on construction):
    std::vector<float>  _stencilMatrixFloat;
    std::vector<double> _stencilMatrixDouble;
};

//
//  Internal specializations to support the stencil matrix of variable
//  precision -- const access presumes it is non-empty (and so assert)
//  while non-const access may be used to populate it.
//
template <>
inline std::vector<float> const &
PatchTree::getStencilMatrix<float>() const {
    assert(!_stencilMatrixFloat.empty());
    return _stencilMatrixFloat;
}
template <>
inline std::vector<double> const &
PatchTree::getStencilMatrix<double>() const {
    assert(!_stencilMatrixDouble.empty());
    return _stencilMatrixDouble;
}

template <>
inline std::vector<float> &
PatchTree::getStencilMatrix<float>() {
    return _stencilMatrixFloat;
}
template <>
inline std::vector<double> &
PatchTree::getStencilMatrix<double>() {
    return _stencilMatrixDouble;
}

//
//  Inline methods:
//
inline Far::PatchParam
PatchTree::GetSubPatchParam(int subPatch) const {
    return _patchParams[subPatch];
}

inline int
PatchTree::FindSubPatch(double u, double v, int subFace, int maxDep) const {
    return searchQuadtree(u, v, subFace, maxDep);
}

template <typename REAL>
inline REAL const *
PatchTree::GetStencilMatrix() const {
    return &getStencilMatrix<REAL>()[0];
}

} // end namespace Bfr

} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

} // end namespace OpenSubdiv

#endif /* OPENSUBDIV3_BFR_PATCH_TREE */
