#include "D3D12Core.h"
#include "D3D12Resources.h"
#include "D3D12Surface.h"

using namespace Microsoft::WRL;
namespace XEngine::graphics::d3d12::core {
namespace {
using suface_collection = utl::free_list<d3d12_surface>;

/**
 * @brief D3D12命令管理类设计说明
 * @details 本类采用RAII设计模式封装Direct3D 12的命令队列和命令列表，提供类型安全的GPU命令提交机制
 * 
 * ## 整体设计思路
 * 
 * 1. **类型安全封装**: 通过模板或构造函数参数区分Direct/Compute/Copy命令类型，
 *    避免运行时类型错误，编译期即可确定队列类型
 * 
 * 2. **资源生命周期管理**: 
 *    - 构造函数负责创建命令队列和命令列表
 *    - release()方法统一释放资源，支持异常安全（构造函数失败时调用）
 *    - 析构函数自动调用release()，防止资源泄漏
 * 
 * 3. **帧同步机制**: 内部command_frame结构体管理每帧的命令分配器，
 *    支持CPU-GPU帧同步，避免命令分配器重置冲突
 * 
 * 4. **命名调试支持**: 使用NAME_D3D12_OBJECT宏为D3D对象设置调试名称，
 *    便于GPU调试工具（PIX/RenderDoc）识别
 * 
 * ## 核心优势
 * 
 * - **异常安全**: 构造函数采用goto错误处理模式，任何步骤失败都会自动清理已分配资源
 * - **多队列支持**: 单一类支持D3D12三种命令队列类型（Direct/Compute/Copy），代码复用率高
 * - **现代D3D12 API**: 使用ID3D12GraphicsCommandList6接口，支持最新渲染特性（如Mesh Shader）
 * - **零开销抽象**: 轻量级封装，不引入额外运行时开销，直接操作底层D3D12对象
 * - **可扩展性**: 预留frame管理接口，可轻松扩展为多缓冲帧循环、命令列表录制状态追踪等功能
 */
// 
// ## 多帧渲染架构设计原理
// 
// 现代GPU渲染采用"生产者-消费者"模型：CPU作为命令生产者录制渲染命令，
// GPU作为消费者异步执行。为避免CPU等待GPU完成，需要引入帧缓冲机制。
// 
// ### 为什么需要多帧缓冲？
// 
// 1. **CPU-GPU并行性**: 单缓冲模式下，CPU必须等待GPU完成当前帧才能录制下一帧，
//    导致CPU空闲等待。多帧缓冲允许CPU提前录制N帧，GPU异步执行，最大化硬件利用率
// 
// 2. **命令分配器冲突解决**: D3D12中ID3D12CommandAllocator在GPU执行期间
//    不能被重置。每帧使用独立的分配器，当前帧提交GPU后，CPU可立即重置
//    (frame_buffer_count-1)帧之前的分配器，实现无等待循环
// 
// 3. **帧时序稳定性**: 缓冲N帧可平滑帧率波动，避免单帧卡顿影响整体流畅度
// 
// ### 帧索引轮转机制
// 
// 使用环形缓冲区(ring buffer)管理帧资源：
// - 当前帧索引: current_frame_index % frame_buffer_count
// - 每帧提交后递增索引，到达frame_buffer_count时归零
// - 确保CPU不会超前GPU超过frame_buffer_count帧，防止资源冲突
// 
// ### 命令列表创建策略
// 
// 创建时使用_cmd_frames[0].cmd_allocator作为初始分配器，原因：
// - 命令列表创建时必须绑定一个分配器，即使后续通过Reset()切换
// - 选择索引0确保初始化阶段有确定的资源状态
// - 实际录制前会调用Reset()绑定当前帧对应的分配器
// 
// ### 立即关闭命令列表的设计考量
// 
// 创建后立即调用Close()，因为：
// - 新创建的命令列表处于"录制打开"状态
// - 但实际渲染前需要重新Reset()绑定正确的帧分配器
// - 先Close()使列表进入可提交状态，避免状态不一致
// - 这是一种防御性编程，确保对象始终处于有效状态
class d3d12_command
{
public:
	d3d12_command() = default;
	DISABLE_COPY_AND_MOVE(d3d12_command)
    explicit d3d12_command(ID3D12Device8 *const device, D3D12_COMMAND_LIST_TYPE type)
    {
        HRESULT hr{ S_OK };
        
        // 创建命令队列
        D3D12_COMMAND_QUEUE_DESC desc{};
        desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;                         // 无特殊标志
        desc.NodeMask = 0;                                                  // 单GPU节点
        desc.Type = type;                                                   // 命令队列类型（Direct/Compute/Copy）
        desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;                // 普通优先级
        DXCall(hr = device->CreateCommandQueue(&desc, IID_PPV_ARGS(&_cmd_queue)));
        if(FAILED(hr)) goto _error;
        NAME_D3D12_OBJECT(_cmd_queue, type == D3D12_COMMAND_LIST_TYPE_DIRECT ? 
            L"Direct Command Queue" :
            type == D3D12_COMMAND_LIST_TYPE_COMPUTE ? 
            L"Compute Command Queue" : L" Command Queue");
        
        // 为所有帧创建命令分配器
        for(u32 i{ 0 }; i < frame_buffer_count; ++i)
        {
            command_frame& frame{_cmd_frames[i]};
            DXCall(hr = device->CreateCommandAllocator(type, IID_PPV_ARGS(&frame.cmd_allocator)));
            if(FAILED(hr)) goto _error;
            NAME_D3D12_OBJECT_INDEXED(frame.cmd_allocator, i, type == D3D12_COMMAND_LIST_TYPE_DIRECT ? 
                L"Direct Command Allocator" :
                type == D3D12_COMMAND_LIST_TYPE_COMPUTE ? 
                L"Compute Command Allocator" : L" Command Allocator");
        }

        // 创建命令列表 - 采用多帧缓冲设计实现CPU-GPU并行渲染,传入第一帧的分配器作为初始分配器,并立即关闭命令列表
        DXCall(hr = device->CreateCommandList(0,type,_cmd_frames[0].cmd_allocator,nullptr,IID_PPV_ARGS(&_cmd_list)));
        if(FAILED(hr)) goto _error;
        DXCall(_cmd_list->Close());
        NAME_D3D12_OBJECT(_cmd_list, type == D3D12_COMMAND_LIST_TYPE_DIRECT ? 
            L"Direct Command List" :
            type == D3D12_COMMAND_LIST_TYPE_COMPUTE ? 
            L"Compute Command List" : L" Command List");

		DXCall(hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&_fence)));
        if(FAILED(hr)) goto _error;
        NAME_D3D12_OBJECT(_fence, L"D3D12 Fence");

