// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0

//
// Simple C-wrapper for the nanovdb structure
// Meant for systems where you lack a C++ compiler.
//

#ifndef __CNANOVDB__
#define __CNANOVDB__

#define CNANOVDB_DATA_ALIGNMENT 32
#define CNANOVDB_ALIGNMENT_PADDING(x, n) (-(x) & ((n) - 1))

#define USE_SINGLE_ROOT_KEY

#ifdef __OPENCL_VERSION__

#define CNANOVDB_GLOBAL __global
#define RESTRICT restrict

// OpenCL doesn't define these basic types:
typedef unsigned long uint64_t;
typedef long int64_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;

#else

#define CNANOVDB_GLOBAL
#define RESTRICT __restrict

#endif

enum cnanovdb_GridType
{
    cnanovdb_GridType_Unknown = 0,
    cnanovdb_GridType_Float = 1,
    cnanovdb_GridType_Double = 2,
    cnanovdb_GridType_Int16 = 3,
    cnanovdb_GridType_Int32 = 4,
    cnanovdb_GridType_Int64 = 5,
    cnanovdb_GridType_Vec3f = 6,
    cnanovdb_GridType_Vec3d = 7,
    cnanovdb_GridType_Mask = 8,
    cnanovdb_GridType_FP16 = 9,
    cnanovdb_GridType_End = 10
};

#define ROOT_LEVEL 3

