/*****************************************************************************/
/*
  Project   - MudBun
  Publisher - Long Bunny Labs
              http://LongBunnyLabs.com
  Author    - Ming-Lun "Allen" Chou
              http://AllenChou.net
*/
/******************************************************************************/

#ifndef MUDBUN_BRUSH_MASKS_FUNCS
#define MUDBUN_BRUSH_MASKS_FUNCS

#include "BrushMaskDefs.cginc"

#include "AllocationDefs.cginc"
#include "BrushDefs.cginc"
#include "NormalFuncs.cginc"
#include "VoxelDefs.cginc"

int num_brush_mask_ints()
{
  return uint(numBrushes + kBitsPerInt - 1) / kBitsPerInt;
}

#define BRUSH_MASK_SET_ALL(mask)                                               \
{                                                                              \
  for (int iInt = 0, n = num_brush_mask_ints(); iInt < n; ++iInt)              \
    mask[iInt] = (~0u);                                                        \
}

#define BRUSH_MASK_CLEAR_ALL(mask)                                             \
{                                                                              \
  for (int iInt = 0, n = num_brush_mask_ints(); iInt < n; ++iInt)              \
    mask[iInt] = 0u;                                                           \
}

#define BRUSH_MASK_SET(mask, bit)                                              \
  (mask[uint(bit) / kBitsPerInt] |= (1u << (uint(bit) % kBitsPerInt)))

#define BRUSH_MASK_CLEAR(mask, bit)                                            \
  (mask[uint(bit) / kBitsPerInt] &= (~(1u << (uint(bit) % kBitsPerInt))))

#define BRUSH_MASK_GET(mask, bit)                                              \
  ((mask[uint(bit) / kBitsPerInt] & (1u << (uint(bit) % kBitsPerInt))) != 0u)

#define READ_BRUSH_MASK(mask, iMask)                                           \
{                                                                              \
  int numInts = num_brush_mask_ints();                                         \
  int iBaseInt = iMask * numInts;                                              \
  for (int iInt = 0; iInt < numInts; ++iInt)                                   \
    mask[iInt] = brushMaskPool[iBaseInt + iInt];                               \
}

#define WRITE_BRUSH_MASK(mask, iMask)                                          \
{                                                                              \
  int numInts = num_brush_mask_ints();                                         \
  int iBaseInt = iMask * numInts;                                              \
  for (int iInt = 0; iInt < numInts; ++iInt)                                   \
    brushMaskPool[iBaseInt + iInt] = mask[iInt];                               \
}

// https://www.geeksforgeeks.org/position-of-rightmost-set-bit/
#define FOR_EACH_BRUSH(iMask, stmt)                                            \
if (iMask >= 0)                                                                \
{                                                                              \
  int numInts = num_brush_mask_ints();                                         \
  int iBaseInt = iMask * numInts;                                              \
  int iBrushBase = 0;                                                          \
  for (int iInt = 0; iInt < numInts; ++iInt, iBrushBase += kBitsPerInt)        \
  {                                                                            \
    uint currInt = brushMaskPool[iBaseInt + iInt];                             \
    while (currInt != 0)                                                       \
    {                                                                          \
      int iFirstSetBit = int(log2(currInt & (~currInt + 1u)));                 \
      int iBrush = iBrushBase + iFirstSetBit;                                  \
      if (iBrush >= numBrushes)                                                \
        break;                                                                 \
      currInt &= ~(1u << iFirstSetBit);                                        \
                                                                               \
      if (aBrush[iBrush].type == kSdfNoOp)                                     \
        continue;                                                              \
                                                                               \
      if ((aBrush[iBrush].flags & kSdfBrushFlagsHidden) != 0)                  \
        continue;                                                              \
                                                                               \
      stmt                                                                     \
    }                                                                          \
  }                                                                            \
}