		_fence_event = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
		assert(_fence_event);
		
		return;

    _error:
        release();
    }

	~d3d12_command()
	{
		assert(!_cmd_queue && !_cmd_list && !_fence);
	}

	// 等待当前帧被标记为完成信号，并重置命令分配器和命令列表
    void begin_frame()
    {
        command_frame& frame{_cmd_frames[_frame_index]};
        frame.wait(_fence_event, _fence);
        // 重置命令分配器将释放之前帧分配的命令内存，使其可重新用于录制新帧的命令
        // 重置命令列表将命令列表重置为可录制状态，准备录制录制命令
        DXCall(frame.cmd_allocator->Reset());
        DXCall(_cmd_list->Reset(frame.cmd_allocator, nullptr));
    }

	// 使用新的围栏值来标记这个围栏
    void end_frame()
    {
        //在提交命令列表前，先关闭命令列表，确保命令列表进入可提交状态
        DXCall(_cmd_list->Close());
        // 将命令列表转为数组形式提交给命令队列执行
        // 虽然目前只有单个命令列表且为单线程工作模式，但仍采用数组方式以保持代码的扩展性
        ID3D12CommandList *const cmd_lists[]{_cmd_list};
        _cmd_queue->ExecuteCommandLists(_countof(cmd_lists), &cmd_lists[0]);

		u64& fence_value{_fence_value};
        ++fence_value;
		command_frame& frame{_cmd_frames[_frame_index]};
		frame.fence_value = fence_value;
		_cmd_queue->Signal(_fence, fence_value);
        _frame_index = (_frame_index + 1) % frame_buffer_count;
    }