#define DEFINEMASK_int(LOG2DIM, SIZE)                                                              \
    typedef struct                                                                                 \
    {                                                                                              \
        uint64_t mWords[SIZE >> 6];                                                                \
    } cnanovdb_mask##LOG2DIM;                                                                      \
                                                                                                   \
    static void cnanovdb_mask##LOG2DIM##_clear(                                                    \
      CNANOVDB_GLOBAL cnanovdb_mask##LOG2DIM* RESTRICT mask)                                       \
    {                                                                                              \
        for (uint32_t i = 0; i < (SIZE >> 6); i++)                                                 \
            mask->mWords[i] = 0;                                                                   \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_mask##LOG2DIM##_isOn(                                                     \
      const CNANOVDB_GLOBAL cnanovdb_mask##LOG2DIM* RESTRICT mask, uint32_t n)                     \
    {                                                                                              \
        return 0 != (mask->mWords[n >> 6] & (((uint64_t) (1)) << (n & 63)));                       \
    }                                                                                              \
    /**/

#define DEFINEMASK(LOG2DIM) DEFINEMASK_int(LOG2DIM, (1U << (3 * LOG2DIM)))

#define INSTANTIATE(LOG2DIM) DEFINEMASK(LOG2DIM)

INSTANTIATE(3)
INSTANTIATE(4)
INSTANTIATE(5)

typedef struct
{
    float mMatF[9];    // r,c = 3*r + c
    float mInvMatF[9]; // r,c = 3*r + c
    float mVecF[3];
    float mTaperF;
    double mMatD[9];    // r,c = 3*r + c
    double mInvMatD[9]; // r,c = 3*r + c
    double mVecD[3];
    double mTaperD;
} cnanovdb_map;

typedef struct
{
    float mVec[3];
} cnanovdb_Vec3F;

typedef struct
{
    int32_t mVec[3];
} cnanovdb_coord;

static int cnanovdb_coord_compare(const CNANOVDB_GLOBAL cnanovdb_coord* a, const cnanovdb_coord* b)
{
    if (a->mVec[0] < b->mVec[0])
        return -1;
    if (a->mVec[0] > b->mVec[0])
        return 1;
    if (a->mVec[1] < b->mVec[1])
        return -1;
    if (a->mVec[1] > b->mVec[1])
        return 1;
    if (a->mVec[2] < b->mVec[2])
        return -1;
    if (a->mVec[2] > b->mVec[2])
        return 1;
    return 0;
}

#ifdef USE_SINGLE_ROOT_KEY
static uint64_t cnanovdb_coord_to_key(const cnanovdb_coord* RESTRICT ijk)
{
    // Define to workaround a bug with 64-bit shifts in the AMD OpenCL compiler.
#if defined(AVOID_64BIT_SHIFT)
    uint2 key = (uint2) (((uint32_t) ijk->mVec[2]) >> 12, 0) |
                (uint2) ((((uint32_t) ijk->mVec[1]) >> 12) << 21, ((uint32_t) ijk->mVec[1]) >> 23) |
                (uint2) (0, (((uint32_t) ijk->mVec[0]) >> 12) << 10);
    return *(uint64_t*) &key;
#else
    return ((uint64_t) (((uint32_t) ijk->mVec[2]) >> 12)) |
           (((uint64_t) (((uint32_t) ijk->mVec[1]) >> 12)) << 21) |
           (((uint64_t) (((uint32_t) ijk->mVec[0]) >> 12)) << 42);
#endif
}
#else
static void cnanovdb_coord_to_key(cnanovdb_coord* RESTRICT key, const cnanovdb_coord* RESTRICT ijk)
{
    key->mVec[0] = ijk->mVec[0] & ~((1u << 12) - 1u);
    key->mVec[1] = ijk->mVec[1] & ~((1u << 12) - 1u);
    key->mVec[2] = ijk->mVec[2] & ~((1u << 12) - 1u);
}
#endif

static void cnanovdb_map_apply(cnanovdb_Vec3F* dst,
                               const CNANOVDB_GLOBAL cnanovdb_map* RESTRICT map,
                               const cnanovdb_Vec3F* src)
{
    float sx = src->mVec[0];
    float sy = src->mVec[1];
    float sz = src->mVec[2];
    dst->mVec[0] = sx * map->mMatF[0] + sy * map->mMatF[1] + sz * map->mMatF[2] + map->mVecF[0];
    dst->mVec[1] = sx * map->mMatF[3] + sy * map->mMatF[4] + sz * map->mMatF[5] + map->mVecF[1];
    dst->mVec[2] = sx * map->mMatF[6] + sy * map->mMatF[7] + sz * map->mMatF[8] + map->mVecF[2];
}

static void cnanovdb_map_applyInverse(cnanovdb_Vec3F* dst,
                                      const CNANOVDB_GLOBAL cnanovdb_map* RESTRICT map,
                                      const cnanovdb_Vec3F* src)
{
    float sx = src->mVec[0] - map->mVecF[0];
    float sy = src->mVec[1] - map->mVecF[1];
    float sz = src->mVec[2] - map->mVecF[2];
    dst->mVec[0] = sx * map->mInvMatF[0] + sy * map->mInvMatF[1] + sz * map->mInvMatF[2];
    dst->mVec[1] = sx * map->mInvMatF[3] + sy * map->mInvMatF[4] + sz * map->mInvMatF[5];
    dst->mVec[2] = sx * map->mInvMatF[6] + sy * map->mInvMatF[7] + sz * map->mInvMatF[8];
}

static void cnanovdb_map_applyJacobi(cnanovdb_Vec3F* dst,
                                     const CNANOVDB_GLOBAL cnanovdb_map* RESTRICT map,
                                     const cnanovdb_Vec3F* src)
{
    float sx = src->mVec[0];
    float sy = src->mVec[1];
    float sz = src->mVec[2];
    dst->mVec[0] = sx * map->mMatF[0] + sy * map->mMatF[1] + sz * map->mMatF[2];
    dst->mVec[1] = sx * map->mMatF[3] + sy * map->mMatF[4] + sz * map->mMatF[5];
    dst->mVec[2] = sx * map->mMatF[6] + sy * map->mMatF[7] + sz * map->mMatF[8];
}

static void cnanovdb_map_applyInverseJacobi(cnanovdb_Vec3F* dst,
                                            const CNANOVDB_GLOBAL cnanovdb_map* RESTRICT map,
                                            const cnanovdb_Vec3F* src)
{
    float sx = src->mVec[0];
    float sy = src->mVec[1];
    float sz = src->mVec[2];
    dst->mVec[0] = sx * map->mInvMatF[0] + sy * map->mInvMatF[1] + sz * map->mInvMatF[2];
    dst->mVec[1] = sx * map->mInvMatF[3] + sy * map->mInvMatF[4] + sz * map->mInvMatF[5];
    dst->mVec[2] = sx * map->mInvMatF[6] + sy * map->mInvMatF[7] + sz * map->mInvMatF[8];
}

static void cnanovdb_map_applyIJT(cnanovdb_Vec3F* dst,
                                  const CNANOVDB_GLOBAL cnanovdb_map* RESTRICT map,
                                  const cnanovdb_Vec3F* src)
{
    float sx = src->mVec[0];
    float sy = src->mVec[1];
    float sz = src->mVec[2];
    dst->mVec[0] = sx * map->mInvMatF[0] + sy * map->mInvMatF[3] + sz * map->mInvMatF[6];
    dst->mVec[1] = sx * map->mInvMatF[1] + sy * map->mInvMatF[4] + sz * map->mInvMatF[7];
    dst->mVec[2] = sx * map->mInvMatF[2] + sy * map->mInvMatF[5] + sz * map->mInvMatF[8];
}

typedef struct
{
    int64_t mByteOffset;    // byte offset to the blind data, relative to the GridData.
    uint64_t mElementCount; // number of elements, e.g. point count
    uint32_t mFlags;        // flags
    uint32_t mSemantic;     // semantic meaning of the data.
    uint32_t mDataClass;    // 4 bytes
    uint32_t mDataType;     // 4 bytes
    char mName[256];
    uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(int64_t) + sizeof(uint64_t) +
                                                   2 * sizeof(uint32_t) + 2 * sizeof(uint32_t) +
                                                   256 * sizeof(char),
                                                 CNANOVDB_DATA_ALIGNMENT)];
} cnanovdb_gridblindmetadata;