// https://www.geeksforgeeks.org/position-of-rightmost-set-bit/
#define FOR_EACH_BRUSH_EXTERN_MASK(mask, stmt)                                 \
{                                                                              \
  int numInts = num_brush_mask_ints();                                         \
  int iBaseInt = 0;                                                            \
  int iBrushBase = 0;                                                          \
  for (int iInt = 0; iInt < numInts; ++iInt, iBrushBase += kBitsPerInt)        \
  {                                                                            \
    uint currInt = mask[iBaseInt + iInt];                                      \
    while (currInt != 0)                                                       \
    {                                                                          \
      int iFirstSetBit = int(log2(currInt & (~currInt + 1u)));                 \
      int iBrush = iBrushBase + iFirstSetBit;                                  \
      if (iBrush >= numBrushes)                                                \
        break;                                                                 \
      currInt &= ~(1u << iFirstSetBit);                                        \
                                                                               \
      if (aBrush[iBrush].type == kSdfNoOp)                                     \
        continue;                                                              \
                                                                               \
      if ((aBrush[iBrush].flags & kSdfBrushFlagsHidden) != 0)                  \
        continue;                                                              \
                                                                               \
      stmt                                                                     \
    }                                                                          \
  }                                                                            \
}

#ifdef MUDBUN_IS_COMPUTE_SHADER

int allocate_brush_mask()
{
  int iMask = -1;
  InterlockedAdd(aNumAllocation[kNumAllocationsBrushMasks], 1, iMask);

  if (iMask >= brushMaskPoolSize)
  {
    aNumAllocation[kNumAllocationsBrushMasks] = brushMaskPoolSize;
    iMask = -1;
  }

  return iMask;
}

int allocate_node_brush_mask(int iNode, Aabb nodeAabb)
{
  int iBrushMask = allocate_brush_mask();
  if (iBrushMask < 0)
    return -1;

  float3 clampedSurfaceShift = max(-aabb_extents(nodeAabb), surfaceShift);
  nodeAabb.boundsMin.xyz -= clampedSurfaceShift;
  nodeAabb.boundsMax.xyz += clampedSurfaceShift;

  BRUSH_MASK(brushMask);

  if (forceAllBrushes)
  {
    BRUSH_MASK_SET_ALL(brushMask);
  }
  else
  {
    BRUSH_MASK_CLEAR_ALL(brushMask);

    int iParent = nodePool[iNode].iParent;
    int iParentMask = iParent >= 0 ? nodePool[iParent].iBrushMask : -1;
    if (iParentMask >= 0)
    {
      float maxBlend = 0.0f;

      FOR_EACH_BRUSH(iParentMask, 
        if (aabb_intersects(nodeAabb, aabbTree[aBrush[iBrush].iProxy].aabb))
          maxBlend = max(maxBlend, aBrush[iBrush].blend);
      );

      nodeAabb.boundsMin -= maxBlend + voxelSize + normalDifferentiationStep;
      nodeAabb.boundsMax += maxBlend + voxelSize + normalDifferentiationStep;

      FOR_EACH_BRUSH(iParentMask, 
        if (aabb_intersects(nodeAabb, aabbTree[aBrush[iBrush].iProxy].aabb))
          BRUSH_MASK_SET(brushMask, iBrush);
      );
    }
    else
    {
      float maxBlend = 0.0f;

      AABB_TREE_QUERY_AABB(aabbTree, aabbRoot, nodeAabb, 
        maxBlend = max(maxBlend, aBrush[iData].blend);
      );

      nodeAabb.boundsMin -= maxBlend + normalDifferentiationStep;
      nodeAabb.boundsMax += maxBlend + normalDifferentiationStep;

      AABB_TREE_QUERY_AABB(aabbTree, aabbRoot, nodeAabb, 
        BRUSH_MASK_SET(brushMask, iData);
      );
    }
  }

  WRITE_BRUSH_MASK(brushMask, iBrushMask);

  return iBrushMask;
}

#endif

int get_brush_mask_index(int iNode)
{
  int iBrushMask = nodePool[iNode].iBrushMask;
  while (iBrushMask < 0)
  {
    iNode = nodePool[iNode].iParent;
    if (iNode < 0)
      break;

    iBrushMask = nodePool[iNode].iBrushMask;
  }

  return iBrushMask;
}

#endif

