// Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto.  Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once

#include "IPath.h"

#include <carb/Interface.h>
#include <carb/flatcache/AttrNameAndType.h>
#include <carb/flatcache/IdTypes.h>
#include <carb/flatcache/Ordered_Set.h>
#include <carb/flatcache/RationalTime.h>

#include <map>
#include <stdint.h>

namespace carb
{
namespace flatcache
{
struct UsdStageId
{
    uint64_t id;
    constexpr bool operator<(const UsdStageId& other) const
    {
        return id < other.id;
    }
    constexpr bool operator==(const UsdStageId& other) const
    {
        return id == other.id;
    }
    constexpr bool operator!=(const UsdStageId& other) const
    {
        return id != other.id;
    }
};

static_assert(std::is_standard_layout<UsdStageId>::value,
              "Struct must be standard layout as it is used in C-ABI interfaces");

static constexpr UsdStageId kUninitializedStage{ 0 };

}
}

namespace std
{
template <>
class hash<carb::flatcache::UsdStageId>
{
public:
    size_t operator()(const carb::flatcache::UsdStageId& key) const
    {
        return key.id;
    }
};
}

namespace carb
{
namespace flatcache
{
struct BucketId
{
    uint64_t id;
    constexpr bool operator<(const BucketId& other) const
    {
        return id < other.id;
    }
    constexpr bool operator<=(const BucketId& other) const
    {
        return id <= other.id;
    }
    constexpr bool operator==(const BucketId& other) const
    {
        return id == other.id;
    }
    constexpr bool operator!=(const BucketId& other) const
    {
        return id != other.id;
    }
    constexpr BucketId& operator++()
    {
        ++id;
        return *this;
    }
    constexpr BucketId& operator--()
    {
        --id;
        return *this;
    }
    constexpr explicit operator size_t() const
    {
        return id;
    }
};

static_assert(std::is_standard_layout<BucketId>::value,
              "Struct must be standard layout as it is used in C-ABI interfaces");

static constexpr BucketId kInvalidBucketId{ 0xffff'ffff'ffff'ffff };


// A struct that represents a subset of a bucket
struct BucketSubset
{
    BucketId bucket; // The target bucket from which we define a subset

    set<TokenC>* attributes; // The subset of attributes to consider - only used if allAttributes == false, MUST be set otherwise
    set<PathC>*  paths;      // The subset of paths to consider - only used if allPaths == false, MUST be set otherwise

    bool allAttributes; //attribute filtering or not
    bool allPaths; //path filtering or not
};

static_assert(std::is_standard_layout<BucketSubset>::value,
              "BucketSubset must be standard layout as it is used in C-ABI interfaces");


}
}

namespace std
{
template <>
class hash<carb::flatcache::BucketId>
{
public:
    size_t operator()(const carb::flatcache::BucketId& key) const
    {
        return key.id;
    }
};
}

namespace carb
{
namespace flatcache
{

// Flatcache stores data in untyped (byte) arrays.
// For conversion back to typed arrays, getArraySpan methods return the
// element size in bytes. They also return elementCount to allow the caller to
// wrap the array in std::span, or bounds check array access themselves.
// Flatcache methods can't return std::span or gsl::span directly, because they
// are not C-ABI compatible. So we define SpanC/ConstSpanC, which are.
struct ConstSpanC
{
    const uint8_t* ptr;
    size_t elementCount;
    size_t elementSize;
};

struct SpanC
{
    uint8_t* ptr;
    size_t elementCount;
    size_t elementSize;

    // Casting SpanC to ConstSpanC is allowed, but not vice versa
    operator ConstSpanC() const
    {
        return { ptr, elementCount, elementSize };
    }
};

struct ConstSpanWithTypeC
{
    const uint8_t* ptr;
    size_t elementCount;
    size_t elementSize;
    TypeC type;
};

struct SpanWithTypeC
{
    uint8_t* ptr;
    size_t elementCount;
    size_t elementSize;
    TypeC type;