typedef struct
{
    uint64_t mMagic;     // 8B magic to validate it is valid grid data.
    uint64_t mChecksum;  // 8B. Checksum of grid buffer.
    uint32_t mVersion;   // 4B. compacted major.minor.path version number.
    uint32_t mFlags;     // 4B. flags for grid.
    uint32_t mGridIndex; // 4B. Index of this grid in the buffer
    uint32_t mGridCount; // 4B. Total number of grids in the buffer
    uint64_t mGridSize;  // 8B. byte count of this entire grid occupied in the buffer.
    char mGridName[256]; // 256B
    cnanovdb_map mMap;   // 264B. affine transformation between index and world space in both single
                         // and double precision
    double mBBox[6];     // 48B. floating-point bounds of active values in WORLD SPACE
    double mVoxelSize[3];          // 24B. size of a voxel in world units
    uint32_t mGridClass;           // 4B.
    uint32_t mGridType;            // 4B.
    uint64_t mBlindMetadataOffset; // 8B. offset of GridBlindMetaData structures.
    int32_t mBlindMetadataCount;   // 4B. count of GridBlindMetaData structures.
    uint32_t _reserved[CNANOVDB_ALIGNMENT_PADDING(8 + 8 + 4 + 4 + 4 + 4 + 8 + 256 + 24 + 24 +
                                                    sizeof(cnanovdb_map) + 24 + 4 + 4 + 8 + 4,
                                                  CNANOVDB_DATA_ALIGNMENT) /
                       4];
} cnanovdb_griddata;

static void cnanovdb_griddata_worldToIndex(cnanovdb_Vec3F* dst,
                                           const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid,
                                           const cnanovdb_Vec3F* src)
{
    cnanovdb_map_applyInverse(dst, &grid->mMap, src);
}

static void cnanovdb_griddata_indexToWorld(cnanovdb_Vec3F* dst,
                                           const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid,
                                           const cnanovdb_Vec3F* src)
{
    cnanovdb_map_apply(dst, &grid->mMap, src);
}

static void
cnanovdb_griddata_worldToIndexDir(cnanovdb_Vec3F* dst,
                                  const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid,
                                  const cnanovdb_Vec3F* src)
{
    cnanovdb_map_applyInverseJacobi(dst, &grid->mMap, src);
}

static void
cnanovdb_griddata_indexToWorldDir(cnanovdb_Vec3F* dst,
                                  const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid,
                                  const cnanovdb_Vec3F* src)
{
    cnanovdb_map_applyJacobi(dst, &grid->mMap, src);
}

static void cnanovdb_griddata_applyIJT(cnanovdb_Vec3F* dst,
                                       const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid,
                                       const cnanovdb_Vec3F* src)
{
    cnanovdb_map_applyIJT(dst, &grid->mMap, src);
}

typedef struct
{
    uint64_t mNodeOffset[ROOT_LEVEL + 1];
    uint32_t mNodeCount[ROOT_LEVEL];
    uint32_t mTileCount[ROOT_LEVEL];
    uint64_t mVoxelCount;
    uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(4 * sizeof(uint64_t) + (3 + 3) * sizeof(uint32_t) +
                                                   sizeof(uint64_t),
                                                 CNANOVDB_DATA_ALIGNMENT)];
} cnanovdb_treedata;

static const CNANOVDB_GLOBAL cnanovdb_treedata*
cnanovdb_griddata_tree(const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT griddata)
{
    return (const CNANOVDB_GLOBAL cnanovdb_treedata*) (griddata + 1);
}

#define CREATE_TILEENTRY(VALUETYPE, SUFFIX)                                                        \
    typedef union                                                                                  \
    {                                                                                              \
        VALUETYPE value;                                                                           \
        uint64_t child;                                                                            \
    } cnanovdb_tileentry##SUFFIX;                                                                  \
    /**/

typedef struct
{
    cnanovdb_coord mKey;
    const CNANOVDB_GLOBAL void* mNode[4];
} cnanovdb_readaccessor;