	/**
	 * @brief 等待所有帧的命令列表执行完成
	 * @details 确保所有帧的命令列表执行完成，避免资源冲突
	 */
	void flush()
	{
		for(u32 i{ 0 }; i < frame_buffer_count; ++i)
		{
			_cmd_frames[i].wait(_fence_event, _fence);
		}
		_frame_index = 0;
	}
	     
    void release()
    {
		flush();
		core::release(_fence);
		_fence_value = 0;

		CloseHandle(_fence_event);
		_fence_event = nullptr;

		core::release(_cmd_queue);
		core::release(_cmd_list);

		for(u32 i{ 0 }; i < frame_buffer_count; ++i)
		{
			_cmd_frames[i].release();
		}
    }

	constexpr ID3D12CommandQueue *const command_queue() const {return _cmd_queue;}
	constexpr ID3D12GraphicsCommandList6 *const command_list() const {return _cmd_list;}
	constexpr u32 frame_index() const {return _frame_index;}
private:

    struct command_frame
    {
        ID3D12CommandAllocator* 	cmd_allocator{ nullptr };
		u64 						fence_value{ 0 };

        void wait(HANDLE fence_event, ID3D12Fence1* fence)
        {
			assert(fence && fence_event);
			// 如果当前的Fence值小于目标值，说明GPU还没有执行完成当前的命令列表
			if(fence->GetCompletedValue() < fence_value)
			{
				// 我们需要等待GPU执行当前的命令列表，设置事件并等待事件触发
				DXCall(fence->SetEventOnCompletion(fence_value, fence_event));
				WaitForSingleObject(fence_event, INFINITE);
			}
        }

        void release()
        {
            core::release(cmd_allocator);
			fence_value = 0;
        }
    };

    ID3D12CommandQueue* 				_cmd_queue{ nullptr };
    ID3D12GraphicsCommandList6* 		_cmd_list{ nullptr };
	ID3D12Fence1* 						_fence{ nullptr };
	// 对于围栏值来说他是64位无符号整型,有2^64-1个值,即便每秒1000帧,也需要5.8亿年才能回绕,所以不需要担心一直递增导致溢出的问题
	u64  								_fence_value{ 0 };
    command_frame 						_cmd_frames[frame_buffer_count]{};
	HANDLE								_fence_event{ nullptr };
    u32 								_frame_index{ 0 };
};

/**
 * @brief 主 Direct3D 12 设备指针
 * @details 指向 Direct3D 12 设备的智能指针，用于创建渲染管线、管理资源与 GPU 命令
 */
ID3D12Device8* 		main_device{ nullptr };

/**
 * @brief DXGI 工厂指针
 * @details 指向 DXGI 工厂的智能指针，用于创建 Direct3D 12 设备
 */
IDXGIFactory7* 		dxgi_factory{ nullptr };

/**
 * @brief 命令管理类实例
 * @details 用于管理 Direct3D 12 命令队列和命令列表，提供类型安全的 GPU命令提交机制
 */
d3d12_command		gfx_command;

suface_collection   surfaces;




/**
 * @brief RTV 描述符堆
 * @details 用于存储渲染目标视图的描述符，用于渲染管线中的输出
 */
descriptor_heap		rtv_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_RTV};
/**
 * @brief DSV 描述符堆
 * @details 用于存储深度模板视图的描述符，用于渲染管线中的深度模板测试
 */
descriptor_heap		dsv_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_DSV};
/**
 * @brief SRV 描述符堆
 * @details 用于存储着色器资源视图的描述符，用于渲染管线中的着色器资源访问
 */