    // Casting SpanWithTypeC to ConstSpanWithTypeC is allowed, but not vice versa
    operator ConstSpanWithTypeC() const
    {
        return { ptr, elementCount, elementSize, type };
    }
};

struct SpanSizeC
{
    size_t* ptr;
    size_t elementCount;
};

struct ConstSpanSizeC
{
    const size_t* ptr;
    size_t elementCount;
};

// An ArrayPointersAndSizesC is an array of immutably sized mutable
// data arrays
//
// Rules (enforced by const):
// {
//     ArrayPointersAndSizesC ps;
//
//     // Allowed: Changing inner array values
//     ps.arrayPtrs[0][0] = 1
//
//     // Disallowed: Changing array pointers
//     ps.arrayPtrs[0] = (uint8_t*)p;
//
//     // Disallowed: Changing inner array sizes
//     ps.sizes[0] = 1;
// }
struct ArrayPointersAndSizesC
{
    uint8_t* const* arrayPtrs;
    const size_t* sizes;
    const size_t elementCount;
};

// A ConstArrayPointersAndSizesC is an array of immutably sized immutable
// data arrays
//
// Rules (enforced by const):
// {
//     ConstArrayPointersAndSizesC ps;
//
//     // Disallowed: Changing inner array values
//     ps.arrayPtrs[0][0] = 1
//
//     // Disallowed: Changing array pointers
//     ps.arrayPtrs[0] = (uint8_t*)p;
//
//     // Disallowed: Changing inner array sizes
//     ps.sizes[0] = 1;
// }
struct ConstArrayPointersAndSizesC
{
    const uint8_t* const* arrayPtrs;
    const size_t* sizes;
    size_t elementCount;
};

static_assert(std::is_standard_layout<Path>::value, "Path must be standard layout as it is used in C-ABI interfaces");
struct ConstPathCSpan
{
    const Path* ptr;
    size_t elementCount;
};

struct ConstAttrNameAndTypeSpanC
{
    const AttrNameAndType* ptr;
    size_t elementCount;
};

struct ConstChangedIndicesC
{
    bool allIndicesChanged;
    ConstSpanSizeC changedIndices;
};

struct ConstChangedIndicesSpanC
{
    const ConstChangedIndicesC* ptr;
    size_t elementCount;
};

struct BucketChangesC
{
    // Which attributes changed
    flatcache::ConstAttrNameAndTypeSpanC changedAttributes;

    // For each attribute, which prims changed?
    flatcache::ConstChangedIndicesSpanC changedIndices;
    flatcache::ConstPathCSpan pathArray;
};

struct AddedPrimIndicesC
{
    // Which prims were added?
    flatcache::ConstSpanSizeC addedIndices;
};

struct StageWithHistorySnapshot
{
    bool valid;
    size_t id;
};

enum class ValidMirrors
{
    eNone = 0,
    eCPU = 1,
    eCudaGPU = 2,
    eGfxGPU = 4
};

constexpr enum ValidMirrors operator|(const enum ValidMirrors a, const enum ValidMirrors b)
{
    return (enum ValidMirrors)(uint32_t(a) | uint32_t(b));
}
constexpr enum ValidMirrors operator&(const enum ValidMirrors a, const enum ValidMirrors b)
{
    return (enum ValidMirrors)(uint32_t(a) & uint32_t(b));
}

using PrimBucket = carb::flatcache::set<AttrNameAndType>;

//
// Note when extending the interface please add to the end so
// that dependencies don't break as easily before they are rebuilt
//
struct IStageInProgress
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IStageInProgress", 0, 2);

    StageInProgressId(CARB_ABI* create)(UsdStageId usdStageId, size_t simFrameNumber);
    StageInProgressId(CARB_ABI* get)(UsdStageId usdStageId);
    void(CARB_ABI* destroy)(UsdStageId usdStageId);
    size_t(CARB_ABI* getFrameNumber)(StageInProgressId stageId);

    // Prefetch prim from USD stage
    // This guarantees that subsequent gets of the prim from the cache will succeed
    void(CARB_ABI* prefetchPrim)(UsdStageId usdStageId, PathC path);

    // Get attribute for read/write access
    SpanC(CARB_ABI* getAttribute)(StageInProgressId stageId, PathC path, TokenC attrName);

    // Get attribute for read only access
    ConstSpanC(CARB_ABI* getAttributeRd)(StageInProgressId stageId, PathC path, TokenC attrName);

    // Get attribute for write only access
    SpanC(CARB_ABI* getAttributeWr)(StageInProgressId stageId, PathC path, TokenC attrName);

    // Get attribute for write only access, creating it if necessary
    SpanC(CARB_ABI* getOrCreateAttributeWr)(StageInProgressId stageId, PathC path, TokenC attrName, TypeC typeC);

    size_t(CARB_ABI* getArrayAttributeSize)(StageInProgressId stageId, PathC path, TokenC attrName);
    void(CARB_ABI* setArrayAttributeSize)(StageInProgressId stageId, PathC path, TokenC attrName, size_t elemCount);

    SpanC(CARB_ABI* setArrayAttributeSizeAndGet)(StageInProgressId stageId, PrimBucketListId primBucketList,
        size_t primBucketListIndex, size_t indexInBucket, TokenC attrName, size_t newElemCount);

    // Get an attribute's type
    Type(CARB_ABI* getType)(StageInProgressId stageId, PathC path, TokenC attrName);

     // Get prim's attribute count
    size_t(CARB_ABI* getAttributeCount)(StageInProgressId stageId, PathC path);

    // Get the names of a prim's attributes
    void(CARB_ABI* getAttributeNamesAndTypes)(Token* outNames,
                                              Type* outTypes,
                                              size_t outCount,
                                              StageInProgressId stageInProgressId, PathC path);


