using System;
using System.Collections.Generic;
namespace AcDream.Core.Terrain;
///
/// Pure-CPU slot allocator for the terrain modern dispatcher's global VBO/EBO.
/// One slot = one landblock's worth of mesh data (384 verts + 384 indices).
/// Uses a FIFO free-list for slot recycling and a monotonic counter for
/// first-time growth, mirroring WorldBuilder's TerrainRenderManager pattern.
/// All bookkeeping is CPU-side; the GPU buffer growth itself is performed
/// by TerrainModernRenderer when sets needsGrow=true.
///
public sealed class TerrainSlotAllocator
{
private readonly Queue _freeSlots = new();
private readonly HashSet _liveSlots = new();
private int _nextFreeSlot;
private int _capacity;
public TerrainSlotAllocator(int initialCapacity = 64)
{
if (initialCapacity <= 0)
throw new ArgumentOutOfRangeException(nameof(initialCapacity), "must be > 0");
_capacity = initialCapacity;
}
/// Current capacity in slots. Growable via .
public int Capacity => _capacity;
/// Slots currently in use (allocated minus freed).
public int LoadedCount => _liveSlots.Count;
///
/// Allocate a slot index. Reuses a freed slot via FIFO if available,
/// otherwise hands out the next monotonic index. Sets
/// to true when the returned slot index is
/// at or beyond current capacity — caller must
/// before using the slot.
///
public int Allocate(out bool needsGrow)
{
int slot;
if (_freeSlots.TryDequeue(out var freed))
{
slot = freed;
}
else
{
slot = _nextFreeSlot++;
}
_liveSlots.Add(slot);
needsGrow = slot >= _capacity;
return slot;
}
///
/// Return a slot to the free list. Throws if the slot wasn't currently
/// allocated (catches double-free bugs).
///
public void Free(int slot)
{
if (!_liveSlots.Remove(slot))
throw new InvalidOperationException(
$"Slot {slot} was not allocated (double-free or unknown slot).");
_freeSlots.Enqueue(slot);
}
/// Update capacity counter after the caller has grown the GPU buffers.
public void GrowTo(int newCapacity)
{
if (newCapacity < _capacity)
throw new ArgumentException("Capacity can only grow", nameof(newCapacity));
_capacity = newCapacity;
}
}