/**
 * @file FreeList.h
 * @brief 带槽位复用机制的稀疏对象容器模板
 * 
 * @details
 * free_list 是一种特殊的容器，维护"已使用槽位 + 空闲链表"结构，
 * 特别适合需要频繁增删元素且使用索引作为外部句柄的场景。
 *
 * ## 核心特性
 *
 * - **O(1) 增删操作**：新增和删除操作近似常数时间复杂度
 * - **槽位复用**：删除后的槽位会被回收并重新利用，避免频繁内存分配
 * - **索引句柄**：使用 u32 索引作为外部句柄，与 id 系统完美配合
 * - **内存紧凑**：底层使用连续内存，缓存友好
 *
 * ## 数据结构原理
 *
 * ```
 * 初始状态:
 * _array: [ 空 | 空 | 空 | 空 ]
 * _next_free_index = invalid_id
 * _size = 0
 *
 * 添加元素 A、B、C 后:
 * _array: [ A | B | C | 空 ]
 * _next_free_index = invalid_id
 * _size = 3
 *
 * 删除元素 B 后（槽位 1 被标记为空闲）:
 * _array: [ A | ->2 | C | 空 ]   // 槽位 1 存储下一个空闲索引 2
 * _next_free_index = 1           // 指向第一个空闲槽位
 * _size = 2
 *
 * 再删除元素 A 后:
 * _array: [ ->1 | ->2 | C | 空 ] // 槽位 0 存储下一个空闲索引 1
 * _next_free_index = 0
 * _size = 1
 *
 * 添加新元素 D 时（复用槽位 0）:
 * _array: [ D | ->2 | C | 空 ]
 * _next_free_index = 1           // 指向下一个空闲槽位
 * _size = 2
 * ```
 *
 * ## 空闲链表机制
 *
 * 删除元素时，该槽位被加入空闲链表：
 * 1. 调用元素的析构函数
 * 2. 将当前 `_next_free_index` 值写入该槽位（作为链表下一个节点）
 * 3. 更新 `_next_free_index` 指向该槽位
 *
 * 添加元素时，优先从空闲链表获取槽位：
 * 1. 如果 `_next_free_index` 有效，复用该槽位
 * 2. 从槽位中读取下一个空闲索引，更新 `_next_free_index`
 * 3. 否则，在数组末尾追加新元素
 *
 * ## 类型要求
 *
 * 由于空闲槽位需要存储 u32 索引，要求：
 * - `sizeof(T) >= sizeof(u32)`（至少 4 字节）
 *
 * ## 使用场景
 *
 * - 游戏对象管理（实体 ID 系统）
 * - 资源句柄管理（纹理、网格等）
 * - 渲染表面管理（多窗口场景）
 * - 任何需要稳定索引句柄的系统
 *
 * ## 注意事项
 *
 * - 删除后的索引可能被新元素复用，外部系统需要处理这种情况
 * - 不保证元素在内存中的顺序
 * - 与 std::vector 配合使用时可能触发重复构造（见 USE_STL_VECTOR 宏）
 *
 * @example
 * @code
 * utl::free_list<game_object> objects;
 *
 * // 添加对象
 * u32 id1 = objects.add(player_data);
 * u32 id2 = objects.add(enemy_data);
 *
 * // 访问对象
 * objects[id1].update();
 *
 * // 删除对象（槽位被回收）
 * objects.remove(id1);
 *
 * // 新对象可能复用 id1 的槽位
 * u32 id3 = objects.add(new_data);  // id3 可能等于 id1
 * @endcode
 *
 * @see utl::vector
 * @see DEFINE_TYPED_ID
 */
#pragma once
#include "CommonHeader.h"

namespace XEngine::utl {

#if USE_STL_VECTOR
#pragma message("WARNING: using utl::free_list with std::vector result in duplicate calls to class constructor!")
#endif

/**
 * @class free_list
 * @brief 带槽位复用机制的稀疏对象容器
 *
 * @tparam T 元素类型，必须满足 `sizeof(T) >= sizeof(u32)`
 *
 * @details
 * 该容器使用空闲链表管理已删除的槽位，实现 O(1) 的增删操作。
 * 特别适合需要稳定索引句柄的场景，如游戏对象管理、资源句柄等。
 */
template<typename T>
class free_list
{
	static_assert(sizeof(T) >= sizeof(u32), "Type T must be at least 4 bytes to store free list indices");

public:
	/**
	 * @brief 默认构造函数，创建空容器
	 * 
	 * @details
	 * 创建一个空的 free_list，不预分配任何内存。
	 * 首次添加元素时会触发内存分配。
	 */
	free_list() = default;

	/**
	 * @brief 预留容量的构造函数
	 * 
	 * @param count 预留的元素槽位数量
	 * 
	 * @details
	 * 预分配指定数量的槽位，避免后续添加元素时的多次扩容。
	 * 适用于已知大概元素数量的场景。
	 */
	explicit free_list(u32 count)
	{
		_array.reserve(count);
	}

	/**
	 * @brief 析构函数，销毁容器
	 * 
	 * @details
	 * 断言检查容器为空（_size == 0），确保所有元素已被正确移除。
	 * 在 DEBUG 模式下使用 USE_STL_VECTOR 时，会清空内存以便检测悬空引用。
	 */
	~free_list()
	{
		assert(!_size);

#if USE_STL_VECTOR
		memset(_array.data(), 0, _array.size() * sizeof(T));
#endif
	}