static void cnanovdb_readaccessor_insert(cnanovdb_readaccessor* RESTRICT acc,
                                         int childlevel,
                                         const CNANOVDB_GLOBAL void* RESTRICT node,
                                         const cnanovdb_coord* RESTRICT ijk)
{
    acc->mNode[childlevel] = node;
    acc->mKey.mVec[0] = ijk->mVec[0];
    acc->mKey.mVec[1] = ijk->mVec[1];
    acc->mKey.mVec[2] = ijk->mVec[2];
}

#define CREATE_LEAF_NODE_int(                                                                      \
  LEVEL, LOG2DIM, CHILDTOTAL, TOTAL, MASK, VALUETYPE, STATSTYPE, SUFFIX)                           \
    typedef struct                                                                                 \
    {                                                                                              \
        cnanovdb_coord mBBox_min;                                                                  \
        uint8_t mBBoxDif[3];                                                                       \
        uint8_t mFlags;                                                                            \
        cnanovdb_mask##LOG2DIM mValueMask;                                                         \
        VALUETYPE mMinimum;                                                                        \
        VALUETYPE mMaximum;                                                                        \
        STATSTYPE mAverage;                                                                        \
        STATSTYPE mStdDevi;                                                                        \
        uint32_t _reserved[CNANOVDB_ALIGNMENT_PADDING(                                             \
                             sizeof(cnanovdb_mask##LOG2DIM) + 2 * sizeof(VALUETYPE) +              \
                               2 * sizeof(STATSTYPE) + sizeof(cnanovdb_coord) +                    \
                               sizeof(uint8_t[3]) + sizeof(uint8_t),                               \
                             CNANOVDB_DATA_ALIGNMENT) /                                            \
                           4];                                                                     \
        VALUETYPE mVoxels[1u << (3 * LOG2DIM)];                                                    \
    } cnanovdb_node##LEVEL##SUFFIX;                                                                \
                                                                                                   \
    static uint32_t cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(                                  \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        return (((ijk->mVec[0] & MASK) >> CHILDTOTAL) << (2 * LOG2DIM)) +                          \
               (((ijk->mVec[1] & MASK) >> CHILDTOTAL) << (LOG2DIM)) +                              \
               ((ijk->mVec[2] & MASK) >> CHILDTOTAL);                                              \
    }                                                                                              \
                                                                                                   \
    static VALUETYPE cnanovdb_node##LEVEL##SUFFIX##_getValue(                                      \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        return node->mVoxels[n];                                                                   \
    }                                                                                              \
                                                                                                   \
    static VALUETYPE cnanovdb_node##LEVEL##SUFFIX##_getValueAndCache(                              \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk,                                                          \
      cnanovdb_readaccessor* RESTRICT /* DO NOT REMOVE: Required for C99 compliance */ acc)        \
    {                                                                                              \
        (void) (acc);                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        return node->mVoxels[n];                                                                   \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_node##LEVEL##SUFFIX##_isActive(                                           \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        if (cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n))                                   \
            return true;                                                                           \
        return false;                                                                              \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_node##LEVEL##SUFFIX##_isActiveAndCache(                                   \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk,                                                          \
      cnanovdb_readaccessor* RESTRICT /* DO NOT REMOVE: Required for C99 compliance */ acc)        \
    {                                                                                              \
        (void) (acc);                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        if (cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n))                                   \
            return true;                                                                           \
        return false;                                                                              \
    }                                                                                              \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX*                                     \
      cnanovdb_tree_getNode##LEVEL##SUFFIX(const CNANOVDB_GLOBAL cnanovdb_treedata* RESTRICT tree, \
                                           uint64_t i)                                             \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* basenode =                             \
          (const CNANOVDB_GLOBAL                                                                   \
             cnanovdb_node##LEVEL##SUFFIX*) ((CNANOVDB_GLOBAL uint8_t*) (tree) +                   \
                                             tree->mNodeOffset[LEVEL]);                            \
        return basenode + i;                                                                       \
    }                                                                                              \
                                                                                                   \
    /**/

#define CREATE_LEAF_NODE(LEVEL, LOG2DIM, TOTAL, VALUETYPE, STATSTYPE, SUFFIX)                      \
    CREATE_LEAF_NODE_int(LEVEL,                                                                    \
                         LOG2DIM,                                                                  \
                         (TOTAL - LOG2DIM),                                                        \
                         TOTAL,                                                                    \
                         ((1u << TOTAL) - 1u),                                                     \
                         VALUETYPE,                                                                \
                         STATSTYPE,                                                                \
                         SUFFIX)