descriptor_heap		srv_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV};
/**
 * @brief UAV 描述符堆
 * @details 用于存储无序访问视图的描述符，用于渲染管线中的无序访问
 */
descriptor_heap		uav_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV};

/**
 * @brief 延迟释放队列
 * @details 每个帧缓冲区对应一个待释放资源的队列，用于在渲染完成后释放资源
 */
utl::vector<IUnknown*>    deferred_releases[frame_buffer_count]{};

/**
 * @brief 延迟释放标志数组
 * @details 每个帧缓冲区对应一个标志位用于记录是否需要延迟释放资源
 */
u32					deferred_release_flag[frame_buffer_count]{};

/**
 * @brief 延迟释放互斥锁
 * @details 用于保护延迟释放标志数组的并发访问，确保线程安全
 */
std::mutex          deferred_release_mutex{};

// 默认渲染目标格式
constexpr DXGI_FORMAT render_target_format{ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB };

// 最小支持的 Direct3D 特本级别
constexpr D3D_FEATURE_LEVEL minumum_feature_level{ D3D_FEATURE_LEVEL_11_0 };

bool
failed_init()
{
	shutdown();
	return false;
}

/**
 * @brief 确定要使用的 GPU
 * @details 枚举所有可用的 GPU，选择支持最小特征级别的 Direct3D 12 设备
 * 注意:该功能可通过以下方式扩展：例如，检查是否有任何
 * 输出设备（即显示器）已连接，枚举支持的分辨率，提供
 * 一种机制供用户在多适配器环境中选择要使用的适配器等
 * @return IDXGIAdapter4* 指向确定的 GPU 的智能指针
 */
IDXGIAdapter4*
determine_main_adapter()
{
	IDXGIAdapter4* adapter;
	for (u32 i{ 0 };
		dxgi_factory->EnumAdapterByGpuPreference(i, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) != DXGI_ERROR_NOT_FOUND;
		++i)
	{
		//获取支持最小特征级别的 Direct3D 12 设备
		if (SUCCEEDED(D3D12CreateDevice(adapter, minumum_feature_level, __uuidof(ID3D12Device8), nullptr)))
		{
			return adapter;
		}
		release(adapter);
	}

	return nullptr;
}

/**
 * @brief 获取指定适配器支持的最高 Direct3D 特性级别
 * @details 检查指定适配器是否支持 Direct3D 12 特性级别
 * @param adapter 指向要检查的适配器的智能指针
 * @return D3D_FEATURE_LEVEL 支持的最高 Direct3D 特性级别
 */
D3D_FEATURE_LEVEL
get_max_feature_level(IDXGIAdapter4* adapter)
{
	constexpr D3D_FEATURE_LEVEL feature_levels[4]{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_12_0,
		D3D_FEATURE_LEVEL_12_1,
	};

	D3D12_FEATURE_DATA_FEATURE_LEVELS feature_level_info{};
	feature_level_info.NumFeatureLevels = (_countof(feature_levels));
	feature_level_info.pFeatureLevelsRequested = feature_levels;

	ComPtr<ID3D12Device> device;
	DXCall(D3D12CreateDevice(adapter, minumum_feature_level, IID_PPV_ARGS(&device)));
	DXCall(device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &feature_level_info, sizeof(feature_level_info)));

	return feature_level_info.MaxSupportedFeatureLevel;
}


/**
 * @brief 处理延迟释放资源
 * @details 遍历指定帧索引的延迟释放资源索引数组，释放每个资源
 * @param frame_index 要处理的帧索引
 * @note 使用 __declspec(noinline) 防止编译器内联此函数，确保在调试时能够准确断点
 */
