// Copyright 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once

// ---------------------------------------------------------------------------
// Sample inner nodes.
//
// Note: We generate files VdbSampleInner_<univary_in>_<univary_out>_<level>.ih
//       from this template using CMake.
// ---------------------------------------------------------------------------

#if (@VKL_VDB_NEXT_LEVEL@+1) < VKL_VDB_NUM_LEVELS
  #include "VdbSamplerDispatchInner_@VKL_VDB_NEXT_LEVEL@.h"
#endif

#define univary_in @VKL_VDB_UNIVARY_IN@
#define univary_out @VKL_VDB_UNIVARY_OUT@

#if (@VKL_VDB_NEXT_LEVEL@ >= VKL_VDB_NUM_LEVELS)
#error "VdbSampleInner.ih generated for too many levels."
#endif

/*
 * Find the level @VKL_VDB_LEVEL@ voxel containing domainOffset.
 *
 * If the voxel is a child pointer then this function will instead
 * dispatch to the next level.
 *
 * We generate versions for both varying and uniform traversal. The latter
 * is used when all sample locations are in the same leaf node.
 */
inline void VdbSampler_sampleInner_@VKL_VDB_UNIVARY_IN@_@VKL_VDB_UNIVARY_OUT@_@VKL_VDB_LEVEL@(
  const VdbSamplerShared  *sampler,
  const univary_in uint64  voxelOffset,
  const univary_out vec3ui &domainOffset,
  univary_out uint64       &voxel)
{
  /* We compute offsets in 64 bit to be safe, but access is in 32 bit! */
  assert(voxelOffset < ((univary_in uint64)1) << 32);
  assert(voxelOffset < sampler->grid->levels[@VKL_VDB_LEVEL@].numNodes
                     * VKL_VDB_NUM_VOXELS_@VKL_VDB_LEVEL@);
  const univary_in uint32 vo32 = ((univary_in uint32)voxelOffset);
  const univary_in uint64 voxelValue
    = sampler->grid->levels[@VKL_VDB_LEVEL@].voxels[vo32];

  // This voxel is pointing to a child, but we have reached max depth.
  // Come up with a tile value.
  if ((openvkl::cpu_device::vklVdbVoxelIsLeafPtr(voxelValue) || openvkl::cpu_device::vklVdbVoxelIsChildPtr(voxelValue))
   && (@VKL_VDB_LEVEL@+1 > sampler->maxSamplingDepth)) // Cannot descend!
  {
    voxel = openvkl::cpu_device::vklVdbVoxelMakeError(@VKL_VDB_LEVEL@, vo32);
    return;
  }

#if (@VKL_VDB_NEXT_LEVEL@+1) < VKL_VDB_NUM_LEVELS
  if (openvkl::cpu_device::vklVdbVoxelIsChildPtr(voxelValue)) {
    VdbSampler_dispatchInner_@VKL_VDB_UNIVARY_IN@_@VKL_VDB_UNIVARY_OUT@_@VKL_VDB_NEXT_LEVEL@(
      sampler,
      openvkl::cpu_device::vklVdbVoxelChildGetIndex(voxelValue),
      domainOffset,
      voxel);
    return;
  }
#else
  assert(!openvkl::cpu_device::vklVdbVoxelIsChildPtr(voxelValue));
#endif

  /* Observers not currently supported on GPU:
    if (!openvkl::cpu_device::vklVdbVoxelIsEmpty(voxelValue)
     && VdbLeafAccessObserver_isObservable(sampler))
    {
      const univary_in uint32 leafIndex = vklVdbVoxelLeafGetIndex(voxelValue);
      VdbLeafAccessObserver_observe_@VKL_VDB_UNIVARY_IN@(sampler, leafIndex);
    }
    */

  voxel = voxelValue;
}

#undef univary_in
#undef univary_out