    // Attribute/prim create/destroy
    void(CARB_ABI* createPrim)(StageInProgressId stageId, PathC path);
    void(CARB_ABI* destroyPrim)(StageInProgressId stageId, PathC path);
    void(CARB_ABI* createAttribute)(StageInProgressId stageId, PathC path, TokenC attrName, TypeC type);
    void(CARB_ABI* createAttributes)(
        StageInProgressId stageId, PathC path, TokenC* attrNames, TypeC* types, uint32_t attrNameAndTypeCount);

    // Deprecated as type attribute is not required!
    void(CARB_ABI* destroyAttribute)(StageInProgressId stageId, PathC path, TokenC attrName, TypeC type);
    // see new destroyAttribute and destroyAttributes functions at end of IFlatcache

    // Attribute SOA accessors
    PrimBucketListId(CARB_ABI* findPrims)(StageInProgressId stageInProgressId,
                                          const carb::flatcache::set<AttrNameAndType>& all,
                                          const carb::flatcache::set<AttrNameAndType>& any,
                                          const carb::flatcache::set<AttrNameAndType>& none);
    void(CARB_ABI* getAttributeArray)(SpanC* out,
                                      StageInProgressId stageInProgressId,
                                      PrimBucketListId primBucketList,
                                      size_t primBucketListIndex,
                                      TokenC attrName);

    void(CARB_ABI* getAttributeArrayRd)(ConstSpanC* out,
                                        StageInProgressId stageInProgressId,
                                        PrimBucketListId primBucketList,
                                        size_t primBucketListIndex,
                                        TokenC attrName);

    void(CARB_ABI* getAttributeArrayWr)(SpanC* out,
                                        StageInProgressId stageInProgressId,
                                        PrimBucketListId primBucketList,
                                        size_t primBucketListIndex,
                                        TokenC attrName);

    void(CARB_ABI* getOrCreateAttributeArrayWr)(SpanC* out,
                                                StageInProgressId stageInProgressId,
                                                PrimBucketListId primBucketList,
                                                size_t primBucketListIndex,
                                                TokenC attrName,
                                                TypeC typeC);

    size_t(CARB_ABI* getBucketPrimCount)(StageInProgressId stageInProgressId,
                                         PrimBucketListId primBucketList,
                                         size_t primBucketListIndex,
                                         TokenC attrName);

    size_t(CARB_ABI* getBucketAttributeCount)(StageInProgressId stageInProgressId,
                                              PrimBucketListId primBucketList,
                                              size_t primBucketListIndex);

    void(CARB_ABI* getBucketAttributeNamesAndTypes)(AttrNameAndType* out, size_t outCount,
                                                    StageInProgressId stageInProgressId, PrimBucketListId primBucketList,
                                                    size_t primBucketListIndex);

    ConstSpanSizeC(CARB_ABI* getArrayAttributeSizeArrayRd)(StageInProgressId stageInProgressId,
                                                           PrimBucketListId primBucketList,
                                                           size_t primBucketListIndex,
                                                           TokenC attrName);

    ArrayPointersAndSizesC(CARB_ABI* getArrayAttributeArrayWithSizes)(StageInProgressId stageInProgressId,
                                                                      PrimBucketListId primBucketList,
                                                                      size_t primBucketListIndex,
                                                                      TokenC attrName);

    ConstArrayPointersAndSizesC(CARB_ABI* getArrayAttributeArrayWithSizesRd)(StageInProgressId stageInProgressId,
                                                                             PrimBucketListId primBucketList,
                                                                             size_t primBucketListIndex,
                                                                             TokenC attrName);

    ArrayPointersAndSizesC(CARB_ABI* getArrayAttributeArrayWithSizesWr)(StageInProgressId stageInProgressId,
                                                                        PrimBucketListId primBucketList,
                                                                        size_t primBucketListIndex,
                                                                        TokenC attrName);

    void(CARB_ABI* getPathArray)(ConstPathCSpan* out,
                                 StageInProgressId stageInProgressId,
                                 PrimBucketListId primBucketList,
                                 size_t primBucketListIndex);
    void(CARB_ABI* printBucketNames)(StageInProgressId stageInProgressId);

    void(CARB_ABI* createForAllStages)(size_t simFrameNumber);
    void(CARB_ABI* destroyForAllStages)();


    void(CARB_ABI* logAttributeWriteForNotice)(StageInProgressId stageId, PathC path, TokenC attrName);

    // Broadcast a USD TfNotice to all registered listeners containing paths of
    // all attributes passed to logAttributeWriteForNotice since this StageInProgress was constructed.
    // This is used, for example, to send changes to PhysX.
    void(CARB_ABI* broadcastTfNoticeForAttributesChanged)(StageInProgressId stageInProgressId);