void __declspec(noinline)
process_deferred_release(u32 frame_index)
{
	std::lock_guard lock{ deferred_release_mutex };

	// 我们在开始的的时候清楚这个帧的标志位,如果我们在结尾的时候清除,
	// 他可能被其他线程重写.
	deferred_release_flag[frame_index] = 0;

	rtv_descriptor_heap.process_deferred_release(frame_index);
	dsv_descriptor_heap.process_deferred_release(frame_index);
	srv_descriptor_heap.process_deferred_release(frame_index);
	uav_descriptor_heap.process_deferred_release(frame_index);

	utl::vector<IUnknown*>& resources{ deferred_releases[frame_index] };
	if(!resources.empty())
	{
		for(auto& resource : resources) release(resource);
		resources.clear();	
	}
}

}// anonymous namespace

namespace detail{
void 
deferred_release(IUnknown* resource)
{
    const u32 frmae_idx {current_frame_index()};
	std::lock_guard lock{ deferred_release_mutex };
	deferred_releases[frmae_idx].push_back(resource);
	set_deferred_release_flag();
}
} // detail namespace


bool
initialize()
{
	if (main_device) shutdown();


	// 在DEBUG模式下,捕获 DXGI 可能抛出的异常
	u32 dxgi_factory_flag{ 0 };
	// 开启调试层, 需要graphics tools支持
#ifdef _DEBUG
	{
		ComPtr<ID3D12Debug3> debug_interface;
		if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_interface))))
		{
			debug_interface->EnableDebugLayer();
		}
		else
		{
			OutputDebugStringA("warning: d3d12 debug interface is not available\n");
		}

		dxgi_factory_flag |= DXGI_CREATE_FACTORY_DEBUG;
	}
#endif

	// 创建 DXGI 工厂实例，用于枚举显卡适配器和创建交换链等操作
	HRESULT hr{ S_OK };
	DXCall(hr = CreateDXGIFactory2(dxgi_factory_flag, IID_PPV_ARGS(&dxgi_factory)));
	if (FAILED(hr)) return failed_init();

	// 确定要使用的 GPU
	ComPtr<IDXGIAdapter4> main_adapter;
	main_adapter.Attach(determine_main_adapter());
	if (!main_adapter) return failed_init();

	// 获取主适配器支持的最高 Direct3D 特性级别,并且不得低于最小要求
	D3D_FEATURE_LEVEL max_feature_level{ get_max_feature_level(main_adapter.Get()) };
	assert(max_feature_level >= minumum_feature_level);
	if (max_feature_level < minumum_feature_level) return failed_init();

	// 使用最高适配特性级别创建Direct3D 12 设备
	DXCall(hr = D3D12CreateDevice(main_adapter.Get(), max_feature_level, IID_PPV_ARGS(&main_device)));
	if (FAILED(hr)) return failed_init();

#ifdef _DEBUG
	{
		ComPtr<ID3D12InfoQueue> info_queue;
		DXCall(main_device->QueryInterface(IID_PPV_ARGS(&info_queue)));

		info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
		info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
		info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
	}
#endif // _DEBUG


	// 使用 placement new 在已分配的内存上构造对象
	// new (&gfx_command) 表示在 gfx_command 的地址处调用构造函数
	// 这种用法允许我们在不分配新内存的情况下，在指定内存位置构造对象
	// 常用于需要在特定内存地址构造对象，或重新初始化已存在的对象
	// 这里 gfx_command 是一个类成员变量，我们直接在其内存位置上构造 d3d12_command 对象
	// 避免了额外的内存分配，同时可以传递构造参数（main_device 和命令队列类型）
	new (&gfx_command) d3d12_command(main_device, D3D12_COMMAND_LIST_TYPE_DIRECT);
	if(!gfx_command.command_queue()) return failed_init();	

	bool result{true};
	result &= rtv_descriptor_heap.initialize(512, false);
	result &= dsv_descriptor_heap.initialize(512, false);
	result &= srv_descriptor_heap.initialize(4096, false);
	result &= uav_descriptor_heap.initialize(512, false);
	if(!result) return failed_init();

	// 为 Direct3D 12 设备设置名称
	NAME_D3D12_OBJECT(main_device, L"Main Device");
	NAME_D3D12_OBJECT(rtv_descriptor_heap.heap(), L"RTV Descriptor Heap");
	NAME_D3D12_OBJECT(dsv_descriptor_heap.heap(), L"DSV Descriptor Heap");
	NAME_D3D12_OBJECT(srv_descriptor_heap.heap(), L"SRV Descriptor Heap");
	NAME_D3D12_OBJECT(uav_descriptor_heap.heap(), L"UAV Descriptor Heap");


	return true;
}