#define CREATE_INTERNAL_NODE_int(                                                                  \
  CHILDLEVEL, LEVEL, LOG2DIM, CHILDTOTAL, TOTAL, MASK, VALUETYPE, STATSTYPE, SUFFIX)               \
    typedef struct                                                                                 \
    {                                                                                              \
        cnanovdb_coord mBBox_min, mBBox_max;                                                       \
        int32_t mOffset;                                                                           \
        uint32_t mFlags;                                                                           \
        cnanovdb_mask##LOG2DIM mValueMask, mChildMask;                                             \
        VALUETYPE mMinimum, mMaximum;                                                              \
        STATSTYPE mAverage, mStdDevi;                                                              \
        uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(                                              \
          sizeof(cnanovdb_mask##LOG2DIM) + sizeof(VALUETYPE) * 2 + sizeof(STATSTYPE) * 2 +         \
            sizeof(cnanovdb_coord) * 2 + sizeof(int32_t) + sizeof(uint32_t),                       \
          CNANOVDB_DATA_ALIGNMENT)];                                                               \
        cnanovdb_tileentry##SUFFIX mTable[1u << (3 * LOG2DIM)];                                    \
    } cnanovdb_node##LEVEL##SUFFIX;                                                                \
                                                                                                   \
    static uint32_t cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(                                  \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        return (((ijk->mVec[0] & MASK) >> CHILDTOTAL) << (2 * LOG2DIM)) +                          \
               (((ijk->mVec[1] & MASK) >> CHILDTOTAL) << (LOG2DIM)) +                              \
               ((ijk->mVec[2] & MASK) >> CHILDTOTAL);                                              \
    }                                                                                              \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX*                                \
      cnanovdb_node##LEVEL##SUFFIX##_getChild(                                                     \
        const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node, uint32_t n)             \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX* childnode =                       \
          (const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX*) (((CNANOVDB_GLOBAL uint8_t*)  \
                                                                         node) +                   \
                                                                      node->mTable[n].child);      \
        return childnode;                                                                          \
    }                                                                                              \
                                                                                                   \
    static VALUETYPE cnanovdb_node##LEVEL##SUFFIX##_getValue(                                      \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n))                                   \
        {                                                                                          \
            const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX* child =                       \
              cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n);                                    \
            return cnanovdb_node##CHILDLEVEL##SUFFIX##_getValue(child, ijk);                       \
        }                                                                                          \
        return node->mTable[n].value;                                                              \
    }                                                                                              \
                                                                                                   \
    static VALUETYPE cnanovdb_node##LEVEL##SUFFIX##_getValueAndCache(                              \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk,                                                          \
      cnanovdb_readaccessor* RESTRICT acc)                                                         \
    {                                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n))                                   \
        {                                                                                          \
            const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX* child =                       \
              cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n);                                    \
            cnanovdb_readaccessor_insert(acc, CHILDLEVEL, child, ijk);                             \
            return cnanovdb_node##CHILDLEVEL##SUFFIX##_getValueAndCache(child, ijk, acc);          \
        }                                                                                          \
        return node->mTable[n].value;                                                              \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_node##LEVEL##SUFFIX##_isActive(                                           \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n))                                   \
        {                                                                                          \
            const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX* child =                       \
              cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n);                                    \
            return cnanovdb_node##CHILDLEVEL##SUFFIX##_isActive(child, ijk);                       \
        }                                                                                          \
        return cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n) ? true : false;                 \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_node##LEVEL##SUFFIX##_isActiveAndCache(                                   \
      const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* RESTRICT node,                           \
      const cnanovdb_coord* RESTRICT ijk,                                                          \
      cnanovdb_readaccessor* RESTRICT acc)                                                         \
    {                                                                                              \
        uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk);                            \
        if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n))                                   \
        {                                                                                          \
            const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX* child =                       \
              cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n);                                    \
            cnanovdb_readaccessor_insert(acc, CHILDLEVEL, child, ijk);                             \
            return cnanovdb_node##CHILDLEVEL##SUFFIX##_isActiveAndCache(child, ijk, acc);          \
        }                                                                                          \
        return cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n) ? true : false;                 \
    }                                                                                              \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX*                                     \
      cnanovdb_tree_getNode##LEVEL##SUFFIX(const CNANOVDB_GLOBAL cnanovdb_treedata* RESTRICT tree, \
                                           uint64_t i)                                             \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX* basenode =                             \
          (const CNANOVDB_GLOBAL                                                                   \
             cnanovdb_node##LEVEL##SUFFIX*) ((CNANOVDB_GLOBAL uint8_t*) (tree) +                   \
                                             tree->mNodeOffset[LEVEL]);                            \
        return basenode + i;                                                                       \
    }                                                                                              \
                                                                                                   \
    /**/