    PrimBucketListId(CARB_ABI* getChanges)(StageInProgressId stageInProgressId, ListenerId listenerId);
    void(CARB_ABI* popChanges)(StageInProgressId stageInProgressId, ListenerId listenerId);

    RationalTime(CARB_ABI* getFrameTime)(StageInProgressId stageId);

    /** @brief Get a Span with a pointer to the head of the relevant array of data
     *         with elementCount and elementSize reflecting the underlying data
     *
     * @stageId[in] Id for the stage to look in
     * @path[in] Path to the prim holding the attribute
     * @name[in] Name of the array attribute
     *
     * @return If a valid prim/attribute that hold an array returns a valid span, otherwise
     *         returns an empty span.
     *
     */
    SpanC(CARB_ABI* getArrayAttribute)(StageInProgressId stageId, PathC path, TokenC attrName);

    /** @brief Get a const Span with a pointer to the head of the relevant array of data
     *         with elementCount and elementSize reflecting the underlying data
     *
     * @stageId[in] Id for the stage to look in
     * @path[in] Path to the prim holding the attribute
     * @name[in] Name of the array attribute
     *
     * @return If a valid prim/attribute that hold an array returns a valid span, otherwise
     *         returns an empty span.
     *
     */
    ConstSpanC(CARB_ABI* getArrayAttributeRd)(StageInProgressId stageId, PathC path, TokenC attrName);

    /** @brief Get a Span with a pointer to the head of the relevant array of data
     *         with elementCount and elementSize reflecting the underlying data
     *
     * @stageId[in] Id for the stage to look in
     * @path[in] Path to the prim holding the attribute
     * @name[in] Name of the array attribute
     *
     * @return If a valid prim/attribute that hold an array returns a valid span, otherwise
     *         returns an empty span.
     *
     */
    SpanC(CARB_ABI* getArrayAttributeWr)(StageInProgressId stageId, PathC path, TokenC attrName);

    /** @brief Destroy attribute with matching name
     *
     * Overloads and superseeds destroyAttribute which takes a unnecessary attribute type.
     *
     * @stageId[in] Id for the stage to look in
     * @path[in] Path to the prim holding the attribute
     * @attrNames[in] Attribute name
     *
     */
    void(CARB_ABI* destroyAttribute2)(StageInProgressId stageId, PathC path, TokenC attrName);

    /** @brief Destroy all attributes with matching names
     *
     * @stageId[in] Id for the stage to look in
     * @path[in] Path to the prim holding the attribute
     * @attrNames[in] Attribute name array
     * @attrNames[in] Attribute name array count
     *
     */
    void(CARB_ABI* destroyAttributes)(StageInProgressId stageId, PathC path, TokenC* attrNames, uint32_t attrNameCount);

    void(CARB_ABI* getAttributeArrayGpu)(SpanC* out,
                                         StageInProgressId stageInProgressId,
                                         PrimBucketListId primBucketList,
                                         size_t primBucketListIndex,
                                         TokenC attrName);

    void(CARB_ABI* getAttributeArrayRdGpu)(ConstSpanC* out,
                                           StageInProgressId stageInProgressId,
                                           PrimBucketListId primBucketList,
                                           size_t primBucketListIndex,
                                           TokenC attrName);

    void(CARB_ABI* getAttributeArrayWrGpu)(SpanC* out,
                                           StageInProgressId stageInProgressId,
                                           PrimBucketListId primBucketList,
                                           size_t primBucketListIndex,
                                           TokenC attrName);

    // Get GPU attribute for read/write access
    SpanC(CARB_ABI* getAttributeGpu)(StageInProgressId stageId, PathC path, TokenC attrName);

    // Get GPU attribute for read only access
    ConstSpanC(CARB_ABI* getAttributeRdGpu)(StageInProgressId stageId, PathC path, TokenC attrName);

    // Get GPU attribute for write only access
    SpanC(CARB_ABI* getAttributeWrGpu)(StageInProgressId stageId, PathC path, TokenC attrName);

    /** @brief Returns which mirrors of the array are valid: CPU, GPU, etc. 
    *
    * @stageId[in] The stage to query validity from
    * @path[in] The prim path
    * @attrName[in] The attribute name
    *
    * @return ValidMirrors struct
    *
    */
    ValidMirrors(CARB_ABI* getAttributeValidBits)(StageInProgressId stageId, const PathC& path, const TokenC& attrName);

    // Connection API
    /**
     * @brief Create a connection on a prim 
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to create the connection on
     * @param[in] connectionName the name of the connection attribute
     * @param[in] connection the target prim and attribute for the connection
     */
    void(CARB_ABI* createConnection)(StageInProgressId stageId, PathC path, TokenC connectionName, Connection connection);