	/**
	 * @brief 添加新元素并返回其槽位 ID
	 * 
	 * @tparam params 构造参数类型包
	 * @param p 传递给元素构造函数的参数
	 * @return 新分配的槽位 ID（u32 索引）
	 * 
	 * @details
	 * 添加元素的策略：
	 * 1. **有空闲槽位**：从空闲链表头部取出一个槽位复用
	 * 2. **无空闲槽位**：在数组末尾追加新元素
	 * 
	 * 复用槽位时：
	 * - 从该槽位读取下一个空闲索引，更新 `_next_free_index`
	 * - 使用 placement new 在该槽位构造新元素
	 * 
	 * @note 返回的 ID 在元素被删除前保持有效
	 * @note 删除后该 ID 可能被新元素复用
	 */
	template<class ... params>
	constexpr u32 add(params&&... p)
	{
		u32 id{ u32_invalid_id };
		if (_next_free_index == u32_invalid_id)
		{
			id = (u32)_array.size();
			_array.emplace_back(std::forward<params>(p)...);
		}
		else
		{
			id = _next_free_index;
			assert(id < _array.size() && already_removed(id));
			_next_free_index = *(const u32 *const)std::addressof(_array[id]);
			new (std::addressof(_array[id])) T(std::forward<params>(p)...);
		}
		++_size;

		return id;
	}

	/**
	 * @brief 移除指定 ID 的元素并回收其槽位
	 * 
	 * @param id 要移除元素的槽位 ID
	 * 
	 * @details
	 * 移除元素的步骤：
	 * 1. 调用元素的析构函数
	 * 2. DEBUG 模式下填充 0xcc 标记已删除
	 * 3. 将当前 `_next_free_index` 写入该槽位（链表链接）
	 * 4. 更新 `_next_free_index` 指向该槽位
	 * 
	 * @pre id 必须是有效的已分配槽位
	 * @pre 该槽位未被删除（不能重复删除）
	 */
	constexpr void remove(u32 id)
	{
		assert(id < _array.size() && !already_removed(id));
		T& item{ _array[id] };
		item.~T();
		DEBUG_OP(memset(std::addressof(_array[id]), 0xcc, sizeof(T)));
		*(u32 *const)std::addressof(_array[id]) = _next_free_index;
		_next_free_index = id;
		--_size;
	}

	/**
	 * @brief 获取当前有效元素数量
	 * 
	 * @return 容器中有效元素的个数
	 * 
	 * @note 这与 capacity() 不同，size() 返回实际存储的元素数
	 */
	constexpr u32 size() const
	{
		return _size;
	}

	/**
	 * @brief 获取已分配槽位总数
	 * 
	 * @return 底层数组的大小（包括空闲槽位）
	 * 
	 * @details
	 * 返回值 >= size()，因为可能存在已删除但未释放的槽位。
	 * 这些槽位会在后续添加元素时被复用。
	 */
	constexpr u32 capacity() const
	{
		return _array.size();
	}

	/**
	 * @brief 检查容器是否为空
	 * 
	 * @return 如果容器中没有有效元素则返回 true
	 */
	constexpr bool empty() const
	{
		return _size == 0;
	}

	/**
	 * @brief 通过 ID 访问元素（可修改）
	 * 
	 * @param id 元素的槽位 ID
	 * @return 元素的引用
	 * 
	 * @pre id 必须有效且指向未删除的元素
	 */
	[[nodiscard]] constexpr T& operator[](u32 id)
	{
		assert(id < _array.size() && !already_removed(id));
		return _array[id];
	}

	/**
	 * @brief 通过 ID 访问元素（只读）
	 * 
	 * @param id 元素的槽位 ID
	 * @return 元素的常量引用
	 * 
	 * @pre id 必须有效且指向未删除的元素
	 */
	[[nodiscard]] constexpr const T& operator[](u32 id) const
	{
		assert(id < _array.size() && !already_removed(id));
		return _array[id];
	}

private:
	/**
	 * @brief 检查指定槽位是否已被删除
	 * 
	 * @param id 要检查的槽位 ID
	 * @return 如果槽位已被删除则返回 true
	 * 
	 * @details
	 * 通过检查槽位内容是否被 0xcc 填充来判断是否已删除。
	 * 仅在 sizeof(T) > sizeof(u32) 时有效，否则直接返回 true。
	 * 
	 * @note 仅用于断言检查，确保不会访问已删除的元素
	 */
	constexpr bool already_removed(u32 id) const
	{
		if constexpr (sizeof(T) > sizeof(u32))
		{
			u32 i{ sizeof(u32) };
			const u8 *const p{ (const u8 *const)std::addressof(_array[id]) };
			while ((p[i] == 0xcc) && (i < sizeof(T))) ++i;
			return i == sizeof(T);
		}
		else
		{
			return true;
		}
	}

#if USE_STL_VECTOR
	utl::vector<T>					_array;
#else
	utl::vector<T, false>			_array;
#endif
	u32								_next_free_index{ u32_invalid_id };
	u32								_size{ 0 };
};

}
