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

#ifndef OPENSUBDIV3_BFR_SURFACE_FACTORY_ADAPTER_H
#define OPENSUBDIV3_BFR_SURFACE_FACTORY_ADAPTER_H

#include "../version.h"

#include <cstdint>

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

namespace Bfr {

class VertexDescriptor;

///
/// @brief Abstract interface adapting SurfaceFactory to a connected mesh
///        representation
///
//  SurfaceFactoryMeshAdapter is an abstract class that defines the interface
//  through which subclasses of SurfaceFactory adapt to a connected mesh
//  representation. The interface defines methods that describe the mesh
//  topology and control indices in the neighborhood of a mesh -- from
//  which the SurfaceFactory to identifies an appropriate limit surface.
//
//  SurfaceFactoryMeshAdapter methods require a subclass to provide a complete
//  description of the topology around a base face, as well as indices
//  associated with it (both vertex and face-varying).  The intent here is
//  to keep the number of methods required to a minimum, and also to minimize
//  the number of invocations required by the factory.
//
//  With the need to support both linear and non-linear cases (for which
//  linear is trivial by comparison) and the limit surface for both vertex
//  and face-varying topologies, the result is a small set of methods
//  covering this matrix of functionality.
//
//  Since face-varying data may differ in topology from the vertex data --
//  with each set of face-varying data potentially having its own unique
//  topology -- sets of face-varying data are uniquely distinguished by an
//  associated integer (a face-varying ID).
//
class SurfaceFactoryMeshAdapter {
public:
    /// @brief Integer type representing a mesh index
    typedef int Index;

    /// @brief Type used to identify and specify face-varying primvars
    ///
    /// A face-varying ID is used to specify face-varying primvars for
    /// evaluation so that they can be identified by the subclass for
    /// the mesh.  It can be assigned as either a positive integer ID
    /// or pointer, with the subclass determining its interpretation.
    ///
    /// Often only one face-varying primvar is of interest, so a default
    /// can be assigned to the factory to avoid repeated specification.
    ///
    typedef std::intptr_t FVarID;

protected:
    /// @cond PROTECTED
    SurfaceFactoryMeshAdapter() { }
    virtual ~SurfaceFactoryMeshAdapter() { }
    /// @endcond

protected:
    //@{
    /// @name Methods to query simple face properties
    ///
    /// Methods to return simple properties associated with a face.
    ///

    /// @brief Returns if a face is a hole
    virtual bool isFaceHole(Index faceIndex) const = 0;

    /// @brief Returns the size of a face (number of vertices)
    virtual int getFaceSize(Index faceIndex) const = 0;
    //@}

    //@{
    /// @name Methods to gather indices for the face's vertices
    ///
    /// These methods gather indices associated with the vertices of a
    /// face, e.g. the indices of the vertices themselves, or the indices
    /// of face-varying values associated with the vertices. These are
    /// used to quickly deal with linear limit surfaces without any
    /// inspection of the neighboring topology.
    ///

    /// @brief Gather the indices of the face's vertices
    virtual int getFaceVertexIndices(Index faceIndex,
                    Index vertexIndices[]) const = 0;

    /// @brief Gather the face-varying indices of the face's vertices
    virtual int getFaceFVarValueIndices(Index faceIndex,
                    FVarID fvarID, Index fvarValueIndices[]) const = 0;
    //@}

protected:
    //@{
    /// @name Methods to identify the neighborhood of a face-vertex
    ///
    /// These methods identify the topology and associated indices for
    /// the complete set of incident faces surrounding a corner (or
    /// face-vertex) of a face.
    ///
    /// Methods here use "FaceVertex" in the name to emphasize that they
    /// require information for a particular corner vertex of the face.
    ///
    /// The topology around the face-vertex is described by populating a
    /// given instance of a simple VertexDescriptor class -- which fully
    /// describes the face-vertex, it incident faces and any sharpness
    /// assigned at or around the face-vertex.  (See the comments with
    /// the VertexDescriptor definition for more details.)
    ///
    /// Additional methods are then required to identify indices for the
    /// incident faces around a face-vertex. One method gathers the
    /// indices for control vertices of the mesh assigned to the incident
    /// faces (their VertexIndices), while the other gathers indices for
    /// a particular set of face-varying values assigned to them (their
    /// FVarValueIndices).
    ///
    /// Both methods expect the incident faces to be ordered consistent
    /// with the specification in VertexDescriptor, and all indices for
    /// all incident faces are required.
    ///
    /// The order of indices assigned to each face for these methods must
    /// also be specified relative to the face-vertex, rather than the
    /// way the face is defined.  For example, if a quad Q is defined by
    /// the four vertices {A, B, C, D}, when gathering the indices for Q
    /// as part of face-vertex C, the indices should be specified starting
    /// with C, i.e. as {C, D, A, B}.  Ordering indices this way makes it
    /// much easier for the factory to identify when face-varying topology
    /// differs from the vertex topology, and both the face-varying and
    /// vertex indices are ordered this way for consistency.
    ///
 
    /// @brief Describe the topology of incident faces around a face-vertex
    virtual int populateFaceVertexDescriptor(
                    Index faceIndex, int faceVertex,
                    VertexDescriptor * vertexDescriptor) const = 0;
 
    /// @brief Gather vertex indices of incident faces around a face-vertex
    virtual int getFaceVertexIncidentFaceVertexIndices(
                    Index faceIndex, int faceVertex,
                    Index vertexIndices[]) const = 0;

    /// @brief Gather face-varying indices of incident faces around a
    ///        face-vertex
    virtual int getFaceVertexIncidentFaceFVarValueIndices(
                    Index faceIndex, int faceVertex,
                    FVarID fvarID, Index fvarValueIndices[]) const = 0;
    //@}

protected:
    //@{
    /// @name Optional methods for purely regular topology
    ///
    /// Optional methods for advanced use to accelerate the case of
    /// purely regular topology around a face.
    ///
    /// For cases when a mesh can quickly determine if the neighborhood
    /// around a faces is purely regular, these methods can be used to
    /// quickly identify the control point indices for the corresponding
    /// regular patch defining its limit surface. In doing so, the more
    /// tedious topological assembly requiring information about each
    /// face-vertex can be avoided.
    ///
    /// The indices returned must be ordered according to the regular
    /// patch type corresponding to the subdivision scheme of the mesh.
    /// Boundary vertices are allowed and indicated by an Index of -1.
    ///
    /// The face-varying version will only be invoked if the vertex
    /// version is purely regular, in which case, the face-varying
    /// topology is expected to be similar.
    ///
    /// Note that these methods allow the caller (the SurfaceFactory) to
    /// pass nullptr (0) for the index arrays -- in which case only the
    /// return value should be provided.
    ///
    virtual bool getFaceNeighborhoodVertexIndicesIfRegular(
            Index faceIndex, Index vertexIndices[]) const;

    virtual bool getFaceNeighborhoodFVarValueIndicesIfRegular(
            Index faceIndex, FVarID fvarID, Index fvarValueIndices[]) const;
    //@}

private:
    //  No private members
};

//
//  Inline defaults for optional methods:
//
inline bool
SurfaceFactoryMeshAdapter::getFaceNeighborhoodVertexIndicesIfRegular(
        Index, Index[]) const {
    return false;
}

inline bool
SurfaceFactoryMeshAdapter::getFaceNeighborhoodFVarValueIndicesIfRegular(
        Index, FVarID, Index[]) const {
    return false;
}

} // end namespace Bfr

} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

} // end namespace OpenSubdiv

#endif /* OPENSUBDIV3_BFR_SURFACE_FACTORY_ADAPTER_H */