    /**
     * @brief Create multiple connections on a prim 
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to create the connection on
     * @param[in] connectionNames the name of the connection attributes to create
     * @param[in] connection the target prim and attribute for the connections
     * @param[in] connectionCount the number of connections to be created. 
     */
    void(CARB_ABI* createConnections)(StageInProgressId stageId, PathC path, const TokenC* connectionNames, const Connection* connections, size_t connectionCount);
  
    /**
     * @brief remove a connection on a prim 
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to remove the connection from
     * @param[in] connectionName the name of the connection attribute
     */
    void(CARB_ABI* destroyConnection)(StageInProgressId stageId, PathC path, TokenC connectionName);

    /**
     * @brief Remove multiple connections from a prim 
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to remove the connections from
     * @param[in] connectionNames the name of the connection attributes to be removed
     * @param[in] connectionCount the number of connections to be removed. 
     */
    void(CARB_ABI* destroyConnections)(StageInProgressId stageId, PathC path, const TokenC* connectionNames, size_t connectionCount);

    /**
     * @brief  Retrieves a connection attribute from a prim
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to fetch the connection from
     * @param[in] connectionName the name of the connection attribute to fetch
     * @return a read/write pointer to the connection
     */
    Connection*(CARB_ABI* getConnection)(StageInProgressId stageId, PathC path, TokenC connectionName);

    /**
     * @brief  Retrieves a connection attribute from a prim
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to fetch the connection from
     * @param[in] connectionName the name of the connection attribute to fetch
     * @return a read only pointer to the connection
     */
    const Connection*(CARB_ABI* getConnectionRd)(StageInProgressId stageId, PathC path, TokenC connectionName);

    /**
     * @brief  Retrieves a connection attribute from a prim
     * 
     * @param[in] stageId the stage to work on
     * @param[in] path the prim to fetch the connection from
     * @param[in] connectionName the name of the connection attribute to fetch
     * @return a write only pointer to the connection
     */
    Connection*(CARB_ABI* getConnectionWr)(StageInProgressId stageId, PathC path, TokenC connectionName);

    /**
     * @brief Copy all attributes from the source prim to the destination prim
     * Will create attributes if they do not exist on the destination prim
     * If an attribute exists on both prims they must have compatible types to copy.
     * 
     * @param[in] stageId the stage id to use for copying 
     * @param[in] srcPath the source prim 
     * @param[in] dstPath the destination prim
     */
    void(CARB_ABI* copyAllAttributes)(StageInProgressId stageId,  PathC srcPath,  PathC dstPath);
    
    /**
     * @brief Copy the specified attributes from the source prim to the the specified 
     * attributes on the destination prim
     * Will create attributes if they do not exist on the destination prim
     * If an attribute exists on both prims they must have compatible types to copy.
     * Note: The srcAttrs and dstAttrs must be the same size as the function assumes
     * that the copy is 1 to 1 in terms of name alignment
     * 
     * @param[in] stageId the stage id to use for copying 
     * @param[in] srcPath the source prim 
     * @param[in] srcAttrs a vector of attributes to be copied.
     * @param[in] dstPath the destination prim
     * @param[in] dstAttrs a vector of attributes to be copied.
     * @param[in] count the number of attributes to copy
     */
    void(CARB_ABI* copySpecifiedAttributes)(StageInProgressId stageId,  PathC srcPath, const TokenC* srcAttrs, PathC dstPath, const TokenC* dstAttrs, size_t count);
};

struct IStageAtTimeInterval
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IStageAtTimeInterval", 0, 1);

    StageAtTimeIntervalId(CARB_ABI* create)(StageWithHistoryId stageWithHistoryId,
                                            RationalTime beginTime,
                                            RationalTime endTime,
                                            bool includeEndTime);
    void(CARB_ABI* destroy)(StageAtTimeIntervalId stageAtTimeIntervalId);

    size_t(CARB_ABI* getTimesampleCount)(StageAtTimeIntervalId stageAtTimeIntervalId);
    void(CARB_ABI* getTimestamps)(RationalTime* out, size_t outCount, StageAtTimeIntervalId stageAtTimeIntervalId);

    // Single attribute accessor
    size_t(CARB_ABI* getAttributeRd)(const void** out,
                                     size_t outCount,
                                     StageAtTimeIntervalId stageAtTimeIntervalId,
                                     PathC path,
                                     TokenC attrName);

    // Attribute SOA accessors
    PrimBucketListId(CARB_ABI* findPrims)(StageAtTimeIntervalId stageAtTimeIntervalId,
                                          const carb::flatcache::set<AttrNameAndType>& all,
                                          const carb::flatcache::set<AttrNameAndType>& any,
                                          const carb::flatcache::set<AttrNameAndType>& none);
    void(CARB_ABI* getAttributeArrayRd)(ConstSpanC* out,
                                        size_t outCount,
                                        StageAtTimeIntervalId stageAtTimeIntervalId,
                                        PrimBucketListId primBucketList,
                                        size_t primBucketListIndex,
                                        TokenC attrName);