#define CREATE_INTERNAL_NODE(CHILDLEVEL, LEVEL, LOG2DIM, TOTAL, VALUETYPE, STATSTYPE, SUFFIX)      \
    CREATE_INTERNAL_NODE_int(CHILDLEVEL,                                                           \
                             LEVEL,                                                                \
                             LOG2DIM,                                                              \
                             (TOTAL - LOG2DIM),                                                    \
                             TOTAL,                                                                \
                             ((1u << TOTAL) - 1u),                                                 \
                             VALUETYPE,                                                            \
                             STATSTYPE,                                                            \
                             SUFFIX)

#ifdef USE_SINGLE_ROOT_KEY
#define DEFINE_KEY(KEY) uint64_t KEY;
#define KEYSIZE sizeof(uint64_t)

#define KEYSEARCH(SUFFIX)                                                                          \
    uint64_t key;                                                                                  \
    key = cnanovdb_coord_to_key(ijk);                                                              \
                                                                                                   \
    for (int i = low; i < high; i++)                                                               \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tile = tiles + i;                    \
        if (tile->key == key)                                                                      \
            return tile;                                                                           \
    }                                                                                              \
/**/
#else
#define DEFINE_KEY(KEY) cnanovdb_coord KEY;
#define KEYSIZE sizeof(cnanovdb_coord)
#define KEYSEARCH(SUFFIX)                                                                          \
    cnanovdb_coord key;                                                                            \
    cnanovdb_coord_to_key(&key, ijk);                                                              \
                                                                                                   \
    while (low != high)                                                                            \
    {                                                                                              \
        int32_t mid = low + ((high - low) >> 1);                                                   \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tile = tiles + mid;                  \
                                                                                                   \
        int keycmp = cnanovdb_coord_compare(&tile->key, &key);                                     \
        if (keycmp == 0)                                                                           \
        {                                                                                          \
            return tile;                                                                           \
        }                                                                                          \
                                                                                                   \
        if (keycmp < 0)                                                                            \
            low = mid + 1;                                                                         \
        else                                                                                       \
            high = mid;                                                                            \
    }                                                                                              \
/**/
#endif