void
shutdown()
{
	gfx_command.release();

	// 注意,我们需要在所有的依赖资源之前调用延迟释放函数.
	// 否则会导致依赖当基础前资源(贴图等)的资源(着色器等)不能被释放.
	for(u32 i{ 0 }; i < frame_buffer_count; ++i)
	{
		process_deferred_release(i);
	}

	release(dxgi_factory);

	rtv_descriptor_heap.release();
	dsv_descriptor_heap.release();
	srv_descriptor_heap.release();
	uav_descriptor_heap.release();

	// 某些类型仅在 shutdown/reset/clear 时使用延迟释放机制来释放资源，
	// 为了确保这些资源被正确释放，需要在 shutdown 最后额外调用一次延迟释放函数。
	process_deferred_release(0);

    #ifdef _DEBUG
	{
        {
            // 关闭调试层,确保最后只有一个活动的主设备
			ComPtr<ID3D12InfoQueue> info_queue;
			DXCall(main_device->QueryInterface(IID_PPV_ARGS(&info_queue)));

			info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, false);
			info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, false);
			info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, false);
		}

		ComPtr<ID3D12DebugDevice2> debug_device;
		DXCall(main_device->QueryInterface(IID_PPV_ARGS(&debug_device)));
		release(main_device);
		DXCall(debug_device->ReportLiveDeviceObjects(
			D3D12_RLDO_SUMMARY | D3D12_RLDO_DETAIL | D3D12_RLDO_IGNORE_INTERNAL
		));
	}
#endif // _DEBUG


	release(main_device);
}

ID3D12Device *const 
device()
{
	return main_device;
}

descriptor_heap& rtv_heap()	{return rtv_descriptor_heap;}
descriptor_heap& dsv_heap()	{return dsv_descriptor_heap;}
descriptor_heap& uav_heap()	{return uav_descriptor_heap;}
descriptor_heap& srv_heap()	{return srv_descriptor_heap;}


u32
current_frame_index()
{
	return gfx_command.frame_index();
}

DXGI_FORMAT
default_render_target_format()
{
	return render_target_format;
}



// X86结构上的整数访问权架构原子的,所以不需要加锁
void
set_deferred_release_flag()
{
	deferred_release_flag[current_frame_index()] = 1;
}

#pragma region surface

surface
create_surface(platform::window window)
{
	surface_id id{ surfaces.add(window) };
	surfaces[id].create_swap_chain(dxgi_factory,gfx_command.command_queue(),render_target_format);
	return surface{id};
}
void
remove_surface(surface_id id)
{
	gfx_command.flush();
	surfaces.remove(id);
}
void
resize_surface(surface_id id, u32 width, u32 height)
{
	gfx_command.flush();
	surfaces[id].resize();
}
u32
surface_width(surface_id id)
{
	return surfaces[id].width();
}
u32
surface_height(surface_id id)
{
	return surfaces[id].height();
}
void
render_surface(surface_id id)
{
		// 等待GPU完成命令列表,并重置命令分配器和命令列表
	gfx_command.begin_frame();
	ID3D12GraphicsCommandList6* cmd_list{ gfx_command.command_list() };

	const u32 frame_index{ current_frame_index() };
	if(deferred_release_flag[frame_index])
	{
		process_deferred_release(frame_index);
	}

	// 呈现交换链
	surfaces[id].present();

		
	// 记录命令
	//
	// 完成命令记录,立即提交命令列表到命令队列执行
	// 为下一帧标记并增加围栏值
	gfx_command.end_frame();
}



#pragma endregion



}// namespace XEngine::graphics::d3d12::core