    void(CARB_ABI* getArrayAttributeArrayWithSizesRd)(ConstArrayPointersAndSizesC* out,
                                                      size_t outCount,
                                                      StageAtTimeIntervalId stageAtTimeIntervalId,
                                                      PrimBucketListId primBucketList,
                                                      size_t primBucketListIndex,
                                                      TokenC attrName);

    void(CARB_ABI* getPathArray)(ConstPathCSpan* out,
                                 size_t outCount,
                                 StageAtTimeIntervalId stageAtTimeIntervalId,
                                 PrimBucketListId primBucketList,
                                 size_t primBucketListIndex);

    void(CARB_ABI* printBucketNames)(StageAtTimeIntervalId stageAtTimeIntervalId);

    void(CARB_ABI* exportUsd)(StageAtTimeIntervalId stageAtTimeIntervalId, UsdStageId usdStageId);

    RationalTime(CARB_ABI* getSimPeriod)(UsdStageId usdStageId);

    void(CARB_ABI* getAttributeCounts)(StageAtTimeIntervalId stageAtTimeIntervalId,
                                      PrimBucketListId primBucketList,
                                      size_t primBucketListIndex,
                                      size_t timesamples,
                                      size_t* outCounts);

    void(CARB_ABI* getAttributeNamesAndTypes)(StageAtTimeIntervalId stageAtTimeIntervalId,
                                              PrimBucketListId primBucketList,
                                              size_t primBucketListIndex,
                                              size_t timesamples,
                                              const size_t* attributeCounts,
                                              Token** outNames,
                                              Type** outTypes);

    size_t(CARB_ABI* getAttributeCountForTimesample)(StageAtTimeIntervalId stageAtTimeIntervalId,
                                                    PrimBucketListId primBucketList,
                                                    size_t primBucketListIndex,
                                                    size_t timesampleIndex);

    void(CARB_ABI* getAttributeNamesAndTypesForTimesample)(StageAtTimeIntervalId stageAtTimeIntervalId,
                                                           PrimBucketListId primBucketList,
                                                           size_t primBucketListIndex,
                                                           size_t timesampleIndex,
                                                           size_t attributeCount,
                                                           Token* outNames,
                                                           Type* outTypes);

    void(CARB_ABI* getArrayAttributeWithSizeRd)(ConstSpanWithTypeC* out,
                                                size_t outCount,
                                                StageAtTimeIntervalId stageAtTimeIntervalId,
                                                carb::flatcache::PathC path,
                                                carb::flatcache::TokenC attrName);
    /** @brief Write a cache file to disk at a specified location
     *
     * @note Many parameters to this function are optional
     * @note This currently only writes the first time in the interval
     * @stageAtTimeIntervalId[in] The stage at time to be written to disk
     * @file[in The location the file is desired to be written to
     * @workingBuffer[in] [Optional] In order to avoid costly reallocations
     *                    the code will attempt to re-use the memory at the buffer
     *                    location if it is large enough. If the buffer isn't larg
     *                    enough the cost of allocation, and re-traversal may be paid
     * @workingBufferSize[in] [Optional] If workingBuffer is non null, then this desrcibes the length
     *                        of the buffer
     * @return The amount of data needed to serialize the cache, a return value of 0 indicates an error
     *
     */
    uint64_t(CARB_ABI* writeCacheToDisk)(
        StageAtTimeIntervalId stageAtTimeIntervalId,
        const char* file,
        uint8_t* workingBuffer,
        uint64_t workingBufferSize);
    /** @brief Add a ref count to any data backed by the StageAtTimeIntercal
     *
     * @note The ref count will not enforce any behavior currently, but will
     *       print a warning if backing data is deleted before all ref counts
     *       are cleared
     *
     * @stageAtTimeIntervalId[in] The stage at time tracked for the ref counting
     *
     * @return None
     *
     */
    void(CARB_ABI* addRefCount)(StageAtTimeIntervalId stageAtTimeIntervalId);

    /** @brief Remove a ref count from an existing timeInterval
     *
     *
     * @stageAtTimeIntervalId[in] The stage at time tracked for the ref counting
     *
     * @return True if ref count was removed successfully, failure conditions may
     *         include
     *           (1) StageAtTimeInterval doesn't exist
     *           (2) RefCount was already 0
     *
     */
    bool(CARB_ABI* removeRefCount)(StageAtTimeIntervalId stageAtTimeIntervalId);

    /** @brief Query ref count for a stage at time
    *
    * @note A stage at time might be represented by multiple actual data sources
    *       in that case we return the largest refcount of all the data sources
    *
    * @stageAtTimeIntervalId[in] The stage at time tracked for the ref counting
    *
    * @return number of reference counts
    *
    */
    unsigned int(CARB_ABI* getRefCount)(StageAtTimeIntervalId stageAtTimeIntervalId);