#define CREATE_ROOTDATA(VALUETYPE, STATSTYPE, SUFFIX)                                              \
    typedef struct                                                                                 \
    {                                                                                              \
        DEFINE_KEY(key);                                                                           \
        int64_t child;                                                                             \
        uint32_t state;                                                                            \
        VALUETYPE value;                                                                           \
        uint8_t _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(KEYSIZE) + sizeof(VALUETYPE) +         \
                                                       sizeof(int64_t) + sizeof(uint32_t),         \
                                                     CNANOVDB_DATA_ALIGNMENT)];                    \
    } cnanovdb_rootdata_tile##SUFFIX;                                                              \
                                                                                                   \
    typedef struct                                                                                 \
    {                                                                                              \
        cnanovdb_coord mBBox_min, mBBox_max;                                                       \
        uint32_t mTableSize;                                                                       \
        VALUETYPE mBackground;                                                                     \
        VALUETYPE mMinimum, mMaximum;                                                              \
        STATSTYPE mAverage, mStdDevi;                                                              \
        uint32_t                                                                                   \
          _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_coord) * 2 + sizeof(uint32_t) +     \
                                                 sizeof(VALUETYPE) * 3 + sizeof(STATSTYPE) * 2,    \
                                               CNANOVDB_DATA_ALIGNMENT) /                          \
                    4];                                                                            \
    } cnanovdb_rootdata##SUFFIX;                                                                   \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* cnanovdb_treedata_root##SUFFIX(        \
      const CNANOVDB_GLOBAL cnanovdb_treedata* RESTRICT treedata)                                  \
    {                                                                                              \
        return (const CNANOVDB_GLOBAL                                                              \
                  cnanovdb_rootdata##SUFFIX*) ((const CNANOVDB_GLOBAL uint8_t*) (treedata) +       \
                                               treedata->mNodeOffset[ROOT_LEVEL]);                 \
    }                                                                                              \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX*                                   \
      cnanovdb_rootdata##SUFFIX##_getTile(                                                         \
        const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata, uint32_t n)            \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* basetile =                           \
          (const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX*) (rootdata + 1);                  \
        return basetile + n;                                                                       \
    }                                                                                              \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX* cnanovdb_rootdata##SUFFIX##_getChild(     \
      const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata,                          \
      const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* RESTRICT tile)                         \
    {                                                                                              \
        CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX* basenode =                                         \
          (CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX*) (((CNANOVDB_GLOBAL uint8_t*) rootdata) +       \
                                                     tile->child);                                 \
        return basenode;                                                                           \
    }                                                                                              \
                                                                                                   \
    static const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX*                                   \
      cnanovdb_rootdata##SUFFIX##_findTile(                                                        \
        const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata,                        \
        const cnanovdb_coord* RESTRICT ijk)                                                        \
    {                                                                                              \
        int32_t low = 0, high = rootdata->mTableSize;                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tiles =                              \
          cnanovdb_rootdata##SUFFIX##_getTile(rootdata, 0);                                        \
                                                                                                   \
        KEYSEARCH(SUFFIX)                                                                          \
        return 0;                                                                                  \
    }                                                                                              \
                                                                                                   \
    static VALUETYPE cnanovdb_rootdata##SUFFIX##_getValue(                                         \
      const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata,                          \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tile =                               \
          cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk);                                     \
        if (!tile)                                                                                 \
            return rootdata->mBackground;                                                          \
        if (tile->child == 0)                                                                      \
            return tile->value;                                                                    \
        return cnanovdb_node2##SUFFIX##_getValue(                                                  \
          cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile), ijk);                              \
    }                                                                                              \
                                                                                                   \
    static VALUETYPE cnanovdb_rootdata##SUFFIX##_getValueAndCache(                                 \
      const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata,                          \
      const cnanovdb_coord* RESTRICT ijk,                                                          \
      cnanovdb_readaccessor* RESTRICT acc)                                                         \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tile =                               \
          cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk);                                     \
        if (!tile)                                                                                 \
            return rootdata->mBackground;                                                          \
        if (tile->child == 0)                                                                      \
            return tile->value;                                                                    \
        const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX* child =                                      \
          cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile);                                    \
        cnanovdb_readaccessor_insert(acc, 2, child, ijk);                                          \
        return cnanovdb_node2##SUFFIX##_getValueAndCache(child, ijk, acc);                         \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_rootdata##SUFFIX##_isActive(                                              \
      const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata,                          \
      const cnanovdb_coord* RESTRICT ijk)                                                          \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tile =                               \
          cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk);                                     \
        if (!tile)                                                                                 \
            return false;                                                                          \
        if (tile->child == 0)                                                                      \
            return tile->state;                                                                    \
        return cnanovdb_node2##SUFFIX##_isActive(                                                  \
          cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile), ijk);                              \
    }                                                                                              \
                                                                                                   \
    static bool cnanovdb_rootdata##SUFFIX##_isActiveAndCache(                                      \
      const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX* RESTRICT rootdata,                          \
      const cnanovdb_coord* RESTRICT ijk,                                                          \
      cnanovdb_readaccessor* RESTRICT acc)                                                         \
    {                                                                                              \
        const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX* tile =                               \
          cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk);                                     \
        if (!tile)                                                                                 \
            return false;                                                                          \
        if (tile->child == 0)                                                                      \
            return tile->state;                                                                    \
        const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX* child =                                      \
          cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile);                                    \
        cnanovdb_readaccessor_insert(acc, 2, child, ijk);                                          \
        return cnanovdb_node2##SUFFIX##_isActiveAndCache(child, ijk, acc);                         \
    }                                                                                              \
    /**/

inline void
cnanovdb_readaccessor_init(cnanovdb_readaccessor* RESTRICT acc,
                           const CNANOVDB_GLOBAL void /*cnanovdb_rootdata* */* RESTRICT rootdata)
{
    acc->mNode[0] = acc->mNode[1] = acc->mNode[2] = 0;
    acc->mNode[3] = rootdata;
}

#define DEFINE_ISCACHED(LEVEL, MASK)                                                               \
    inline bool cnanovdb_readaccessor_isCached##LEVEL(cnanovdb_readaccessor* RESTRICT acc,         \
                                                      int32_t dirty)                               \
    {                                                                                              \
        if (!acc->mNode[LEVEL])                                                                    \
            return false;                                                                          \
        if (dirty & ~MASK)                                                                         \
        {                                                                                          \
            acc->mNode[LEVEL] = 0;                                                                 \
            return false;                                                                          \
        }                                                                                          \
        return true;                                                                               \
    }                                                                                              \
    /**/

DEFINE_ISCACHED(0, ((1u << 3) - 1u))
DEFINE_ISCACHED(1, ((1u << 7) - 1u))
DEFINE_ISCACHED(2, ((1u << 12) - 1u))