    // Access GPU Array attribute
    void(CARB_ABI* getAttributeArrayRdGpu)(ConstSpanC* out,
                                           size_t outCount,
                                           StageAtTimeIntervalId stageAtTimeIntervalId,
                                           PrimBucketListId primBucketList,
                                           size_t primBucketListIndex,
                                           TokenC attrName);
    // Access GPU pointer attribute
    void(CARB_ABI* getAttributeRdGpu)(ConstSpanC* out,
                                      size_t outCount,
                                      StageAtTimeIntervalId stageAtTimeIntervalId,
                                      PathC path,
                                      TokenC attrName);
    // Get array size, useful for GPU attributes
    size_t(CARB_ABI* getArrayAttributeSize)(size_t* out, size_t outCount, StageAtTimeIntervalId stageAtTimeIntervalId, PathC path, TokenC attrName);
    /** @brief Returns which mirrors of the array are valid: CPU, GPU, etc. 
    *
    * @stageAtTimeIntervalId[in] The stage to query validity from
    * @path[in] The prim path
    * @attrName[in] The attribute name
    *
    * @return ValidMirrors struct
    *
    */
    ValidMirrors(CARB_ABI* getAttributeValidBits)(StageAtTimeIntervalId stageAtTimeIntervalId, const PathC& path, const TokenC& attrName);

    /**
     * @brief 
     * 
     */
    void(CARB_ABI* getConnectionRd)(const void** out,
                                     size_t outCount,
                                     StageAtTimeIntervalId stageAtTimeIntervalId,
                                     PathC path,
                                     TokenC connectionName);

};

struct IStageWithHistory
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IStageWithHistory", 0, 1);

    StageWithHistoryId(CARB_ABI* create)(UsdStageId usdStageId, size_t historyFrameCount, RationalTime simPeriod);
    StageWithHistoryId(CARB_ABI* get)(UsdStageId usdStageId);
    void(CARB_ABI* destroy)(UsdStageId usdStageId);
    //
    // Create a snapshot of the stageWIthHistory for the usdStageId, this currently just resets
    // the stage in progress, but it probably should be extended to copy the entire ringbuffer if we intend to
    // so anything other than reset to the start frame.
    //
    StageWithHistorySnapshot(CARB_ABI* saveSnapshot)(UsdStageId usdStageId);

    bool(CARB_ABI* deleteSnapshot)(UsdStageId usdStageId, size_t snapshotId);

    bool(CARB_ABI* restoreFromSnapshot)(UsdStageId usdStageId, size_t snapshotId);


    RationalTime(CARB_ABI* getSimPeriod)(UsdStageId usdStageId);

    // For multi-process replication. Stores the link between the stage id on the master process and the local stage id.
    void(CARB_ABI* setStageIdMapping)(UsdStageId usdStageIdMaster, UsdStageId usdStageIdLocal);

    ListenerId(CARB_ABI* createListener)();


    /** @brief Get the last frame that was written to the StageWithHistory
    *
    * @usdStageId[in] The identifier for the statge
    *
    * @return the time, and period of the last valid data written to the StageWithHistory
    *
    */
    RationalTime(CARB_ABI* getLatestFrame)(UsdStageId usdStageId);

    StageWithHistoryId(CARB_ABI* create2)(UsdStageId usdStageId, size_t historyFrameCount, RationalTime simPeriod, bool withCuda);

    UsdStageId(CARB_ABI* getLocalStageId)(UsdStageId usdStageIdMaster);
};

struct IStageWithHistoryDefaults
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IStageWithHistoryDefaults", 0, 1);

    void(CARB_ABI* setStageHistoryFrameCount)(size_t historyFrameCount);
    void(CARB_ABI* setStageHistoryUpdatePeriod)(uint64_t periodNumerator, uint64_t periodDenominator);
};

struct IPrimBucketList
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IPrimBucketList", 0, 2);

    void(CARB_ABI* destroy)(PrimBucketListId primBucketListId);
    size_t(CARB_ABI* getBucketCount)(PrimBucketListId primBucketListId);
    void(CARB_ABI* print)(PrimBucketListId primBucketListId);
    BucketChangesC(CARB_ABI* getChanges)(PrimBucketListId changeListId, size_t index);
    AddedPrimIndicesC(CARB_ABI* getAddedPrims)(PrimBucketListId changeListId, size_t index);
};

struct IChangeTrackerConfig
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IChangeTrackerConfig", 0, 3);

    void(CARB_ABI* pause)(StageInProgressId stageInProgressId, ListenerId listenerId);
    void(CARB_ABI* resume)(StageInProgressId stageInProgressId, ListenerId listenerId);
    bool(CARB_ABI* isChangeTrackingPaused)(StageInProgressId stageInProgressId, ListenerId listenerId);

    void(CARB_ABI* attributeEnable)(StageInProgressId stageInProgressId, TokenC attrName, ListenerId listenerId);
    void(CARB_ABI* attributeDisable)(StageInProgressId stageInProgressId, TokenC attrName, ListenerId listenerId);

    bool(CARB_ABI* isListenerAttached)(StageInProgressId stageInProgressId, ListenerId listenerId);
    void(CARB_ABI* detachListener)(StageInProgressId stageInProgressId, ListenerId listenerId);
    size_t(CARB_ABI* getListenerCount)(StageInProgressId stageInProgressId);

    void(CARB_ABI* enablePrimCreateTracking)(StageInProgressId stageInProgressId, ListenerId listenerId);
};

/** @brief The Serializer interface provides the C-ABI compatible functions for
 *         working with all serialization of SWH and workflows. This covers
 *            (1) In-memory serialization/deserialization
 *            (2) Serialization to Disk and From
 *            (3) Functions to support replication based on serialization
 *         Because of the nature of SWH there are multiple places one might want to
 *         actually serialize the cache from, we provide some convenience functions
 *         that wrap this up, but also the direct functionality to serialize a
 *         PathToAttributesMap directly to/from a buffer for convenience.
 *
 */
struct ISerializer
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::ISerializer", 0, 2);

    //
    // deprecated for more appropriately named serializeRingBuffer
    //
    uint64_t(CARB_ABI* serializeStage)(StageWithHistoryId stageWithHistoryId, size_t slot, uint8_t* dest, size_t destSize);

    //
    // deprecated for more appropriately named deserializeIntoRingBuffer
    //
    bool (CARB_ABI* deserializeStage)(StageWithHistoryId stageWithHistoryId, size_t slot, const uint8_t* input, const size_t inputSize,
        size_t simFrameNumber, carb::flatcache::RationalTime simFrameTime);


    /** @brief Attempt to serialize the stage into the provided buffer. This function
     *         is intended to be used when you want to serialize all the data within a
     *         ring buffer entry, however this is often more data than needs to be sent.
     *
     * @stage[in] The StageWithHistory with the ring buffer to be serialized
     * @slot[in] The slot from the ring buffer to send
     * @dest[in/out] Pointer to buffer to be written to, will start writing to head
     *       of pointer. dest will be left pointing to the point after the last write
     * @destSize Size of buffer that was allocated for the data (in bytes)
     *
     * @return Number of bytes written success is determined by (return <= @destSize)
     *
     *
     * @invariant It is safe to write to any memory within[dest, dest+size] for the
     *            duration of the function call.
     *
     * @note If the cache will not fit into the size of memory allocated in
     *       @dest then it will stop writing, but continue to run the serialize
     *       algorithm to calculate the actual amount of data that needs to be
     *       written
     *
     */
    uint64_t(CARB_ABI* serializeRingBuffer)(StageWithHistoryId stageWithHistoryId, size_t slot, uint8_t* dest, size_t destSize);

    /** @brief Given a buffer that has the serialized version of a cache written
     *         using the serialize function, this function will override all the data
     *         in the ringbuffer at the requested slot with the data encoded in the
     *         buffer. This function will only succeed if the StageWithHistory that
     *         is passed in was created from the same UsdStage (opened at the same root layer)
     *         that was used to create the original serialized cache.
     *
     *
     * @stageWithHistoryId[in] The stage to write the data to
     * @slot[in] The index in the ring buffer to pull to
     * @input[in] Pointer to buffer of data containing serialized cache
     * @inputSize[in] Size of data in the buffer
     * @simFrameNumber[in] The frame of the simulation to set the ring buffer entry to
     * @simFrameTime[in] The simFrame time to set the ring buffer to
     *
     * @return True if buffer was successfully de-serialized
     *
     * @TODO: whould we care that it came from the same version of the USD file?
     */
    bool (CARB_ABI* deserializeIntoRingBuffer)(StageWithHistoryId stageWithHistoryId, size_t slot, const uint8_t* input, const size_t inputSize,
        size_t simFrameNumber, carb::flatcache::RationalTime simFrameTime);


    /** @brief Replicate the ring buffers from the master to the workers when running
     *         multiple processes. Data is serialized into buffers allocated and broadcast
     *         by Realm, followed by deserialization into the remote ring buffers. This
     *         function is synchronous, i.e., the remote FlatCaches have finished updating
     *         when this function returns.
     */
    void (CARB_ABI* replicateRingBuffers)();


};

struct Platform;
struct IPlatform
{
    CARB_PLUGIN_INTERFACE("carb::flatcache::IPlatform", 0, 1);

    const Platform& (CARB_ABI* get)(const PlatformId& platformId);
    Platform& (CARB_ABI* getMutable)(const PlatformId& platformId);
    void (CARB_ABI* reset)(const PlatformId& platformId);
    void (CARB_ABI* resetAll)();
};

} // namespace flatcache
} // namespace carb