inline int32_t cnanovdb_readaccessor_computeDirty(const cnanovdb_readaccessor* RESTRICT acc,
                                                  const cnanovdb_coord* RESTRICT ijk)
{
    return (ijk->mVec[0] ^ acc->mKey.mVec[0]) | (ijk->mVec[1] ^ acc->mKey.mVec[1]) |
           (ijk->mVec[2] ^ acc->mKey.mVec[2]);
}

#define CREATE_ACCESSOR(VALUETYPE, SUFFIX)                                                         \
    inline VALUETYPE cnanovdb_readaccessor_getValue##SUFFIX(cnanovdb_readaccessor* RESTRICT acc,   \
                                                            const cnanovdb_coord* RESTRICT ijk)    \
    {                                                                                              \
        int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk);                              \
                                                                                                   \
        if (cnanovdb_readaccessor_isCached0(acc, dirty))                                           \
            return cnanovdb_node0##SUFFIX##_getValue(                                              \
              ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX*) acc->mNode[0]), ijk);                     \
        if (cnanovdb_readaccessor_isCached1(acc, dirty))                                           \
            return cnanovdb_node1##SUFFIX##_getValueAndCache(                                      \
              ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX*) acc->mNode[1]), ijk, acc);                \
        if (cnanovdb_readaccessor_isCached2(acc, dirty))                                           \
            return cnanovdb_node2##SUFFIX##_getValueAndCache(                                      \
              ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX*) acc->mNode[2]), ijk, acc);                \
                                                                                                   \
        return cnanovdb_rootdata##SUFFIX##_getValueAndCache(                                       \
          ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX*) acc->mNode[3]), ijk, acc);                 \
    }                                                                                              \
                                                                                                   \
    inline bool cnanovdb_readaccessor_isActive##SUFFIX(cnanovdb_readaccessor* RESTRICT acc,        \
                                                       const cnanovdb_coord* RESTRICT ijk)         \
    {                                                                                              \
        int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk);                              \
                                                                                                   \
        if (cnanovdb_readaccessor_isCached0(acc, dirty))                                           \
            return cnanovdb_node0##SUFFIX##_isActive(                                              \
              ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX*) acc->mNode[0]), ijk);                     \
        if (cnanovdb_readaccessor_isCached1(acc, dirty))                                           \
            return cnanovdb_node1##SUFFIX##_isActiveAndCache(                                      \
              ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX*) acc->mNode[1]), ijk, acc);                \
        if (cnanovdb_readaccessor_isCached2(acc, dirty))                                           \
            return cnanovdb_node2##SUFFIX##_isActiveAndCache(                                      \
              ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX*) acc->mNode[2]), ijk, acc);                \
                                                                                                   \
        return cnanovdb_rootdata##SUFFIX##_isActiveAndCache(                                       \
          ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX*) acc->mNode[3]), ijk, acc);                 \
    }                                                                                              \
    /**/

#define CREATE_GRIDTYPE(VALUETYPE, STATSTYPE, SUFFIX)                                              \
    CREATE_TILEENTRY(VALUETYPE, SUFFIX)                                                            \
    CREATE_LEAF_NODE(0, 3, 3, VALUETYPE, STATSTYPE, SUFFIX)                                        \
    CREATE_INTERNAL_NODE(0, 1, 4, 7, VALUETYPE, STATSTYPE, SUFFIX)                                 \
    CREATE_INTERNAL_NODE(1, 2, 5, 12, VALUETYPE, STATSTYPE, SUFFIX)                                \
    CREATE_ROOTDATA(VALUETYPE, STATSTYPE, SUFFIX)                                                  \
    CREATE_ACCESSOR(VALUETYPE, SUFFIX)                                                             \
    /**/

CREATE_GRIDTYPE(float, float, F)
CREATE_GRIDTYPE(bool, bool, B)
CREATE_GRIDTYPE(int, int, I32)

static int cnanovdb_griddata_valid(const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid)
{
    if (!grid)
        return 0;
    if (grid->mMagic != 0x304244566f6e614eUL)
        return 0;
    return 1;
}

static int cnanovdb_griddata_validF(const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid)
{
    if (!cnanovdb_griddata_valid(grid))
        return 0;
    if (grid->mGridType != cnanovdb_GridType_Float)
        return 0;
    return 1;
}

static int cnanovdb_griddata_validF3(const CNANOVDB_GLOBAL cnanovdb_griddata* RESTRICT grid)
{
    if (!cnanovdb_griddata_valid(grid))
        return 0;
    if (grid->mGridType != cnanovdb_GridType_Vec3f)
        return 0;
    return 1;
}

#endif
